Coverage Report

Created: 2026-07-25 06:50

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</li>
2535
 * <li> geotransform[4] : rotational coefficient, zero for north up images.</li>
2536
 * <li> geotransform[2] : rotational coefficient, zero for north up images.</li>
2537
 * <li> geotransform[5] : height of pixel (but negative)</li>
2538
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2539
 * offset to center of top left pixel.</li>
2540
 * <li> geotransform[3] + 0.5 *
2541
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2542
 * pixel.</li>
2543
 * </ul>
2544
 *
2545
 * @param pszFilename the world file name.
2546
 * @param padfGeoTransform the six double array into which the
2547
 * geotransformation should be placed.
2548
 *
2549
 * @return TRUE on success or FALSE on failure.
2550
 */
2551
2552
int CPL_STDCALL GDALLoadWorldFile(const char *pszFilename,
2553
                                  double *padfGeoTransform)
2554
2555
0
{
2556
0
    VALIDATE_POINTER1(pszFilename, "GDALLoadWorldFile", FALSE);
2557
0
    VALIDATE_POINTER1(padfGeoTransform, "GDALLoadWorldFile", FALSE);
2558
2559
0
    char **papszLines = CSLLoad2(pszFilename, 100, 100, nullptr);
2560
2561
0
    if (!papszLines)
2562
0
        return FALSE;
2563
2564
0
    double world[6] = {0.0};
2565
    // reads the first 6 non-empty lines
2566
0
    int nLines = 0;
2567
0
    const int nLinesCount = CSLCount(papszLines);
2568
0
    for (int i = 0;
2569
0
         i < nLinesCount && nLines < static_cast<int>(CPL_ARRAYSIZE(world));
2570
0
         ++i)
2571
0
    {
2572
0
        CPLString line(papszLines[i]);
2573
0
        if (line.Trim().empty())
2574
0
            continue;
2575
2576
0
        world[nLines] = CPLAtofM(line);
2577
0
        ++nLines;
2578
0
    }
2579
2580
0
    if (nLines == 6 && (world[0] != 0.0 || world[2] != 0.0) &&
2581
0
        (world[3] != 0.0 || world[1] != 0.0))
2582
0
    {
2583
0
        padfGeoTransform[0] = world[4];
2584
0
        padfGeoTransform[1] = world[0];
2585
0
        padfGeoTransform[2] = world[2];
2586
0
        padfGeoTransform[3] = world[5];
2587
0
        padfGeoTransform[4] = world[1];
2588
0
        padfGeoTransform[5] = world[3];
2589
2590
        // correct for center of pixel vs. top left of pixel
2591
0
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[1];
2592
0
        padfGeoTransform[0] -= 0.5 * padfGeoTransform[2];
2593
0
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[4];
2594
0
        padfGeoTransform[3] -= 0.5 * padfGeoTransform[5];
2595
2596
0
        CSLDestroy(papszLines);
2597
2598
0
        return TRUE;
2599
0
    }
2600
0
    else
2601
0
    {
2602
0
        CPLDebug("GDAL",
2603
0
                 "GDALLoadWorldFile(%s) found file, but it was corrupt.",
2604
0
                 pszFilename);
2605
0
        CSLDestroy(papszLines);
2606
0
        return FALSE;
2607
0
    }
2608
0
}
2609
2610
/************************************************************************/
2611
/*                         GDALReadWorldFile()                          */
2612
/************************************************************************/
2613
2614
/**
2615
 * \brief Read ESRI world file.
2616
 *
2617
 * This function reads an ESRI style world file, and formats a geotransform
2618
 * from its contents.  It does the same as GDALLoadWorldFile() function, but
2619
 * it will form the filename for the worldfile from the filename of the raster
2620
 * file referred and the suggested extension.  If no extension is provided,
2621
 * the code will internally try the unix style and windows style world file
2622
 * extensions (eg. for .tif these would be .tfw and .tifw).
2623
 *
2624
 * The world file contains an affine transformation with the parameters
2625
 * in a different order than in a geotransform array.
2626
 *
2627
 * <ul>
2628
 * <li> geotransform[1] : width of pixel</li>
2629
 * <li> geotransform[4] : rotational coefficient, zero for north up images.</li>
2630
 * <li> geotransform[2] : rotational coefficient, zero for north up images.</li>
2631
 * <li> geotransform[5] : height of pixel (but negative)</li>
2632
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2633
 * offset to center of top left pixel.</li>
2634
 * <li> geotransform[3] + 0.5 *
2635
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2636
 * pixel.</li>
2637
 * </ul>
2638
 *
2639
 * @param pszBaseFilename the target raster file.
2640
 * @param pszExtension the extension to use (i.e. "wld") or NULL to derive it
2641
 * from the pszBaseFilename
2642
 * @param padfGeoTransform the six double array into which the
2643
 * geotransformation should be placed.
2644
 *
2645
 * @return TRUE on success or FALSE on failure.
2646
 */
2647
2648
int CPL_STDCALL GDALReadWorldFile(const char *pszBaseFilename,
2649
                                  const char *pszExtension,
2650
                                  double *padfGeoTransform)
2651
2652
0
{
2653
0
    return GDALReadWorldFile2(pszBaseFilename, pszExtension, padfGeoTransform,
2654
0
                              nullptr, nullptr);
2655
0
}
2656
2657
int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
2658
                       GDALGeoTransform &gt, CSLConstList papszSiblingFiles,
2659
                       char **ppszWorldFileNameOut)
2660
0
{
2661
0
    return GDALReadWorldFile2(pszBaseFilename, pszExtension, gt.data(),
2662
0
                              papszSiblingFiles, ppszWorldFileNameOut);
2663
0
}
2664
2665
int GDALReadWorldFile2(const char *pszBaseFilename, const char *pszExtension,
2666
                       double *padfGeoTransform, CSLConstList papszSiblingFiles,
2667
                       char **ppszWorldFileNameOut)
2668
0
{
2669
0
    VALIDATE_POINTER1(pszBaseFilename, "GDALReadWorldFile", FALSE);
2670
0
    VALIDATE_POINTER1(padfGeoTransform, "GDALReadWorldFile", FALSE);
2671
2672
0
    if (ppszWorldFileNameOut)
2673
0
        *ppszWorldFileNameOut = nullptr;
2674
2675
0
    if (!GDALCanFileAcceptSidecarFile(pszBaseFilename))
2676
0
        return FALSE;
2677
2678
    /* -------------------------------------------------------------------- */
2679
    /*      If we aren't given an extension, try both the unix and          */
2680
    /*      windows style extensions.                                       */
2681
    /* -------------------------------------------------------------------- */
2682
0
    if (pszExtension == nullptr)
2683
0
    {
2684
0
        const std::string oBaseExt = CPLGetExtensionSafe(pszBaseFilename);
2685
2686
0
        if (oBaseExt.length() < 2)
2687
0
            return FALSE;
2688
2689
        // windows version - first + last + 'w'
2690
0
        char szDerivedExtension[100] = {'\0'};
2691
0
        szDerivedExtension[0] = oBaseExt[0];
2692
0
        szDerivedExtension[1] = oBaseExt[oBaseExt.length() - 1];
2693
0
        szDerivedExtension[2] = 'w';
2694
0
        szDerivedExtension[3] = '\0';
2695
2696
0
        if (GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
2697
0
                               padfGeoTransform, papszSiblingFiles,
2698
0
                               ppszWorldFileNameOut))
2699
0
            return TRUE;
2700
2701
        // unix version - extension + 'w'
2702
0
        if (oBaseExt.length() > sizeof(szDerivedExtension) - 2)
2703
0
            return FALSE;
2704
2705
0
        snprintf(szDerivedExtension, sizeof(szDerivedExtension), "%sw",
2706
0
                 oBaseExt.c_str());
2707
0
        return GDALReadWorldFile2(pszBaseFilename, szDerivedExtension,
2708
0
                                  padfGeoTransform, papszSiblingFiles,
2709
0
                                  ppszWorldFileNameOut);
2710
0
    }
2711
2712
    /* -------------------------------------------------------------------- */
2713
    /*      Skip the leading period in the extension if there is one.       */
2714
    /* -------------------------------------------------------------------- */
2715
0
    if (*pszExtension == '.')
2716
0
        pszExtension++;
2717
2718
    /* -------------------------------------------------------------------- */
2719
    /*      Generate upper and lower case versions of the extension.        */
2720
    /* -------------------------------------------------------------------- */
2721
0
    char szExtUpper[32] = {'\0'};
2722
0
    char szExtLower[32] = {'\0'};
2723
0
    CPLStrlcpy(szExtUpper, pszExtension, sizeof(szExtUpper));
2724
0
    CPLStrlcpy(szExtLower, pszExtension, sizeof(szExtLower));
2725
2726
0
    for (int i = 0; szExtUpper[i] != '\0'; i++)
2727
0
    {
2728
0
        szExtUpper[i] = static_cast<char>(
2729
0
            CPLToupper(static_cast<unsigned char>(szExtUpper[i])));
2730
0
        szExtLower[i] = static_cast<char>(
2731
0
            CPLTolower(static_cast<unsigned char>(szExtLower[i])));
2732
0
    }
2733
2734
0
    std::string osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtLower);
2735
2736
0
    if (papszSiblingFiles &&
2737
        // cppcheck-suppress knownConditionTrueFalse
2738
0
        GDALCanReliablyUseSiblingFileList(osTFW.c_str()))
2739
0
    {
2740
0
        const int iSibling =
2741
0
            CSLFindString(papszSiblingFiles, CPLGetFilename(osTFW.c_str()));
2742
0
        if (iSibling >= 0)
2743
0
        {
2744
0
            CPLString osTFWFilename = pszBaseFilename;
2745
0
            osTFWFilename.resize(strlen(pszBaseFilename) -
2746
0
                                 strlen(CPLGetFilename(pszBaseFilename)));
2747
0
            osTFWFilename += papszSiblingFiles[iSibling];
2748
0
            if (GDALLoadWorldFile(osTFWFilename, padfGeoTransform))
2749
0
            {
2750
0
                if (ppszWorldFileNameOut)
2751
0
                    *ppszWorldFileNameOut = CPLStrdup(osTFWFilename);
2752
0
                return TRUE;
2753
0
            }
2754
0
        }
2755
0
        return FALSE;
2756
0
    }
2757
2758
    /* -------------------------------------------------------------------- */
2759
    /*      Try lower case, then upper case.                                */
2760
    /* -------------------------------------------------------------------- */
2761
2762
0
    VSIStatBufL sStatBuf;
2763
0
    bool bGotTFW =
2764
0
        VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
2765
2766
0
    if (!bGotTFW && VSIIsCaseSensitiveFS(osTFW.c_str()))
2767
0
    {
2768
0
        osTFW = CPLResetExtensionSafe(pszBaseFilename, szExtUpper);
2769
0
        bGotTFW =
2770
0
            VSIStatExL(osTFW.c_str(), &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0;
2771
0
    }
2772
2773
0
    if (!bGotTFW)
2774
0
        return FALSE;
2775
2776
    /* -------------------------------------------------------------------- */
2777
    /*      We found the file, now load and parse it.                       */
2778
    /* -------------------------------------------------------------------- */
2779
0
    if (GDALLoadWorldFile(osTFW.c_str(), padfGeoTransform))
2780
0
    {
2781
0
        if (ppszWorldFileNameOut)
2782
0
            *ppszWorldFileNameOut = CPLStrdup(osTFW.c_str());
2783
0
        return TRUE;
2784
0
    }
2785
0
    return FALSE;
2786
0
}
2787
2788
/************************************************************************/
2789
/*                         GDALWriteWorldFile()                         */
2790
/*                                                                      */
2791
/*      Helper function for translator implementer wanting              */
2792
/*      support for ESRI world files.                                   */
2793
/************************************************************************/
2794
2795
/**
2796
 * \brief Write ESRI world file.
2797
 *
2798
 * This function writes an ESRI style world file from the passed geotransform.
2799
 *
2800
 * The world file contains an affine transformation with the parameters
2801
 * in a different order than in a geotransform array.
2802
 *
2803
 * <ul>
2804
 * <li> geotransform[1] : width of pixel</li>
2805
 * <li> geotransform[4] : rotational coefficient, zero for north up images.</li>
2806
 * <li> geotransform[2] : rotational coefficient, zero for north up images.</li>
2807
 * <li> geotransform[5] : height of pixel (but negative)</li>
2808
 * <li> geotransform[0] + 0.5 * geotransform[1] + 0.5 * geotransform[2] : x
2809
 * offset to center of top left pixel.</li>
2810
 * <li> geotransform[3] + 0.5 *
2811
 * geotransform[4] + 0.5 * geotransform[5] : y offset to center of top left
2812
 * pixel.</li>
2813
 * </ul>
2814
 *
2815
 * @param pszBaseFilename the target raster file.
2816
 * @param pszExtension the extension to use (i.e. "wld"). Must not be NULL
2817
 * @param padfGeoTransform the six double array from which the
2818
 * geotransformation should be read.
2819
 *
2820
 * @return TRUE on success or FALSE on failure.
2821
 */
2822
2823
int CPL_STDCALL GDALWriteWorldFile(const char *pszBaseFilename,
2824
                                   const char *pszExtension,
2825
                                   double *padfGeoTransform)
2826
2827
0
{
2828
0
    VALIDATE_POINTER1(pszBaseFilename, "GDALWriteWorldFile", FALSE);
2829
0
    VALIDATE_POINTER1(pszExtension, "GDALWriteWorldFile", FALSE);
2830
0
    VALIDATE_POINTER1(padfGeoTransform, "GDALWriteWorldFile", FALSE);
2831
2832
    /* -------------------------------------------------------------------- */
2833
    /*      Prepare the text to write to the file.                          */
2834
    /* -------------------------------------------------------------------- */
2835
0
    CPLString osTFWText;
2836
2837
0
    osTFWText.Printf("%.15f\n%.15f\n%.15f\n%.15f\n%.15f\n%.15f\n",
2838
0
                     padfGeoTransform[1], padfGeoTransform[4],
2839
0
                     padfGeoTransform[2], padfGeoTransform[5],
2840
0
                     padfGeoTransform[0] + 0.5 * padfGeoTransform[1] +
2841
0
                         0.5 * padfGeoTransform[2],
2842
0
                     padfGeoTransform[3] + 0.5 * padfGeoTransform[4] +
2843
0
                         0.5 * padfGeoTransform[5]);
2844
2845
    /* -------------------------------------------------------------------- */
2846
    /*      Update extension, and write to disk.                            */
2847
    /* -------------------------------------------------------------------- */
2848
0
    const std::string osTFW =
2849
0
        CPLResetExtensionSafe(pszBaseFilename, pszExtension);
2850
0
    VSILFILE *const fpTFW = VSIFOpenL(osTFW.c_str(), "wt");
2851
0
    if (fpTFW == nullptr)
2852
0
        return FALSE;
2853
2854
0
    const int bRet =
2855
0
        VSIFWriteL(osTFWText.c_str(), osTFWText.size(), 1, fpTFW) == 1;
2856
0
    if (VSIFCloseL(fpTFW) != 0)
2857
0
        return FALSE;
2858
2859
0
    return bRet;
2860
0
}
2861
2862
/************************************************************************/
2863
/*                          GDALVersionInfo()                           */
2864
/************************************************************************/
2865
2866
/**
2867
 * \brief Get runtime version information.
2868
 *
2869
 * Available pszRequest values:
2870
 * <ul>
2871
 * <li> "VERSION_NUM": Returns GDAL_VERSION_NUM formatted as a string.  i.e.
2872
 * "30603000", e.g for GDAL 3.6.3.0</li>
2873
 * <li> "RELEASE_DATE": Returns GDAL_RELEASE_DATE formatted as a
2874
 * string. i.e. "20230312".</li>
2875
 * <li> "RELEASE_NAME": Returns the GDAL_RELEASE_NAME. ie. "3.6.3"</li>
2876
 * <li> "RELEASE_NICKNAME": (>= 3.11) Returns the GDAL_RELEASE_NICKNAME.
2877
 * (may be empty)</li>
2878
 * <li> "\--version": Returns one line version message suitable for
2879
 * use in response to \--version requests.  i.e. "GDAL 3.6.3, released
2880
 * 2023/03/12"</li>
2881
 * <li> "LICENSE": Returns the content of the LICENSE.TXT file from
2882
 * the GDAL_DATA directory.
2883
 * </li>
2884
 * <li> "BUILD_INFO": List of NAME=VALUE pairs separated by newlines
2885
 * with information on build time options.</li>
2886
 * </ul>
2887
 *
2888
 * @param pszRequest the type of version info desired, as listed above.
2889
 *
2890
 * @return an internal string containing the requested information.
2891
 */
2892
2893
const char *CPL_STDCALL GDALVersionInfo(const char *pszRequest)
2894
2895
0
{
2896
    /* -------------------------------------------------------------------- */
2897
    /*      Try to capture as much build information as practical.          */
2898
    /* -------------------------------------------------------------------- */
2899
0
    if (pszRequest != nullptr && EQUAL(pszRequest, "BUILD_INFO"))
2900
0
    {
2901
0
        CPLString osBuildInfo;
2902
2903
0
#define STRINGIFY_HELPER(x) #x
2904
0
#define STRINGIFY(x) STRINGIFY_HELPER(x)
2905
2906
#ifdef ESRI_BUILD
2907
        osBuildInfo += "ESRI_BUILD=YES\n";
2908
#endif
2909
#ifdef PAM_ENABLED
2910
        osBuildInfo += "PAM_ENABLED=YES\n";
2911
#endif
2912
0
        osBuildInfo += "OGR_ENABLED=YES\n";  // Deprecated.  Always yes.
2913
#ifdef HAVE_CURL
2914
        osBuildInfo += "CURL_ENABLED=YES\n";
2915
        osBuildInfo += "CURL_VERSION=" LIBCURL_VERSION "\n";
2916
#endif
2917
#ifdef HAVE_GEOS
2918
        osBuildInfo += "GEOS_ENABLED=YES\n";
2919
#ifdef GEOS_CAPI_VERSION
2920
        osBuildInfo += "GEOS_VERSION=" GEOS_CAPI_VERSION "\n";
2921
#endif
2922
#endif
2923
0
        osBuildInfo +=
2924
0
            "PROJ_BUILD_VERSION=" STRINGIFY(PROJ_VERSION_MAJOR) "." STRINGIFY(
2925
0
                PROJ_VERSION_MINOR) "." STRINGIFY(PROJ_VERSION_PATCH) "\n";
2926
0
        osBuildInfo += "PROJ_RUNTIME_VERSION=";
2927
0
        osBuildInfo += proj_info().version;
2928
0
        osBuildInfo += '\n';
2929
2930
0
#ifdef __VERSION__
2931
0
#ifdef __clang_version__
2932
0
        osBuildInfo += "COMPILER=clang " __clang_version__ "\n";
2933
#elif defined(__GNUC__)
2934
        osBuildInfo += "COMPILER=GCC " __VERSION__ "\n";
2935
#elif defined(__INTEL_COMPILER)
2936
        osBuildInfo += "COMPILER=" __VERSION__ "\n";
2937
#else
2938
        // STRINGIFY() as we're not sure if its a int or a string
2939
        osBuildInfo += "COMPILER=unknown compiler " STRINGIFY(__VERSION__) "\n";
2940
#endif
2941
#elif defined(_MSC_FULL_VER)
2942
        osBuildInfo += "COMPILER=MSVC " STRINGIFY(_MSC_FULL_VER) "\n";
2943
#elif defined(__INTEL_COMPILER)
2944
        osBuildInfo +=
2945
            "COMPILER=Intel compiler " STRINGIFY(__INTEL_COMPILER) "\n";
2946
#endif
2947
#ifdef CMAKE_UNITY_BUILD
2948
        osBuildInfo += "CMAKE_UNITY_BUILD=YES\n";
2949
#endif
2950
0
#ifdef EMBED_RESOURCE_FILES
2951
0
        osBuildInfo += "EMBED_RESOURCE_FILES=YES\n";
2952
0
#endif
2953
#ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
2954
        osBuildInfo += "USE_ONLY_EMBEDDED_RESOURCE_FILES=YES\n";
2955
#endif
2956
0
#ifdef DEBUG
2957
0
        osBuildInfo += "DEBUG=YES\n";
2958
0
#endif
2959
0
#undef STRINGIFY_HELPER
2960
0
#undef STRINGIFY
2961
2962
0
        CPLFree(CPLGetTLS(CTLS_VERSIONINFO));
2963
0
        CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osBuildInfo), TRUE);
2964
0
        return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
2965
0
    }
2966
2967
    /* -------------------------------------------------------------------- */
2968
    /*      LICENSE is a special case. We try to find and read the          */
2969
    /*      LICENSE.TXT file from the GDAL_DATA directory and return it     */
2970
    /* -------------------------------------------------------------------- */
2971
0
    if (pszRequest != nullptr && EQUAL(pszRequest, "LICENSE"))
2972
0
    {
2973
#if defined(EMBED_RESOURCE_FILES) && defined(USE_ONLY_EMBEDDED_RESOURCE_FILES)
2974
        return GDALGetEmbeddedLicense();
2975
#else
2976
0
        char *pszResultLicence =
2977
0
            reinterpret_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO_LICENCE));
2978
0
        if (pszResultLicence != nullptr)
2979
0
        {
2980
0
            return pszResultLicence;
2981
0
        }
2982
2983
0
        VSILFILE *fp = nullptr;
2984
0
#ifndef USE_ONLY_EMBEDDED_RESOURCE_FILES
2985
0
#ifdef EMBED_RESOURCE_FILES
2986
0
        CPLErrorStateBackuper oErrorStateBackuper(CPLQuietErrorHandler);
2987
0
#endif
2988
0
        const char *pszFilename = CPLFindFile("etc", "LICENSE.TXT");
2989
0
        if (pszFilename != nullptr)
2990
0
            fp = VSIFOpenL(pszFilename, "r");
2991
0
        if (fp != nullptr)
2992
0
        {
2993
0
            if (VSIFSeekL(fp, 0, SEEK_END) == 0)
2994
0
            {
2995
                // TODO(schwehr): Handle if VSITellL returns a value too large
2996
                // for size_t.
2997
0
                const size_t nLength = static_cast<size_t>(VSIFTellL(fp) + 1);
2998
0
                if (VSIFSeekL(fp, SEEK_SET, 0) == 0)
2999
0
                {
3000
0
                    pszResultLicence =
3001
0
                        static_cast<char *>(VSICalloc(1, nLength));
3002
0
                    if (pszResultLicence)
3003
0
                        CPL_IGNORE_RET_VAL(
3004
0
                            VSIFReadL(pszResultLicence, 1, nLength - 1, fp));
3005
0
                }
3006
0
            }
3007
3008
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
3009
0
        }
3010
0
#endif
3011
3012
0
#ifdef EMBED_RESOURCE_FILES
3013
0
        if (!fp)
3014
0
        {
3015
0
            return GDALGetEmbeddedLicense();
3016
0
        }
3017
0
#endif
3018
3019
0
        if (!pszResultLicence)
3020
0
        {
3021
0
            pszResultLicence =
3022
0
                CPLStrdup("GDAL/OGR is released under the MIT license.\n"
3023
0
                          "The LICENSE.TXT distributed with GDAL/OGR should\n"
3024
0
                          "contain additional details.\n");
3025
0
        }
3026
3027
0
        CPLSetTLS(CTLS_VERSIONINFO_LICENCE, pszResultLicence, TRUE);
3028
0
        return pszResultLicence;
3029
0
#endif
3030
0
    }
3031
3032
    /* -------------------------------------------------------------------- */
3033
    /*      All other strings are fairly small.                             */
3034
    /* -------------------------------------------------------------------- */
3035
0
    CPLString osVersionInfo;
3036
3037
0
    if (pszRequest == nullptr || EQUAL(pszRequest, "VERSION_NUM"))
3038
0
        osVersionInfo.Printf("%d", GDAL_VERSION_NUM);
3039
0
    else if (EQUAL(pszRequest, "RELEASE_DATE"))
3040
0
        osVersionInfo.Printf("%d", GDAL_RELEASE_DATE);
3041
0
    else if (EQUAL(pszRequest, "RELEASE_NAME"))
3042
0
        osVersionInfo.Printf(GDAL_RELEASE_NAME);
3043
0
    else if (EQUAL(pszRequest, "RELEASE_NICKNAME"))
3044
0
        osVersionInfo.Printf("%s", GDAL_RELEASE_NICKNAME);
3045
0
    else  // --version
3046
0
    {
3047
0
        osVersionInfo = "GDAL " GDAL_RELEASE_NAME;
3048
        if constexpr (GDAL_RELEASE_NICKNAME[0] != '\0')
3049
        {
3050
            osVersionInfo += " \"" GDAL_RELEASE_NICKNAME "\"";
3051
        }
3052
0
        osVersionInfo += CPLString().Printf(
3053
0
            ", released %d/%02d/%02d", GDAL_RELEASE_DATE / 10000,
3054
0
            (GDAL_RELEASE_DATE % 10000) / 100, GDAL_RELEASE_DATE % 100);
3055
#if defined(__GNUC__) && !defined(__OPTIMIZE__)
3056
        // Cf https://gcc.gnu.org/onlinedocs/cpp/Common-Predefined-Macros.html
3057
        // also true for CLang
3058
        osVersionInfo += " (debug build)";
3059
#elif defined(_ITERATOR_DEBUG_LEVEL) && _ITERATOR_DEBUG_LEVEL == 2
3060
        // https://docs.microsoft.com/en-us/cpp/standard-library/iterator-debug-level?view=msvc-170
3061
        // In release mode, the compiler generates an error if you specify
3062
        // _ITERATOR_DEBUG_LEVEL as 2.
3063
        osVersionInfo += " (debug build)";
3064
#endif
3065
0
    }
3066
3067
0
    CPLFree(CPLGetTLS(CTLS_VERSIONINFO));  // clear old value.
3068
0
    CPLSetTLS(CTLS_VERSIONINFO, CPLStrdup(osVersionInfo), TRUE);
3069
0
    return static_cast<char *>(CPLGetTLS(CTLS_VERSIONINFO));
3070
0
}
3071
3072
/************************************************************************/
3073
/*                          GDALCheckVersion()                          */
3074
/************************************************************************/
3075
3076
/** Return TRUE if GDAL library version at runtime matches
3077
   nVersionMajor.nVersionMinor.
3078
3079
    The purpose of this method is to ensure that calling code will run
3080
    with the GDAL version it is compiled for. It is primarily intended
3081
    for external plugins.
3082
3083
    @param nVersionMajor Major version to be tested against
3084
    @param nVersionMinor Minor version to be tested against
3085
    @param pszCallingComponentName If not NULL, in case of version mismatch, the
3086
   method will issue a failure mentioning the name of the calling component.
3087
3088
    @return TRUE if GDAL library version at runtime matches
3089
    nVersionMajor.nVersionMinor, FALSE otherwise.
3090
  */
3091
int CPL_STDCALL GDALCheckVersion(int nVersionMajor, int nVersionMinor,
3092
                                 const char *pszCallingComponentName)
3093
0
{
3094
0
    if (nVersionMajor == GDAL_VERSION_MAJOR &&
3095
0
        nVersionMinor == GDAL_VERSION_MINOR)
3096
0
        return TRUE;
3097
3098
0
    if (pszCallingComponentName)
3099
0
    {
3100
0
        CPLError(CE_Failure, CPLE_AppDefined,
3101
0
                 "%s was compiled against GDAL %d.%d, but "
3102
0
                 "the current library version is %d.%d",
3103
0
                 pszCallingComponentName, nVersionMajor, nVersionMinor,
3104
0
                 GDAL_VERSION_MAJOR, GDAL_VERSION_MINOR);
3105
0
    }
3106
0
    return FALSE;
3107
0
}
3108
3109
/************************************************************************/
3110
/*                            GDALDecToDMS()                            */
3111
/************************************************************************/
3112
3113
/** Translate a decimal degrees value to a DMS string with hemisphere.
3114
 */
3115
const char *CPL_STDCALL GDALDecToDMS(double dfAngle, const char *pszAxis,
3116
                                     int nPrecision)
3117
3118
0
{
3119
0
    return CPLDecToDMS(dfAngle, pszAxis, nPrecision);
3120
0
}
3121
3122
/************************************************************************/
3123
/*                         GDALPackedDMSToDec()                         */
3124
/************************************************************************/
3125
3126
/**
3127
 * \brief Convert a packed DMS value (DDDMMMSSS.SS) into decimal degrees.
3128
 *
3129
 * See CPLPackedDMSToDec().
3130
 */
3131
3132
double CPL_STDCALL GDALPackedDMSToDec(double dfPacked)
3133
3134
0
{
3135
0
    return CPLPackedDMSToDec(dfPacked);
3136
0
}
3137
3138
/************************************************************************/
3139
/*                         GDALDecToPackedDMS()                         */
3140
/************************************************************************/
3141
3142
/**
3143
 * \brief Convert decimal degrees into packed DMS value (DDDMMMSSS.SS).
3144
 *
3145
 * See CPLDecToPackedDMS().
3146
 */
3147
3148
double CPL_STDCALL GDALDecToPackedDMS(double dfDec)
3149
3150
0
{
3151
0
    return CPLDecToPackedDMS(dfDec);
3152
0
}
3153
3154
/************************************************************************/
3155
/*                       GDALGCPsToGeoTransform()                       */
3156
/************************************************************************/
3157
3158
/**
3159
 * \brief Generate Geotransform from GCPs.
3160
 *
3161
 * Given a set of GCPs perform first order fit as a geotransform.
3162
 *
3163
 * Due to imprecision in the calculations the fit algorithm will often
3164
 * return non-zero rotational coefficients even if given perfectly non-rotated
3165
 * inputs.  A special case has been implemented for corner corner coordinates
3166
 * given in TL, TR, BR, BL order.  So when using this to get a geotransform
3167
 * from 4 corner coordinates, pass them in this order.
3168
 *
3169
 * If bApproxOK = FALSE, the
3170
 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK configuration option will be read. If
3171
 * set to YES, then bApproxOK will be overridden with TRUE.
3172
 * When exact fit is asked, the
3173
 * GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD configuration option can be set to
3174
 * give the maximum error threshold in pixel. The default is 0.25.
3175
 *
3176
 * @param nGCPCount the number of GCPs being passed in.
3177
 * @param pasGCPs the list of GCP structures.
3178
 * @param padfGeoTransform the six double array in which the affine
3179
 * geotransformation will be returned.
3180
 * @param bApproxOK If FALSE the function will fail if the geotransform is not
3181
 * essentially an exact fit (within 0.25 pixel) for all GCPs.
3182
 *
3183
 * @return TRUE on success or FALSE if there aren't enough points to prepare a
3184
 * geotransform, the pointers are ill-determined or if bApproxOK is FALSE
3185
 * and the fit is poor.
3186
 */
3187
3188
// TODO(schwehr): Add consts to args.
3189
int CPL_STDCALL GDALGCPsToGeoTransform(int nGCPCount, const GDAL_GCP *pasGCPs,
3190
                                       double *padfGeoTransform, int bApproxOK)
3191
3192
0
{
3193
0
    double dfPixelThreshold = 0.25;
3194
0
    if (!bApproxOK)
3195
0
    {
3196
0
        bApproxOK = CPLTestBool(
3197
0
            CPLGetConfigOption("GDAL_GCPS_TO_GEOTRANSFORM_APPROX_OK", "NO"));
3198
0
        if (!bApproxOK)
3199
0
        {
3200
0
            dfPixelThreshold = std::clamp(
3201
0
                CPLAtof(CPLGetConfigOption(
3202
0
                    "GDAL_GCPS_TO_GEOTRANSFORM_APPROX_THRESHOLD", "0.25")),
3203
0
                0.0, std::numeric_limits<double>::max());
3204
0
        }
3205
0
    }
3206
3207
    /* -------------------------------------------------------------------- */
3208
    /*      Recognise a few special cases.                                  */
3209
    /* -------------------------------------------------------------------- */
3210
0
    if (nGCPCount < 2)
3211
0
        return FALSE;
3212
3213
0
    if (nGCPCount == 2)
3214
0
    {
3215
0
        if (pasGCPs[1].dfGCPPixel == pasGCPs[0].dfGCPPixel ||
3216
0
            pasGCPs[1].dfGCPLine == pasGCPs[0].dfGCPLine)
3217
0
            return FALSE;
3218
3219
0
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
3220
0
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
3221
0
        padfGeoTransform[2] = 0.0;
3222
3223
0
        padfGeoTransform[4] = 0.0;
3224
0
        padfGeoTransform[5] = (pasGCPs[1].dfGCPY - pasGCPs[0].dfGCPY) /
3225
0
                              (pasGCPs[1].dfGCPLine - pasGCPs[0].dfGCPLine);
3226
3227
0
        padfGeoTransform[0] = pasGCPs[0].dfGCPX -
3228
0
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[1] -
3229
0
                              pasGCPs[0].dfGCPLine * padfGeoTransform[2];
3230
3231
0
        padfGeoTransform[3] = pasGCPs[0].dfGCPY -
3232
0
                              pasGCPs[0].dfGCPPixel * padfGeoTransform[4] -
3233
0
                              pasGCPs[0].dfGCPLine * padfGeoTransform[5];
3234
3235
0
        return TRUE;
3236
0
    }
3237
3238
    /* -------------------------------------------------------------------- */
3239
    /*      Special case of 4 corner coordinates of a non-rotated           */
3240
    /*      image.  The points must be in TL-TR-BR-BL order for now.        */
3241
    /*      This case helps avoid some imprecision in the general           */
3242
    /*      calculations.                                                   */
3243
    /* -------------------------------------------------------------------- */
3244
0
    if (nGCPCount == 4 && pasGCPs[0].dfGCPLine == pasGCPs[1].dfGCPLine &&
3245
0
        pasGCPs[2].dfGCPLine == pasGCPs[3].dfGCPLine &&
3246
0
        pasGCPs[0].dfGCPPixel == pasGCPs[3].dfGCPPixel &&
3247
0
        pasGCPs[1].dfGCPPixel == pasGCPs[2].dfGCPPixel &&
3248
0
        pasGCPs[0].dfGCPLine != pasGCPs[2].dfGCPLine &&
3249
0
        pasGCPs[0].dfGCPPixel != pasGCPs[1].dfGCPPixel &&
3250
0
        pasGCPs[0].dfGCPY == pasGCPs[1].dfGCPY &&
3251
0
        pasGCPs[2].dfGCPY == pasGCPs[3].dfGCPY &&
3252
0
        pasGCPs[0].dfGCPX == pasGCPs[3].dfGCPX &&
3253
0
        pasGCPs[1].dfGCPX == pasGCPs[2].dfGCPX &&
3254
0
        pasGCPs[0].dfGCPY != pasGCPs[2].dfGCPY &&
3255
0
        pasGCPs[0].dfGCPX != pasGCPs[1].dfGCPX)
3256
0
    {
3257
0
        padfGeoTransform[1] = (pasGCPs[1].dfGCPX - pasGCPs[0].dfGCPX) /
3258
0
                              (pasGCPs[1].dfGCPPixel - pasGCPs[0].dfGCPPixel);
3259
0
        padfGeoTransform[2] = 0.0;
3260
0
        padfGeoTransform[4] = 0.0;
3261
0
        padfGeoTransform[5] = (pasGCPs[2].dfGCPY - pasGCPs[1].dfGCPY) /
3262
0
                              (pasGCPs[2].dfGCPLine - pasGCPs[1].dfGCPLine);
3263
3264
0
        padfGeoTransform[0] =
3265
0
            pasGCPs[0].dfGCPX - pasGCPs[0].dfGCPPixel * padfGeoTransform[1];
3266
0
        padfGeoTransform[3] =
3267
0
            pasGCPs[0].dfGCPY - pasGCPs[0].dfGCPLine * padfGeoTransform[5];
3268
0
        return TRUE;
3269
0
    }
3270
3271
    /* -------------------------------------------------------------------- */
3272
    /*      Compute source and destination ranges so we can normalize       */
3273
    /*      the values to make the least squares computation more stable.   */
3274
    /* -------------------------------------------------------------------- */
3275
0
    double min_pixel = pasGCPs[0].dfGCPPixel;
3276
0
    double max_pixel = pasGCPs[0].dfGCPPixel;
3277
0
    double min_line = pasGCPs[0].dfGCPLine;
3278
0
    double max_line = pasGCPs[0].dfGCPLine;
3279
0
    double min_geox = pasGCPs[0].dfGCPX;
3280
0
    double max_geox = pasGCPs[0].dfGCPX;
3281
0
    double min_geoy = pasGCPs[0].dfGCPY;
3282
0
    double max_geoy = pasGCPs[0].dfGCPY;
3283
3284
0
    for (int i = 1; i < nGCPCount; ++i)
3285
0
    {
3286
0
        min_pixel = std::min(min_pixel, pasGCPs[i].dfGCPPixel);
3287
0
        max_pixel = std::max(max_pixel, pasGCPs[i].dfGCPPixel);
3288
0
        min_line = std::min(min_line, pasGCPs[i].dfGCPLine);
3289
0
        max_line = std::max(max_line, pasGCPs[i].dfGCPLine);
3290
0
        min_geox = std::min(min_geox, pasGCPs[i].dfGCPX);
3291
0
        max_geox = std::max(max_geox, pasGCPs[i].dfGCPX);
3292
0
        min_geoy = std::min(min_geoy, pasGCPs[i].dfGCPY);
3293
0
        max_geoy = std::max(max_geoy, pasGCPs[i].dfGCPY);
3294
0
    }
3295
3296
0
    double EPS = 1.0e-12;
3297
3298
0
    if (std::abs(max_pixel - min_pixel) < EPS ||
3299
0
        std::abs(max_line - min_line) < EPS ||
3300
0
        std::abs(max_geox - min_geox) < EPS ||
3301
0
        std::abs(max_geoy - min_geoy) < EPS)
3302
0
    {
3303
0
        return FALSE;  // degenerate in at least one dimension.
3304
0
    }
3305
3306
0
    double pl_normalize[6], geo_normalize[6];
3307
3308
0
    pl_normalize[0] = -min_pixel / (max_pixel - min_pixel);
3309
0
    pl_normalize[1] = 1.0 / (max_pixel - min_pixel);
3310
0
    pl_normalize[2] = 0.0;
3311
0
    pl_normalize[3] = -min_line / (max_line - min_line);
3312
0
    pl_normalize[4] = 0.0;
3313
0
    pl_normalize[5] = 1.0 / (max_line - min_line);
3314
3315
0
    geo_normalize[0] = -min_geox / (max_geox - min_geox);
3316
0
    geo_normalize[1] = 1.0 / (max_geox - min_geox);
3317
0
    geo_normalize[2] = 0.0;
3318
0
    geo_normalize[3] = -min_geoy / (max_geoy - min_geoy);
3319
0
    geo_normalize[4] = 0.0;
3320
0
    geo_normalize[5] = 1.0 / (max_geoy - min_geoy);
3321
3322
    /* -------------------------------------------------------------------- */
3323
    /* In the general case, do a least squares error approximation by       */
3324
    /* solving the equation Sum[(A - B*x + C*y - Lon)^2] = minimum          */
3325
    /* -------------------------------------------------------------------- */
3326
3327
0
    double sum_x = 0.0;
3328
0
    double sum_y = 0.0;
3329
0
    double sum_xy = 0.0;
3330
0
    double sum_xx = 0.0;
3331
0
    double sum_yy = 0.0;
3332
0
    double sum_Lon = 0.0;
3333
0
    double sum_Lonx = 0.0;
3334
0
    double sum_Lony = 0.0;
3335
0
    double sum_Lat = 0.0;
3336
0
    double sum_Latx = 0.0;
3337
0
    double sum_Laty = 0.0;
3338
3339
0
    for (int i = 0; i < nGCPCount; ++i)
3340
0
    {
3341
0
        double pixel, line, geox, geoy;
3342
3343
0
        GDALApplyGeoTransform(pl_normalize, pasGCPs[i].dfGCPPixel,
3344
0
                              pasGCPs[i].dfGCPLine, &pixel, &line);
3345
0
        GDALApplyGeoTransform(geo_normalize, pasGCPs[i].dfGCPX,
3346
0
                              pasGCPs[i].dfGCPY, &geox, &geoy);
3347
3348
0
        sum_x += pixel;
3349
0
        sum_y += line;
3350
0
        sum_xy += pixel * line;
3351
0
        sum_xx += pixel * pixel;
3352
0
        sum_yy += line * line;
3353
0
        sum_Lon += geox;
3354
0
        sum_Lonx += geox * pixel;
3355
0
        sum_Lony += geox * line;
3356
0
        sum_Lat += geoy;
3357
0
        sum_Latx += geoy * pixel;
3358
0
        sum_Laty += geoy * line;
3359
0
    }
3360
3361
0
    const double divisor = nGCPCount * (sum_xx * sum_yy - sum_xy * sum_xy) +
3362
0
                           2 * sum_x * sum_y * sum_xy - sum_y * sum_y * sum_xx -
3363
0
                           sum_x * sum_x * sum_yy;
3364
3365
    /* -------------------------------------------------------------------- */
3366
    /*      If the divisor is zero, there is no valid solution.             */
3367
    /* -------------------------------------------------------------------- */
3368
0
    if (divisor == 0.0)
3369
0
        return FALSE;
3370
3371
    /* -------------------------------------------------------------------- */
3372
    /*      Compute top/left origin.                                        */
3373
    /* -------------------------------------------------------------------- */
3374
0
    double gt_normalized[6] = {0.0};
3375
0
    gt_normalized[0] = (sum_Lon * (sum_xx * sum_yy - sum_xy * sum_xy) +
3376
0
                        sum_Lonx * (sum_y * sum_xy - sum_x * sum_yy) +
3377
0
                        sum_Lony * (sum_x * sum_xy - sum_y * sum_xx)) /
3378
0
                       divisor;
3379
3380
0
    gt_normalized[3] = (sum_Lat * (sum_xx * sum_yy - sum_xy * sum_xy) +
3381
0
                        sum_Latx * (sum_y * sum_xy - sum_x * sum_yy) +
3382
0
                        sum_Laty * (sum_x * sum_xy - sum_y * sum_xx)) /
3383
0
                       divisor;
3384
3385
    /* -------------------------------------------------------------------- */
3386
    /*      Compute X related coefficients.                                 */
3387
    /* -------------------------------------------------------------------- */
3388
0
    gt_normalized[1] = (sum_Lon * (sum_y * sum_xy - sum_x * sum_yy) +
3389
0
                        sum_Lonx * (nGCPCount * sum_yy - sum_y * sum_y) +
3390
0
                        sum_Lony * (sum_x * sum_y - sum_xy * nGCPCount)) /
3391
0
                       divisor;
3392
3393
0
    gt_normalized[2] = (sum_Lon * (sum_x * sum_xy - sum_y * sum_xx) +
3394
0
                        sum_Lonx * (sum_x * sum_y - nGCPCount * sum_xy) +
3395
0
                        sum_Lony * (nGCPCount * sum_xx - sum_x * sum_x)) /
3396
0
                       divisor;
3397
3398
    /* -------------------------------------------------------------------- */
3399
    /*      Compute Y related coefficients.                                 */
3400
    /* -------------------------------------------------------------------- */
3401
0
    gt_normalized[4] = (sum_Lat * (sum_y * sum_xy - sum_x * sum_yy) +
3402
0
                        sum_Latx * (nGCPCount * sum_yy - sum_y * sum_y) +
3403
0
                        sum_Laty * (sum_x * sum_y - sum_xy * nGCPCount)) /
3404
0
                       divisor;
3405
3406
0
    gt_normalized[5] = (sum_Lat * (sum_x * sum_xy - sum_y * sum_xx) +
3407
0
                        sum_Latx * (sum_x * sum_y - nGCPCount * sum_xy) +
3408
0
                        sum_Laty * (nGCPCount * sum_xx - sum_x * sum_x)) /
3409
0
                       divisor;
3410
3411
    /* -------------------------------------------------------------------- */
3412
    /*      Compose the resulting transformation with the normalization     */
3413
    /*      geotransformations.                                             */
3414
    /* -------------------------------------------------------------------- */
3415
0
    double gt1p2[6] = {0.0};
3416
0
    double inv_geo_normalize[6] = {0.0};
3417
0
    if (!GDALInvGeoTransform(geo_normalize, inv_geo_normalize))
3418
0
        return FALSE;
3419
3420
0
    GDALComposeGeoTransforms(pl_normalize, gt_normalized, gt1p2);
3421
0
    GDALComposeGeoTransforms(gt1p2, inv_geo_normalize, padfGeoTransform);
3422
3423
    // "Hour-glass" like shape of GCPs. Cf https://github.com/OSGeo/gdal/issues/11618
3424
0
    if (std::abs(padfGeoTransform[1]) <= 1e-15 ||
3425
0
        std::abs(padfGeoTransform[5]) <= 1e-15)
3426
0
    {
3427
0
        return FALSE;
3428
0
    }
3429
3430
    /* -------------------------------------------------------------------- */
3431
    /*      Now check if any of the input points fit this poorly.           */
3432
    /* -------------------------------------------------------------------- */
3433
0
    if (!bApproxOK)
3434
0
    {
3435
        // FIXME? Not sure if it is the more accurate way of computing
3436
        // pixel size
3437
0
        double dfPixelSize =
3438
0
            0.5 *
3439
0
            (std::abs(padfGeoTransform[1]) + std::abs(padfGeoTransform[2]) +
3440
0
             std::abs(padfGeoTransform[4]) + std::abs(padfGeoTransform[5]));
3441
0
        if (dfPixelSize == 0.0)
3442
0
        {
3443
0
            CPLDebug("GDAL", "dfPixelSize = 0");
3444
0
            return FALSE;
3445
0
        }
3446
3447
0
        for (int i = 0; i < nGCPCount; i++)
3448
0
        {
3449
0
            const double dfErrorX =
3450
0
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[1] +
3451
0
                 pasGCPs[i].dfGCPLine * padfGeoTransform[2] +
3452
0
                 padfGeoTransform[0]) -
3453
0
                pasGCPs[i].dfGCPX;
3454
0
            const double dfErrorY =
3455
0
                (pasGCPs[i].dfGCPPixel * padfGeoTransform[4] +
3456
0
                 pasGCPs[i].dfGCPLine * padfGeoTransform[5] +
3457
0
                 padfGeoTransform[3]) -
3458
0
                pasGCPs[i].dfGCPY;
3459
3460
0
            if (std::abs(dfErrorX) > dfPixelThreshold * dfPixelSize ||
3461
0
                std::abs(dfErrorY) > dfPixelThreshold * dfPixelSize)
3462
0
            {
3463
0
                CPLDebug("GDAL",
3464
0
                         "dfErrorX/dfPixelSize = %.2f, "
3465
0
                         "dfErrorY/dfPixelSize = %.2f",
3466
0
                         std::abs(dfErrorX) / dfPixelSize,
3467
0
                         std::abs(dfErrorY) / dfPixelSize);
3468
0
                return FALSE;
3469
0
            }
3470
0
        }
3471
0
    }
3472
3473
0
    return TRUE;
3474
0
}
3475
3476
/************************************************************************/
3477
/*                      GDALComposeGeoTransforms()                      */
3478
/************************************************************************/
3479
3480
/**
3481
 * \brief Compose two geotransforms.
3482
 *
3483
 * The resulting geotransform is the equivalent to padfGT1 and then padfGT2
3484
 * being applied to a point.
3485
 *
3486
 * @param padfGT1 the first geotransform, six values.
3487
 * @param padfGT2 the second geotransform, six values.
3488
 * @param padfGTOut the output geotransform, six values, may safely be the same
3489
 * array as padfGT1 or padfGT2.
3490
 */
3491
3492
void GDALComposeGeoTransforms(const double *padfGT1, const double *padfGT2,
3493
                              double *padfGTOut)
3494
3495
0
{
3496
0
    double gtwrk[6] = {0.0};
3497
    // We need to think of the geotransform in a more normal form to do
3498
    // the matrix multiple:
3499
    //
3500
    //  __                                __
3501
    //  | gt.xscale   gt.xrot     gt.xorig |
3502
    //  | gt.yrot     gt.yscale   gt.yorig |
3503
    //  |  0.0        0.0         1.0      |
3504
    //  --                                --
3505
    //
3506
    // Then we can use normal matrix multiplication to produce the
3507
    // composed transformation.  I don't actually reform the matrix
3508
    // explicitly which is why the following may seem kind of spagettish.
3509
3510
0
    gtwrk[1] = padfGT2[1] * padfGT1[1] + padfGT2[2] * padfGT1[4];
3511
0
    gtwrk[2] = padfGT2[1] * padfGT1[2] + padfGT2[2] * padfGT1[5];
3512
0
    gtwrk[0] =
3513
0
        padfGT2[1] * padfGT1[0] + padfGT2[2] * padfGT1[3] + padfGT2[0] * 1.0;
3514
3515
0
    gtwrk[4] = padfGT2[4] * padfGT1[1] + padfGT2[5] * padfGT1[4];
3516
0
    gtwrk[5] = padfGT2[4] * padfGT1[2] + padfGT2[5] * padfGT1[5];
3517
0
    gtwrk[3] =
3518
0
        padfGT2[4] * padfGT1[0] + padfGT2[5] * padfGT1[3] + padfGT2[3] * 1.0;
3519
0
    memcpy(padfGTOut, gtwrk, sizeof(gtwrk));
3520
0
}
3521
3522
/************************************************************************/
3523
/*                       StripIrrelevantOptions()                       */
3524
/************************************************************************/
3525
3526
static void StripIrrelevantOptions(CPLXMLNode *psCOL, int nOptions)
3527
0
{
3528
0
    if (psCOL == nullptr)
3529
0
        return;
3530
0
    if (nOptions == 0)
3531
0
        nOptions = GDAL_OF_RASTER;
3532
0
    if ((nOptions & GDAL_OF_RASTER) != 0 && (nOptions & GDAL_OF_VECTOR) != 0)
3533
0
        return;
3534
3535
0
    CPLXMLNode *psPrev = nullptr;
3536
0
    for (CPLXMLNode *psIter = psCOL->psChild; psIter;)
3537
0
    {
3538
0
        if (psIter->eType == CXT_Element)
3539
0
        {
3540
0
            CPLXMLNode *psScope = CPLGetXMLNode(psIter, "scope");
3541
0
            bool bStrip = false;
3542
0
            if (nOptions == GDAL_OF_RASTER && psScope && psScope->psChild &&
3543
0
                psScope->psChild->pszValue &&
3544
0
                EQUAL(psScope->psChild->pszValue, "vector"))
3545
0
            {
3546
0
                bStrip = true;
3547
0
            }
3548
0
            else if (nOptions == GDAL_OF_VECTOR && psScope &&
3549
0
                     psScope->psChild && psScope->psChild->pszValue &&
3550
0
                     EQUAL(psScope->psChild->pszValue, "raster"))
3551
0
            {
3552
0
                bStrip = true;
3553
0
            }
3554
0
            if (psScope)
3555
0
            {
3556
0
                CPLRemoveXMLChild(psIter, psScope);
3557
0
                CPLDestroyXMLNode(psScope);
3558
0
            }
3559
3560
0
            CPLXMLNode *psNext = psIter->psNext;
3561
0
            if (bStrip)
3562
0
            {
3563
0
                if (psPrev)
3564
0
                    psPrev->psNext = psNext;
3565
0
                else if (psCOL->psChild == psIter)
3566
0
                    psCOL->psChild = psNext;
3567
0
                psIter->psNext = nullptr;
3568
0
                CPLDestroyXMLNode(psIter);
3569
0
                psIter = psNext;
3570
0
            }
3571
0
            else
3572
0
            {
3573
0
                psPrev = psIter;
3574
0
                psIter = psNext;
3575
0
            }
3576
0
        }
3577
0
        else
3578
0
        {
3579
0
            psIter = psIter->psNext;
3580
0
        }
3581
0
    }
3582
0
}
3583
3584
/************************************************************************/
3585
/*                        GDALPrintDriverList()                         */
3586
/************************************************************************/
3587
3588
/** Print on stdout the driver list */
3589
std::string GDALPrintDriverList(int nOptions, bool bJSON)
3590
0
{
3591
0
    if (nOptions == 0)
3592
0
        nOptions = GDAL_OF_RASTER;
3593
3594
0
    if (bJSON)
3595
0
    {
3596
0
        auto poDM = GetGDALDriverManager();
3597
0
        CPLJSONArray oArray;
3598
0
        const int nDriverCount = poDM->GetDriverCount();
3599
0
        for (int iDr = 0; iDr < nDriverCount; ++iDr)
3600
0
        {
3601
0
            auto poDriver = poDM->GetDriver(iDr);
3602
0
            CSLConstList papszMD = poDriver->GetMetadata();
3603
3604
0
            if (nOptions == GDAL_OF_RASTER &&
3605
0
                !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3606
0
                continue;
3607
0
            if (nOptions == GDAL_OF_VECTOR &&
3608
0
                !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3609
0
                continue;
3610
0
            if (nOptions == GDAL_OF_GNM &&
3611
0
                !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3612
0
                continue;
3613
0
            if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
3614
0
                !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3615
0
                continue;
3616
3617
0
            CPLJSONObject oJDriver;
3618
0
            oJDriver.Set("short_name", poDriver->GetDescription());
3619
0
            if (const char *pszLongName =
3620
0
                    CSLFetchNameValue(papszMD, GDAL_DMD_LONGNAME))
3621
0
                oJDriver.Set("long_name", pszLongName);
3622
0
            CPLJSONArray oJScopes;
3623
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3624
0
                oJScopes.Add("raster");
3625
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3626
0
                oJScopes.Add("multidimensional_raster");
3627
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3628
0
                oJScopes.Add("vector");
3629
0
            oJDriver.Add("scopes", oJScopes);
3630
0
            CPLJSONArray oJCaps;
3631
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
3632
0
                oJCaps.Add("open");
3633
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
3634
0
                oJCaps.Add("create");
3635
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
3636
0
                oJCaps.Add("create_copy");
3637
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
3638
0
                oJCaps.Add("update");
3639
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
3640
0
                oJCaps.Add("virtual_io");
3641
0
            oJDriver.Add("capabilities", oJCaps);
3642
3643
0
            if (const char *pszExtensions = CSLFetchNameValueDef(
3644
0
                    papszMD, GDAL_DMD_EXTENSIONS,
3645
0
                    CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3646
0
            {
3647
0
                const CPLStringList aosExt(
3648
0
                    CSLTokenizeString2(pszExtensions, " ", 0));
3649
0
                CPLJSONArray oJExts;
3650
0
                for (int i = 0; i < aosExt.size(); ++i)
3651
0
                {
3652
0
                    oJExts.Add(aosExt[i]);
3653
0
                }
3654
0
                oJDriver.Add("file_extensions", oJExts);
3655
0
            }
3656
3657
0
            oArray.Add(oJDriver);
3658
0
        }
3659
3660
0
        return oArray.Format(CPLJSONObject::PrettyFormat::Pretty);
3661
0
    }
3662
3663
0
    std::string ret;
3664
0
    ret = "Supported Formats: (ro:read-only, rw:read-write, "
3665
0
          "+:write from scratch, u:update, "
3666
0
          "v:virtual-I/O s:subdatasets)\n";
3667
0
    for (int iDr = 0; iDr < GDALGetDriverCount(); iDr++)
3668
0
    {
3669
0
        GDALDriverH hDriver = GDALGetDriver(iDr);
3670
3671
0
        const char *pszRFlag = "", *pszWFlag, *pszVirtualIO, *pszSubdatasets;
3672
0
        CSLConstList papszMD = GDALGetMetadata(hDriver, nullptr);
3673
3674
0
        if (nOptions == GDAL_OF_RASTER &&
3675
0
            !CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false) &&
3676
            // HACK For CPHD driver to appear
3677
0
            !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3678
0
            continue;
3679
0
        if (nOptions == GDAL_OF_VECTOR &&
3680
0
            !CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3681
0
            continue;
3682
0
        if (nOptions == GDAL_OF_GNM &&
3683
0
            !CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3684
0
            continue;
3685
0
        if (nOptions == GDAL_OF_MULTIDIM_RASTER &&
3686
0
            !CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3687
0
            continue;
3688
3689
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
3690
0
            pszRFlag = "r";
3691
3692
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
3693
0
            pszWFlag = "w+";
3694
0
        else if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
3695
0
            pszWFlag = "w";
3696
0
        else
3697
0
            pszWFlag = "o";
3698
3699
0
        const char *pszUpdate = "";
3700
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
3701
0
            pszUpdate = "u";
3702
3703
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
3704
0
            pszVirtualIO = "v";
3705
0
        else
3706
0
            pszVirtualIO = "";
3707
3708
0
        if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
3709
0
            pszSubdatasets = "s";
3710
0
        else
3711
0
            pszSubdatasets = "";
3712
3713
0
        CPLString osKind;
3714
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
3715
0
            osKind = "raster";
3716
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
3717
0
        {
3718
0
            if (!osKind.empty())
3719
0
                osKind += ',';
3720
0
            osKind += "multidimensional raster";
3721
0
        }
3722
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
3723
0
        {
3724
0
            if (!osKind.empty())
3725
0
                osKind += ',';
3726
0
            osKind += "vector";
3727
0
        }
3728
0
        if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
3729
0
        {
3730
0
            if (!osKind.empty())
3731
0
                osKind += ',';
3732
0
            osKind += "geography network";
3733
0
        }
3734
0
        if (osKind.empty())
3735
0
            osKind = "unknown kind";
3736
3737
0
        std::string osExtensions;
3738
0
        if (const char *pszExtensions = CSLFetchNameValueDef(
3739
0
                papszMD, GDAL_DMD_EXTENSIONS,
3740
0
                CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSION)))
3741
0
        {
3742
0
            const CPLStringList aosExt(
3743
0
                CSLTokenizeString2(pszExtensions, " ", 0));
3744
0
            for (int i = 0; i < aosExt.size(); ++i)
3745
0
            {
3746
0
                if (i == 0)
3747
0
                    osExtensions = " (*.";
3748
0
                else
3749
0
                    osExtensions += ", *.";
3750
0
                osExtensions += aosExt[i];
3751
0
            }
3752
0
            if (!osExtensions.empty())
3753
0
                osExtensions += ')';
3754
0
        }
3755
3756
0
        ret += CPLSPrintf("  %s -%s- (%s%s%s%s%s): %s%s\n", /*ok*/
3757
0
                          GDALGetDriverShortName(hDriver), osKind.c_str(),
3758
0
                          pszRFlag, pszWFlag, pszUpdate, pszVirtualIO,
3759
0
                          pszSubdatasets, GDALGetDriverLongName(hDriver),
3760
0
                          osExtensions.c_str());
3761
0
    }
3762
3763
0
    return ret;
3764
0
}
3765
3766
/************************************************************************/
3767
/*                    GDALGeneralCmdLineProcessor()                     */
3768
/************************************************************************/
3769
3770
/**
3771
 * \brief General utility option processing.
3772
 *
3773
 * This function is intended to provide a variety of generic commandline
3774
 * options for all GDAL commandline utilities.  It takes care of the following
3775
 * commandline options:
3776
 *
3777
 *  \--version: report version of GDAL in use.
3778
 *  \--build: report build info about GDAL in use.
3779
 *  \--license: report GDAL license info.
3780
 *  \--formats: report all format drivers configured. Can be used with -json since 3.10
3781
 *  \--format [format]: report details of one format driver.
3782
 *  \--optfile filename: expand an option file into the argument list.
3783
 *  \--config key value: set system configuration option.
3784
 *  \--config key=value: set system configuration option (since GDAL 3.9)
3785
 *  \--debug [on/off/value]: set debug level.
3786
 *  \--mempreload dir: preload directory contents into /vsimem
3787
 *  \--pause: Pause for user input (allows time to attach debugger)
3788
 *  \--locale [locale]: Install a locale using setlocale() (debugging)
3789
 *  \--help-general: report detailed help on general options.
3790
 *
3791
 * The argument array is replaced "in place" and should be freed with
3792
 * CSLDestroy() when no longer needed.  The typical usage looks something
3793
 * like the following.  Note that the formats should be registered so that
3794
 * the \--formats and \--format options will work properly.
3795
 *
3796
 *  int main( int argc, char ** argv )
3797
 *  {
3798
 *    GDALAllRegister();
3799
 *
3800
 *    argc = GDALGeneralCmdLineProcessor( argc, &argv, 0 );
3801
 *    if( argc < 1 )
3802
 *        exit( -argc );
3803
 *
3804
 * @param nArgc number of values in the argument list.
3805
 * @param ppapszArgv pointer to the argument list array (will be updated in
3806
 * place).
3807
 * @param nOptions a or-able combination of GDAL_OF_RASTER and GDAL_OF_VECTOR
3808
 *                 to determine which drivers should be displayed by \--formats.
3809
 *                 If set to 0, GDAL_OF_RASTER is assumed.
3810
 *
3811
 * @return updated nArgc argument count.  Return of 0 requests terminate
3812
 * without error, return of -1 requests exit with error code.
3813
 */
3814
3815
int CPL_STDCALL GDALGeneralCmdLineProcessor(int nArgc, char ***ppapszArgv,
3816
                                            int nOptions)
3817
3818
0
{
3819
0
    CPLStringList aosReturn;
3820
0
    int iArg;
3821
0
    char **papszArgv = *ppapszArgv;
3822
3823
    /* -------------------------------------------------------------------- */
3824
    /*      Preserve the program name.                                      */
3825
    /* -------------------------------------------------------------------- */
3826
0
    aosReturn.AddString(papszArgv[0]);
3827
3828
    /* ==================================================================== */
3829
    /*      Loop over all arguments.                                        */
3830
    /* ==================================================================== */
3831
3832
    // Start with --debug, so that "my_command --config UNKNOWN_CONFIG_OPTION --debug on"
3833
    // detects and warns about a unknown config option.
3834
0
    for (iArg = 1; iArg < nArgc; iArg++)
3835
0
    {
3836
0
        if (EQUAL(papszArgv[iArg], "--config") && iArg + 2 < nArgc &&
3837
0
            EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
3838
0
        {
3839
0
            if (iArg + 1 >= nArgc)
3840
0
            {
3841
0
                CPLError(CE_Failure, CPLE_AppDefined,
3842
0
                         "--config option given without a key=value argument.");
3843
0
                return -1;
3844
0
            }
3845
3846
0
            const char *pszArg = papszArgv[iArg + 1];
3847
0
            if (strchr(pszArg, '=') != nullptr)
3848
0
            {
3849
0
                char *pszKey = nullptr;
3850
0
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
3851
0
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
3852
0
                {
3853
0
                    CPLSetConfigOption(pszKey, pszValue);
3854
0
                }
3855
0
                CPLFree(pszKey);
3856
0
                ++iArg;
3857
0
            }
3858
0
            else
3859
0
            {
3860
                // cppcheck-suppress knownConditionTrueFalse
3861
0
                if (iArg + 2 >= nArgc)
3862
0
                {
3863
0
                    CPLError(CE_Failure, CPLE_AppDefined,
3864
0
                             "--config option given without a key and value "
3865
0
                             "argument.");
3866
0
                    return -1;
3867
0
                }
3868
3869
0
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
3870
0
                    CPLSetConfigOption(papszArgv[iArg + 1],
3871
0
                                       papszArgv[iArg + 2]);
3872
3873
0
                iArg += 2;
3874
0
            }
3875
0
        }
3876
0
        else if (EQUAL(papszArgv[iArg], "--debug"))
3877
0
        {
3878
0
            if (iArg + 1 >= nArgc)
3879
0
            {
3880
0
                CPLError(CE_Failure, CPLE_AppDefined,
3881
0
                         "--debug option given without debug level.");
3882
0
                return -1;
3883
0
            }
3884
3885
0
            CPLSetConfigOption("CPL_DEBUG", papszArgv[iArg + 1]);
3886
0
            iArg += 1;
3887
0
        }
3888
0
    }
3889
3890
0
    for (iArg = 1; iArg < nArgc; iArg++)
3891
0
    {
3892
        /* --------------------------------------------------------------------
3893
         */
3894
        /*      --version */
3895
        /* --------------------------------------------------------------------
3896
         */
3897
0
        if (EQUAL(papszArgv[iArg], "--version"))
3898
0
        {
3899
0
            printf("%s\n", GDALVersionInfo("--version")); /*ok*/
3900
0
            return 0;
3901
0
        }
3902
3903
        /* --------------------------------------------------------------------
3904
         */
3905
        /*      --build */
3906
        /* --------------------------------------------------------------------
3907
         */
3908
0
        else if (EQUAL(papszArgv[iArg], "--build"))
3909
0
        {
3910
0
            printf("%s", GDALVersionInfo("BUILD_INFO")); /*ok*/
3911
0
            return 0;
3912
0
        }
3913
3914
        /* --------------------------------------------------------------------
3915
         */
3916
        /*      --license */
3917
        /* --------------------------------------------------------------------
3918
         */
3919
0
        else if (EQUAL(papszArgv[iArg], "--license"))
3920
0
        {
3921
0
            printf("%s\n", GDALVersionInfo("LICENSE")); /*ok*/
3922
0
            return 0;
3923
0
        }
3924
3925
        /* --------------------------------------------------------------------
3926
         */
3927
        /*      --config */
3928
        /* --------------------------------------------------------------------
3929
         */
3930
0
        else if (EQUAL(papszArgv[iArg], "--config"))
3931
0
        {
3932
0
            if (iArg + 1 >= nArgc)
3933
0
            {
3934
0
                CPLError(CE_Failure, CPLE_AppDefined,
3935
0
                         "--config option given without a key=value argument.");
3936
0
                return -1;
3937
0
            }
3938
3939
0
            const char *pszArg = papszArgv[iArg + 1];
3940
0
            if (strchr(pszArg, '=') != nullptr)
3941
0
            {
3942
0
                char *pszKey = nullptr;
3943
0
                const char *pszValue = CPLParseNameValue(pszArg, &pszKey);
3944
0
                if (pszKey && !EQUAL(pszKey, "CPL_DEBUG") && pszValue)
3945
0
                {
3946
0
                    CPLSetConfigOption(pszKey, pszValue);
3947
0
                }
3948
0
                CPLFree(pszKey);
3949
0
                ++iArg;
3950
0
            }
3951
0
            else
3952
0
            {
3953
0
                if (iArg + 2 >= nArgc)
3954
0
                {
3955
0
                    CPLError(CE_Failure, CPLE_AppDefined,
3956
0
                             "--config option given without a key and value "
3957
0
                             "argument.");
3958
0
                    return -1;
3959
0
                }
3960
3961
0
                if (!EQUAL(papszArgv[iArg + 1], "CPL_DEBUG"))
3962
0
                    CPLSetConfigOption(papszArgv[iArg + 1],
3963
0
                                       papszArgv[iArg + 2]);
3964
3965
0
                iArg += 2;
3966
0
            }
3967
0
        }
3968
3969
        /* --------------------------------------------------------------------
3970
         */
3971
        /*      --mempreload */
3972
        /* --------------------------------------------------------------------
3973
         */
3974
0
        else if (EQUAL(papszArgv[iArg], "--mempreload"))
3975
0
        {
3976
0
            if (iArg + 1 >= nArgc)
3977
0
            {
3978
0
                CPLError(CE_Failure, CPLE_AppDefined,
3979
0
                         "--mempreload option given without directory path.");
3980
0
                return -1;
3981
0
            }
3982
3983
0
            char **papszFiles = VSIReadDir(papszArgv[iArg + 1]);
3984
0
            if (CSLCount(papszFiles) == 0)
3985
0
            {
3986
0
                CPLError(CE_Failure, CPLE_AppDefined,
3987
0
                         "--mempreload given invalid or empty directory.");
3988
0
                return -1;
3989
0
            }
3990
3991
0
            for (int i = 0; papszFiles[i] != nullptr; i++)
3992
0
            {
3993
0
                if (EQUAL(papszFiles[i], ".") || EQUAL(papszFiles[i], ".."))
3994
0
                    continue;
3995
3996
0
                std::string osOldPath;
3997
0
                CPLString osNewPath;
3998
0
                osOldPath = CPLFormFilenameSafe(papszArgv[iArg + 1],
3999
0
                                                papszFiles[i], nullptr);
4000
0
                osNewPath.Printf("/vsimem/%s", papszFiles[i]);
4001
4002
0
                VSIStatBufL sStatBuf;
4003
0
                if (VSIStatL(osOldPath.c_str(), &sStatBuf) != 0 ||
4004
0
                    VSI_ISDIR(sStatBuf.st_mode))
4005
0
                {
4006
0
                    CPLDebug("VSI", "Skipping preload of %s.",
4007
0
                             osOldPath.c_str());
4008
0
                    continue;
4009
0
                }
4010
4011
0
                CPLDebug("VSI", "Preloading %s to %s.", osOldPath.c_str(),
4012
0
                         osNewPath.c_str());
4013
4014
0
                if (CPLCopyFile(osNewPath, osOldPath.c_str()) != 0)
4015
0
                {
4016
0
                    CPLError(CE_Failure, CPLE_AppDefined,
4017
0
                             "Failed to copy %s to /vsimem", osOldPath.c_str());
4018
0
                    return -1;
4019
0
                }
4020
0
            }
4021
4022
0
            CSLDestroy(papszFiles);
4023
0
            iArg += 1;
4024
0
        }
4025
4026
        /* --------------------------------------------------------------------
4027
         */
4028
        /*      --debug */
4029
        /* --------------------------------------------------------------------
4030
         */
4031
0
        else if (EQUAL(papszArgv[iArg], "--debug"))
4032
0
        {
4033
0
            if (iArg + 1 >= nArgc)
4034
0
            {
4035
0
                CPLError(CE_Failure, CPLE_AppDefined,
4036
0
                         "--debug option given without debug level.");
4037
0
                return -1;
4038
0
            }
4039
4040
0
            iArg += 1;
4041
0
        }
4042
4043
        /* --------------------------------------------------------------------
4044
         */
4045
        /*      --optfile */
4046
        /* --------------------------------------------------------------------
4047
         */
4048
0
        else if (EQUAL(papszArgv[iArg], "--optfile"))
4049
0
        {
4050
0
            if (iArg + 1 >= nArgc)
4051
0
            {
4052
0
                CPLError(CE_Failure, CPLE_AppDefined,
4053
0
                         "--optfile option given without filename.");
4054
0
                return -1;
4055
0
            }
4056
4057
0
            VSILFILE *fpOptFile = VSIFOpenL(papszArgv[iArg + 1], "rb");
4058
4059
0
            if (fpOptFile == nullptr)
4060
0
            {
4061
0
                CPLError(CE_Failure, CPLE_AppDefined,
4062
0
                         "Unable to open optfile '%s'.\n%s",
4063
0
                         papszArgv[iArg + 1], VSIStrerror(errno));
4064
0
                return -1;
4065
0
            }
4066
4067
0
            const char *pszLine;
4068
0
            CPLStringList aosArgvOptfile;
4069
            // dummy value as first argument to please
4070
            // GDALGeneralCmdLineProcessor()
4071
0
            aosArgvOptfile.AddString("");
4072
0
            bool bHasOptfile = false;
4073
0
            while ((pszLine = CPLReadLineL(fpOptFile)) != nullptr)
4074
0
            {
4075
0
                if (pszLine[0] == '#' || strlen(pszLine) == 0)
4076
0
                    continue;
4077
4078
0
                char **papszTokens = CSLTokenizeString(pszLine);
4079
0
                for (int i = 0;
4080
0
                     papszTokens != nullptr && papszTokens[i] != nullptr; i++)
4081
0
                {
4082
0
                    if (EQUAL(papszTokens[i], "--optfile"))
4083
0
                    {
4084
                        // To avoid potential recursion
4085
0
                        CPLError(CE_Warning, CPLE_AppDefined,
4086
0
                                 "--optfile not supported in a option file");
4087
0
                        bHasOptfile = true;
4088
0
                    }
4089
0
                    aosArgvOptfile.AddStringDirectly(papszTokens[i]);
4090
0
                    papszTokens[i] = nullptr;
4091
0
                }
4092
0
                CSLDestroy(papszTokens);
4093
0
            }
4094
4095
0
            VSIFCloseL(fpOptFile);
4096
4097
0
            char **papszArgvOptfile = aosArgvOptfile.StealList();
4098
0
            if (!bHasOptfile)
4099
0
            {
4100
0
                char **papszArgvOptfileBefore = papszArgvOptfile;
4101
0
                if (GDALGeneralCmdLineProcessor(CSLCount(papszArgvOptfile),
4102
0
                                                &papszArgvOptfile,
4103
0
                                                nOptions) < 0)
4104
0
                {
4105
0
                    CSLDestroy(papszArgvOptfile);
4106
0
                    return -1;
4107
0
                }
4108
0
                CSLDestroy(papszArgvOptfileBefore);
4109
0
            }
4110
4111
0
            char **papszIter = papszArgvOptfile + 1;
4112
0
            while (*papszIter)
4113
0
            {
4114
0
                aosReturn.AddString(*papszIter);
4115
0
                ++papszIter;
4116
0
            }
4117
0
            CSLDestroy(papszArgvOptfile);
4118
4119
0
            iArg += 1;
4120
0
        }
4121
4122
        /* --------------------------------------------------------------------
4123
         */
4124
        /*      --formats */
4125
        /* --------------------------------------------------------------------
4126
         */
4127
0
        else if (EQUAL(papszArgv[iArg], "--formats"))
4128
0
        {
4129
0
            bool bJSON = false;
4130
0
            for (int i = 1; i < nArgc; i++)
4131
0
            {
4132
0
                if (strcmp(papszArgv[i], "-json") == 0 ||
4133
0
                    strcmp(papszArgv[i], "--json") == 0)
4134
0
                {
4135
0
                    bJSON = true;
4136
0
                    break;
4137
0
                }
4138
0
            }
4139
4140
0
            printf("%s", GDALPrintDriverList(nOptions, bJSON).c_str()); /*ok*/
4141
4142
0
            return 0;
4143
0
        }
4144
4145
        /* --------------------------------------------------------------------
4146
         */
4147
        /*      --format */
4148
        /* --------------------------------------------------------------------
4149
         */
4150
0
        else if (EQUAL(papszArgv[iArg], "--format"))
4151
0
        {
4152
0
            GDALDriverH hDriver;
4153
4154
0
            if (iArg + 1 >= nArgc)
4155
0
            {
4156
0
                CPLError(CE_Failure, CPLE_AppDefined,
4157
0
                         "--format option given without a format code.");
4158
0
                return -1;
4159
0
            }
4160
4161
0
            hDriver = GDALGetDriverByName(papszArgv[iArg + 1]);
4162
0
            if (hDriver == nullptr)
4163
0
            {
4164
0
                CPLError(CE_Failure, CPLE_AppDefined,
4165
0
                         "--format option given with format '%s', but that "
4166
0
                         "format not\nrecognised.  Use the --formats option "
4167
0
                         "to get a list of available formats,\n"
4168
0
                         "and use the short code (i.e. GTiff or HFA) as the "
4169
0
                         "format identifier.\n",
4170
0
                         papszArgv[iArg + 1]);
4171
0
                return -1;
4172
0
            }
4173
4174
0
            printf("Format Details:\n"); /*ok*/
4175
0
            printf(/*ok*/ "  Short Name: %s\n",
4176
0
                   GDALGetDriverShortName(hDriver));
4177
0
            printf(/*ok*/ "  Long Name: %s\n", GDALGetDriverLongName(hDriver));
4178
4179
0
            CSLConstList papszMD = GDALGetMetadata(hDriver, nullptr);
4180
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_RASTER, false))
4181
0
                printf("  Supports: Raster\n"); /*ok*/
4182
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIDIM_RASTER, false))
4183
0
                printf("  Supports: Multidimensional raster\n"); /*ok*/
4184
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_VECTOR, false))
4185
0
                printf("  Supports: Vector\n"); /*ok*/
4186
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_GNM, false))
4187
0
                printf("  Supports: Geography Network\n"); /*ok*/
4188
4189
0
            const char *pszExt =
4190
0
                CSLFetchNameValue(papszMD, GDAL_DMD_EXTENSIONS);
4191
0
            if (pszExt != nullptr)
4192
0
                printf("  Extension%s: %s\n", /*ok*/
4193
0
                       (strchr(pszExt, ' ') ? "s" : ""), pszExt);
4194
4195
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE))
4196
0
                printf("  Mime Type: %s\n", /*ok*/
4197
0
                       CSLFetchNameValue(papszMD, GDAL_DMD_MIMETYPE));
4198
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC))
4199
0
                printf("  Help Topic: %s\n", /*ok*/
4200
0
                       CSLFetchNameValue(papszMD, GDAL_DMD_HELPTOPIC));
4201
4202
0
            if (CPLFetchBool(papszMD, GDAL_DMD_SUBDATASETS, false))
4203
0
                printf("  Supports: Raster subdatasets\n"); /*ok*/
4204
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_OPEN, false))
4205
0
                printf("  Supports: Open() - Open existing dataset.\n"); /*ok*/
4206
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE, false))
4207
0
                printf(/*ok*/
4208
0
                       "  Supports: Create() - Create writable dataset.\n");
4209
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATE_MULTIDIMENSIONAL, false))
4210
0
                printf(/*ok*/ "  Supports: CreateMultiDimensional() - Create "
4211
0
                              "multidimensional dataset.\n");
4212
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CREATECOPY, false))
4213
0
                printf(/*ok*/ "  Supports: CreateCopy() - Create dataset by "
4214
0
                              "copying "
4215
0
                              "another.\n");
4216
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_UPDATE, false))
4217
0
                printf("  Supports: Update\n"); /*ok*/
4218
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_VIRTUALIO, false))
4219
0
                printf("  Supports: Virtual IO - eg. /vsimem/\n"); /*ok*/
4220
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES))
4221
0
                printf("  Creation Datatypes: %s\n", /*ok*/
4222
0
                       CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONDATATYPES));
4223
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATATYPES))
4224
0
                printf("  Creation Field Datatypes: %s\n", /*ok*/
4225
0
                       CSLFetchNameValue(papszMD,
4226
0
                                         GDAL_DMD_CREATIONFIELDDATATYPES));
4227
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CREATIONFIELDDATASUBTYPES))
4228
0
                printf("  Creation Field Data Sub-types: %s\n", /*ok*/
4229
0
                       CSLFetchNameValue(papszMD,
4230
0
                                         GDAL_DMD_CREATIONFIELDDATASUBTYPES));
4231
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_FIELDS, false))
4232
0
                printf(/*ok*/ "  Supports: Creating fields with NOT NULL "
4233
0
                              "constraint.\n");
4234
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_UNIQUE_FIELDS, false))
4235
0
                printf(/*ok*/
4236
0
                       "  Supports: Creating fields with UNIQUE constraint.\n");
4237
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_DEFAULT_FIELDS, false))
4238
0
                printf(/*ok*/
4239
0
                       "  Supports: Creating fields with DEFAULT values.\n");
4240
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_NOTNULL_GEOMFIELDS, false))
4241
0
                /*ok*/ printf(
4242
0
                    "  Supports: Creating geometry fields with NOT NULL "
4243
0
                    "constraint.\n");
4244
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_CURVE_GEOMETRIES, false))
4245
0
                /*ok*/ printf("  Supports: Curve geometries.\n");
4246
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_Z_GEOMETRIES, false))
4247
0
                /*ok*/ printf("  Supports: 3D (Z) geometries.\n");
4248
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_MEASURED_GEOMETRIES, false))
4249
0
                /*ok*/ printf("  Supports: Measured (M) geometries.\n");
4250
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_HONOR_GEOM_COORDINATE_PRECISION,
4251
0
                             false))
4252
0
                /*ok*/ printf("  Supports: Writing geometries with given "
4253
0
                              "coordinate precision\n");
4254
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_READ, false))
4255
0
                printf("  Supports: Reading feature styles.\n"); /*ok*/
4256
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_FEATURE_STYLES_WRITE, false))
4257
0
                printf("  Supports: Writing feature styles.\n"); /*ok*/
4258
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_COORDINATE_EPOCH, false))
4259
0
                printf("  Supports: Coordinate epoch.\n"); /*ok*/
4260
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_MULTIPLE_VECTOR_LAYERS, false))
4261
0
                printf("  Supports: Multiple vector layers.\n"); /*ok*/
4262
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_FIELD_DOMAINS, false))
4263
0
                printf("  Supports: Reading field domains.\n"); /*ok*/
4264
0
            if (CPLFetchBool(papszMD, GDAL_DCAP_UPSERT, false))
4265
0
                printf("  Supports: Feature upsert.\n"); /*ok*/
4266
0
            if (CSLFetchNameValue(papszMD,
4267
0
                                  GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES))
4268
0
                printf("  Creation field domain types: %s\n", /*ok*/
4269
0
                       CSLFetchNameValue(papszMD,
4270
0
                                         GDAL_DMD_CREATION_FIELD_DOMAIN_TYPES));
4271
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_SUPPORTED_SQL_DIALECTS))
4272
0
                printf("  Supported SQL dialects: %s\n", /*ok*/
4273
0
                       CSLFetchNameValue(papszMD,
4274
0
                                         GDAL_DMD_SUPPORTED_SQL_DIALECTS));
4275
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_UPDATE_ITEMS))
4276
0
                printf("  Supported items for update: %s\n", /*ok*/
4277
0
                       CSLFetchNameValue(papszMD, GDAL_DMD_UPDATE_ITEMS));
4278
4279
0
            for (const char *key :
4280
0
                 {GDAL_DMD_CREATIONOPTIONLIST,
4281
0
                  GDAL_DMD_OVERVIEW_CREATIONOPTIONLIST,
4282
0
                  GDAL_DMD_MULTIDIM_DATASET_CREATIONOPTIONLIST,
4283
0
                  GDAL_DMD_MULTIDIM_GROUP_CREATIONOPTIONLIST,
4284
0
                  GDAL_DMD_MULTIDIM_DIMENSION_CREATIONOPTIONLIST,
4285
0
                  GDAL_DMD_MULTIDIM_ARRAY_CREATIONOPTIONLIST,
4286
0
                  GDAL_DMD_MULTIDIM_ARRAY_OPENOPTIONLIST,
4287
0
                  GDAL_DMD_MULTIDIM_ATTRIBUTE_CREATIONOPTIONLIST,
4288
0
                  GDAL_DS_LAYER_CREATIONOPTIONLIST})
4289
0
            {
4290
0
                if (CSLFetchNameValue(papszMD, key))
4291
0
                {
4292
0
                    CPLXMLNode *psCOL =
4293
0
                        CPLParseXMLString(CSLFetchNameValue(papszMD, key));
4294
0
                    StripIrrelevantOptions(psCOL, nOptions);
4295
0
                    char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
4296
4297
0
                    CPLDestroyXMLNode(psCOL);
4298
4299
0
                    printf("\n%s\n", pszFormattedXML); /*ok*/
4300
0
                    CPLFree(pszFormattedXML);
4301
0
                }
4302
0
            }
4303
4304
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX))
4305
0
                printf("  Connection prefix: %s\n", /*ok*/
4306
0
                       CSLFetchNameValue(papszMD, GDAL_DMD_CONNECTION_PREFIX));
4307
4308
0
            if (CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST))
4309
0
            {
4310
0
                CPLXMLNode *psCOL = CPLParseXMLString(
4311
0
                    CSLFetchNameValue(papszMD, GDAL_DMD_OPENOPTIONLIST));
4312
0
                StripIrrelevantOptions(psCOL, nOptions);
4313
0
                char *pszFormattedXML = CPLSerializeXMLTree(psCOL);
4314
4315
0
                CPLDestroyXMLNode(psCOL);
4316
4317
0
                printf("%s\n", pszFormattedXML); /*ok*/
4318
0
                CPLFree(pszFormattedXML);
4319
0
            }
4320
4321
0
            bool bFirstOtherOption = true;
4322
0
            for (CSLConstList papszIter = papszMD; papszIter && *papszIter;
4323
0
                 ++papszIter)
4324
0
            {
4325
0
                if (!STARTS_WITH(*papszIter, "DCAP_") &&
4326
0
                    !STARTS_WITH(*papszIter, "DMD_") &&
4327
0
                    !STARTS_WITH(*papszIter, "DS_") &&
4328
0
                    !STARTS_WITH(*papszIter, "OGR_DRIVER="))
4329
0
                {
4330
0
                    if (bFirstOtherOption)
4331
0
                        printf("  Other metadata items:\n"); /*ok*/
4332
0
                    bFirstOtherOption = false;
4333
0
                    printf("    %s\n", *papszIter); /*ok*/
4334
0
                }
4335
0
            }
4336
4337
0
            return 0;
4338
0
        }
4339
4340
        /* --------------------------------------------------------------------
4341
         */
4342
        /*      --help-general */
4343
        /* --------------------------------------------------------------------
4344
         */
4345
0
        else if (EQUAL(papszArgv[iArg], "--help-general"))
4346
0
        {
4347
0
            printf("Generic GDAL utility command options:\n");       /*ok*/
4348
0
            printf("  --version: report version of GDAL in use.\n"); /*ok*/
4349
0
            /*ok*/ printf(
4350
0
                "  --build: report detailed information about GDAL in "
4351
0
                "use.\n");
4352
0
            printf("  --license: report GDAL license info.\n"); /*ok*/
4353
0
            printf(                                             /*ok*/
4354
0
                   "  --formats: report all configured format drivers.\n"); /*ok*/
4355
0
            printf("  --format [<format>]: details of one format.\n"); /*ok*/
4356
0
            /*ok*/ printf(
4357
0
                "  --optfile filename: expand an option file into the "
4358
0
                "argument list.\n");
4359
0
            printf(/*ok*/
4360
0
                   "  --config <key> <value> or --config <key>=<value>: set "
4361
0
                   "system configuration option.\n");               /*ok*/
4362
0
            printf("  --debug [on/off/value]: set debug level.\n"); /*ok*/
4363
0
            /*ok*/ printf(                                          /*ok*/
4364
0
                          "  --pause: wait for user input, time to attach "
4365
0
                          "debugger\n");
4366
0
            printf("  --locale [<locale>]: install locale for debugging " /*ok*/
4367
0
                   "(i.e. en_US.UTF-8)\n");
4368
0
            printf("  --help-general: report detailed help on general " /*ok*/
4369
0
                   "options.\n");
4370
4371
0
            return 0;
4372
0
        }
4373
4374
        /* --------------------------------------------------------------------
4375
         */
4376
        /*      --locale */
4377
        /* --------------------------------------------------------------------
4378
         */
4379
0
        else if (iArg < nArgc - 1 && EQUAL(papszArgv[iArg], "--locale"))
4380
0
        {
4381
0
            CPLsetlocale(LC_ALL, papszArgv[++iArg]);
4382
0
        }
4383
4384
        /* --------------------------------------------------------------------
4385
         */
4386
        /*      --pause */
4387
        /* --------------------------------------------------------------------
4388
         */
4389
0
        else if (EQUAL(papszArgv[iArg], "--pause"))
4390
0
        {
4391
0
            std::cout << "Hit <ENTER> to Continue." << std::endl;
4392
0
            std::cin.clear();
4393
0
            std::cin.ignore(cpl::NumericLimits<std::streamsize>::max(), '\n');
4394
0
        }
4395
4396
        /* --------------------------------------------------------------------
4397
         */
4398
        /*      Carry through unrecognized options. */
4399
        /* --------------------------------------------------------------------
4400
         */
4401
0
        else
4402
0
        {
4403
0
            aosReturn.AddString(papszArgv[iArg]);
4404
0
        }
4405
0
    }
4406
4407
0
    const int nSize = aosReturn.size();
4408
0
    *ppapszArgv = aosReturn.StealList();
4409
4410
0
    return nSize;
4411
0
}
4412
4413
/************************************************************************/
4414
/*                          _FetchDblFromMD()                           */
4415
/************************************************************************/
4416
4417
static bool _FetchDblFromMD(CSLConstList papszMD, const char *pszKey,
4418
                            double *padfTarget, int nCount, double dfDefault)
4419
4420
0
{
4421
0
    char szFullKey[200];
4422
4423
0
    snprintf(szFullKey, sizeof(szFullKey), "%s", pszKey);
4424
4425
0
    const char *pszValue = CSLFetchNameValue(papszMD, szFullKey);
4426
4427
0
    for (int i = 0; i < nCount; i++)
4428
0
        padfTarget[i] = dfDefault;
4429
4430
0
    if (pszValue == nullptr)
4431
0
        return false;
4432
4433
0
    if (nCount == 1)
4434
0
    {
4435
0
        *padfTarget = CPLAtofM(pszValue);
4436
0
        return true;
4437
0
    }
4438
4439
0
    char **papszTokens = CSLTokenizeStringComplex(pszValue, " ,", FALSE, FALSE);
4440
4441
0
    if (CSLCount(papszTokens) != nCount)
4442
0
    {
4443
0
        CSLDestroy(papszTokens);
4444
0
        return false;
4445
0
    }
4446
4447
0
    for (int i = 0; i < nCount; i++)
4448
0
        padfTarget[i] = CPLAtofM(papszTokens[i]);
4449
4450
0
    CSLDestroy(papszTokens);
4451
4452
0
    return true;
4453
0
}
4454
4455
/************************************************************************/
4456
/*                         GDALExtractRPCInfo()                         */
4457
/************************************************************************/
4458
4459
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
4460
 *
4461
 * The inverse of this function is RPCInfoV1ToMD() in alg/gdal_rpc.cpp
4462
 *
4463
 * @param papszMD Dictionary of metadata representing RPC
4464
 * @param psRPC (output) Pointer to structure to hold the RPC values.
4465
 * @return TRUE in case of success. FALSE in case of failure.
4466
 */
4467
int CPL_STDCALL GDALExtractRPCInfoV1(CSLConstList papszMD, GDALRPCInfoV1 *psRPC)
4468
4469
0
{
4470
0
    GDALRPCInfoV2 sRPC;
4471
0
    if (!GDALExtractRPCInfoV2(papszMD, &sRPC))
4472
0
        return FALSE;
4473
0
    memcpy(psRPC, &sRPC, sizeof(GDALRPCInfoV1));
4474
0
    return TRUE;
4475
0
}
4476
4477
/** Extract RPC info from metadata, and apply to an RPCInfo structure.
4478
 *
4479
 * The inverse of this function is RPCInfoV2ToMD() in alg/gdal_rpc.cpp
4480
 *
4481
 * @param papszMD Dictionary of metadata representing RPC
4482
 * @param psRPC (output) Pointer to structure to hold the RPC values.
4483
 * @return TRUE in case of success. FALSE in case of failure.
4484
 */
4485
int CPL_STDCALL GDALExtractRPCInfoV2(CSLConstList papszMD, GDALRPCInfoV2 *psRPC)
4486
4487
0
{
4488
0
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr)
4489
0
        return FALSE;
4490
4491
0
    if (CSLFetchNameValue(papszMD, RPC_LINE_NUM_COEFF) == nullptr ||
4492
0
        CSLFetchNameValue(papszMD, RPC_LINE_DEN_COEFF) == nullptr ||
4493
0
        CSLFetchNameValue(papszMD, RPC_SAMP_NUM_COEFF) == nullptr ||
4494
0
        CSLFetchNameValue(papszMD, RPC_SAMP_DEN_COEFF) == nullptr)
4495
0
    {
4496
0
        CPLError(CE_Failure, CPLE_AppDefined,
4497
0
                 "Some required RPC metadata missing in GDALExtractRPCInfo()");
4498
0
        return FALSE;
4499
0
    }
4500
4501
0
    _FetchDblFromMD(papszMD, RPC_ERR_BIAS, &(psRPC->dfERR_BIAS), 1, -1.0);
4502
0
    _FetchDblFromMD(papszMD, RPC_ERR_RAND, &(psRPC->dfERR_RAND), 1, -1.0);
4503
0
    _FetchDblFromMD(papszMD, RPC_LINE_OFF, &(psRPC->dfLINE_OFF), 1, 0.0);
4504
0
    _FetchDblFromMD(papszMD, RPC_LINE_SCALE, &(psRPC->dfLINE_SCALE), 1, 1.0);
4505
0
    _FetchDblFromMD(papszMD, RPC_SAMP_OFF, &(psRPC->dfSAMP_OFF), 1, 0.0);
4506
0
    _FetchDblFromMD(papszMD, RPC_SAMP_SCALE, &(psRPC->dfSAMP_SCALE), 1, 1.0);
4507
0
    _FetchDblFromMD(papszMD, RPC_HEIGHT_OFF, &(psRPC->dfHEIGHT_OFF), 1, 0.0);
4508
0
    _FetchDblFromMD(papszMD, RPC_HEIGHT_SCALE, &(psRPC->dfHEIGHT_SCALE), 1,
4509
0
                    1.0);
4510
0
    _FetchDblFromMD(papszMD, RPC_LAT_OFF, &(psRPC->dfLAT_OFF), 1, 0.0);
4511
0
    _FetchDblFromMD(papszMD, RPC_LAT_SCALE, &(psRPC->dfLAT_SCALE), 1, 1.0);
4512
0
    _FetchDblFromMD(papszMD, RPC_LONG_OFF, &(psRPC->dfLONG_OFF), 1, 0.0);
4513
0
    _FetchDblFromMD(papszMD, RPC_LONG_SCALE, &(psRPC->dfLONG_SCALE), 1, 1.0);
4514
4515
0
    _FetchDblFromMD(papszMD, RPC_LINE_NUM_COEFF, psRPC->adfLINE_NUM_COEFF, 20,
4516
0
                    0.0);
4517
0
    _FetchDblFromMD(papszMD, RPC_LINE_DEN_COEFF, psRPC->adfLINE_DEN_COEFF, 20,
4518
0
                    0.0);
4519
0
    _FetchDblFromMD(papszMD, RPC_SAMP_NUM_COEFF, psRPC->adfSAMP_NUM_COEFF, 20,
4520
0
                    0.0);
4521
0
    _FetchDblFromMD(papszMD, RPC_SAMP_DEN_COEFF, psRPC->adfSAMP_DEN_COEFF, 20,
4522
0
                    0.0);
4523
4524
0
    _FetchDblFromMD(papszMD, RPC_MIN_LONG, &(psRPC->dfMIN_LONG), 1, -180.0);
4525
0
    _FetchDblFromMD(papszMD, RPC_MIN_LAT, &(psRPC->dfMIN_LAT), 1, -90.0);
4526
0
    _FetchDblFromMD(papszMD, RPC_MAX_LONG, &(psRPC->dfMAX_LONG), 1, 180.0);
4527
0
    _FetchDblFromMD(papszMD, RPC_MAX_LAT, &(psRPC->dfMAX_LAT), 1, 90.0);
4528
4529
0
    return TRUE;
4530
0
}
4531
4532
/************************************************************************/
4533
/*                     GDALFindAssociatedAuxFile()                      */
4534
/************************************************************************/
4535
4536
GDALDataset *GDALFindAssociatedAuxFile(const char *pszBasename,
4537
                                       GDALAccess eAccess,
4538
                                       GDALDataset *poDependentDS)
4539
4540
0
{
4541
0
    const char *pszAuxSuffixLC = "aux";
4542
0
    const char *pszAuxSuffixUC = "AUX";
4543
4544
0
    if (EQUAL(CPLGetExtensionSafe(pszBasename).c_str(), pszAuxSuffixLC))
4545
0
        return nullptr;
4546
4547
    /* -------------------------------------------------------------------- */
4548
    /*      Don't even try to look for an .aux file if we don't have a      */
4549
    /*      path of any kind.                                               */
4550
    /* -------------------------------------------------------------------- */
4551
0
    if (strlen(pszBasename) == 0)
4552
0
        return nullptr;
4553
4554
    /* -------------------------------------------------------------------- */
4555
    /*      We didn't find that, so try and find a corresponding aux        */
4556
    /*      file.  Check that we are the dependent file of the aux          */
4557
    /*      file, or if we aren't verify that the dependent file does       */
4558
    /*      not exist, likely mean it is us but some sort of renaming       */
4559
    /*      has occurred.                                                   */
4560
    /* -------------------------------------------------------------------- */
4561
0
    CPLString osJustFile = CPLGetFilename(pszBasename);  // without dir
4562
0
    CPLString osAuxFilename =
4563
0
        CPLResetExtensionSafe(pszBasename, pszAuxSuffixLC);
4564
0
    GDALDataset *poODS = nullptr;
4565
0
    GByte abyHeader[32];
4566
4567
0
    VSILFILE *fp = VSIFOpenL(osAuxFilename, "rb");
4568
4569
0
    if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
4570
0
    {
4571
        // Can't found file with lower case suffix. Try the upper case one.
4572
0
        osAuxFilename = CPLResetExtensionSafe(pszBasename, pszAuxSuffixUC);
4573
0
        fp = VSIFOpenL(osAuxFilename, "rb");
4574
0
    }
4575
4576
0
    if (fp != nullptr)
4577
0
    {
4578
0
        if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
4579
0
            STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
4580
0
                           "EHFA_HEADER_TAG"))
4581
0
        {
4582
            /* Avoid causing failure in opening of main file from SWIG bindings
4583
             */
4584
            /* when auxiliary file cannot be opened (#3269) */
4585
0
            CPLTurnFailureIntoWarningBackuper oErrorsToWarnings{};
4586
0
            if (poDependentDS != nullptr && poDependentDS->GetShared())
4587
0
                poODS = GDALDataset::FromHandle(
4588
0
                    GDALOpenShared(osAuxFilename, eAccess));
4589
0
            else
4590
0
                poODS =
4591
0
                    GDALDataset::FromHandle(GDALOpen(osAuxFilename, eAccess));
4592
0
        }
4593
0
        CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
4594
0
    }
4595
4596
    /* -------------------------------------------------------------------- */
4597
    /*      Try replacing extension with .aux                               */
4598
    /* -------------------------------------------------------------------- */
4599
0
    if (poODS != nullptr)
4600
0
    {
4601
0
        const char *pszDep =
4602
0
            poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
4603
0
        if (pszDep == nullptr)
4604
0
        {
4605
0
            CPLDebug("AUX", "Found %s but it has no dependent file, ignoring.",
4606
0
                     osAuxFilename.c_str());
4607
0
            GDALClose(poODS);
4608
0
            poODS = nullptr;
4609
0
        }
4610
0
        else if (!EQUAL(pszDep, osJustFile))
4611
0
        {
4612
0
            VSIStatBufL sStatBuf;
4613
4614
0
            if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
4615
0
            {
4616
0
                CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
4617
0
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4618
0
                GDALClose(poODS);
4619
0
                poODS = nullptr;
4620
0
            }
4621
0
            else
4622
0
            {
4623
0
                CPLDebug("AUX",
4624
0
                         "%s is for file %s, not %s, but since\n"
4625
0
                         "%s does not exist, we will use .aux file as our own.",
4626
0
                         osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4627
0
                         pszDep);
4628
0
            }
4629
0
        }
4630
4631
        /* --------------------------------------------------------------------
4632
         */
4633
        /*      Confirm that the aux file matches the configuration of the */
4634
        /*      dependent dataset. */
4635
        /* --------------------------------------------------------------------
4636
         */
4637
0
        if (poODS != nullptr && poDependentDS != nullptr &&
4638
0
            (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
4639
0
             poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
4640
0
             poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
4641
0
        {
4642
0
            CPLDebug("AUX",
4643
0
                     "Ignoring aux file %s as its raster configuration\n"
4644
0
                     "(%dP x %dL x %dB) does not match master file (%dP x %dL "
4645
0
                     "x %dB)",
4646
0
                     osAuxFilename.c_str(), poODS->GetRasterXSize(),
4647
0
                     poODS->GetRasterYSize(), poODS->GetRasterCount(),
4648
0
                     poDependentDS->GetRasterXSize(),
4649
0
                     poDependentDS->GetRasterYSize(),
4650
0
                     poDependentDS->GetRasterCount());
4651
4652
0
            GDALClose(poODS);
4653
0
            poODS = nullptr;
4654
0
        }
4655
0
    }
4656
4657
    /* -------------------------------------------------------------------- */
4658
    /*      Try appending .aux to the end of the filename.                  */
4659
    /* -------------------------------------------------------------------- */
4660
0
    if (poODS == nullptr)
4661
0
    {
4662
0
        osAuxFilename = pszBasename;
4663
0
        osAuxFilename += ".";
4664
0
        osAuxFilename += pszAuxSuffixLC;
4665
0
        fp = VSIFOpenL(osAuxFilename, "rb");
4666
0
        if (fp == nullptr && VSIIsCaseSensitiveFS(osAuxFilename))
4667
0
        {
4668
            // Can't found file with lower case suffix. Try the upper case one.
4669
0
            osAuxFilename = pszBasename;
4670
0
            osAuxFilename += ".";
4671
0
            osAuxFilename += pszAuxSuffixUC;
4672
0
            fp = VSIFOpenL(osAuxFilename, "rb");
4673
0
        }
4674
4675
0
        if (fp != nullptr)
4676
0
        {
4677
0
            if (VSIFReadL(abyHeader, 1, 32, fp) == 32 &&
4678
0
                STARTS_WITH_CI(reinterpret_cast<const char *>(abyHeader),
4679
0
                               "EHFA_HEADER_TAG"))
4680
0
            {
4681
                /* Avoid causing failure in opening of main file from SWIG
4682
                 * bindings */
4683
                /* when auxiliary file cannot be opened (#3269) */
4684
0
                CPLTurnFailureIntoWarningBackuper oErrorsToWarnings{};
4685
0
                if (poDependentDS != nullptr && poDependentDS->GetShared())
4686
0
                    poODS = GDALDataset::FromHandle(
4687
0
                        GDALOpenShared(osAuxFilename, eAccess));
4688
0
                else
4689
0
                    poODS = GDALDataset::FromHandle(
4690
0
                        GDALOpen(osAuxFilename, eAccess));
4691
0
            }
4692
0
            CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
4693
0
        }
4694
4695
0
        if (poODS != nullptr)
4696
0
        {
4697
0
            const char *pszDep =
4698
0
                poODS->GetMetadataItem("HFA_DEPENDENT_FILE", "HFA");
4699
0
            if (pszDep == nullptr)
4700
0
            {
4701
0
                CPLDebug("AUX",
4702
0
                         "Found %s but it has no dependent file, ignoring.",
4703
0
                         osAuxFilename.c_str());
4704
0
                GDALClose(poODS);
4705
0
                poODS = nullptr;
4706
0
            }
4707
0
            else if (!EQUAL(pszDep, osJustFile))
4708
0
            {
4709
0
                VSIStatBufL sStatBuf;
4710
4711
0
                if (VSIStatExL(pszDep, &sStatBuf, VSI_STAT_EXISTS_FLAG) == 0)
4712
0
                {
4713
0
                    CPLDebug("AUX", "%s is for file %s, not %s, ignoring.",
4714
0
                             osAuxFilename.c_str(), pszDep, osJustFile.c_str());
4715
0
                    GDALClose(poODS);
4716
0
                    poODS = nullptr;
4717
0
                }
4718
0
                else
4719
0
                {
4720
0
                    CPLDebug(
4721
0
                        "AUX",
4722
0
                        "%s is for file %s, not %s, but since\n"
4723
0
                        "%s does not exist, we will use .aux file as our own.",
4724
0
                        osAuxFilename.c_str(), pszDep, osJustFile.c_str(),
4725
0
                        pszDep);
4726
0
                }
4727
0
            }
4728
0
        }
4729
0
    }
4730
4731
    /* -------------------------------------------------------------------- */
4732
    /*      Confirm that the aux file matches the configuration of the      */
4733
    /*      dependent dataset.                                              */
4734
    /* -------------------------------------------------------------------- */
4735
0
    if (poODS != nullptr && poDependentDS != nullptr &&
4736
0
        (poODS->GetRasterCount() != poDependentDS->GetRasterCount() ||
4737
0
         poODS->GetRasterXSize() != poDependentDS->GetRasterXSize() ||
4738
0
         poODS->GetRasterYSize() != poDependentDS->GetRasterYSize()))
4739
0
    {
4740
0
        CPLDebug(
4741
0
            "AUX",
4742
0
            "Ignoring aux file %s as its raster configuration\n"
4743
0
            "(%dP x %dL x %dB) does not match master file (%dP x %dL x %dB)",
4744
0
            osAuxFilename.c_str(), poODS->GetRasterXSize(),
4745
0
            poODS->GetRasterYSize(), poODS->GetRasterCount(),
4746
0
            poDependentDS->GetRasterXSize(), poDependentDS->GetRasterYSize(),
4747
0
            poDependentDS->GetRasterCount());
4748
4749
0
        GDALClose(poODS);
4750
0
        poODS = nullptr;
4751
0
    }
4752
4753
0
    return poODS;
4754
0
}
4755
4756
/************************************************************************/
4757
/*    Infrastructure to check that dataset characteristics are valid    */
4758
/************************************************************************/
4759
4760
CPL_C_START
4761
4762
/**
4763
 * \brief Return TRUE if the dataset dimensions are valid.
4764
 *
4765
 * @param nXSize raster width
4766
 * @param nYSize raster height
4767
 *
4768
 */
4769
int GDALCheckDatasetDimensions(int nXSize, int nYSize)
4770
0
{
4771
0
    if (nXSize <= 0 || nYSize <= 0)
4772
0
    {
4773
0
        CPLError(CE_Failure, CPLE_AppDefined,
4774
0
                 "Invalid dataset dimensions : %d x %d", nXSize, nYSize);
4775
0
        return FALSE;
4776
0
    }
4777
0
    return TRUE;
4778
0
}
4779
4780
/**
4781
 * \brief Return TRUE if the band count is valid.
4782
 *
4783
 * If the configuration option GDAL_MAX_BAND_COUNT is defined,
4784
 * the band count will be compared to the maximum number of band allowed.
4785
 * If not defined, the maximum number allowed is 65536.
4786
 *
4787
 * @param nBands the band count
4788
 * @param bIsZeroAllowed TRUE if band count == 0 is allowed
4789
 *
4790
 */
4791
4792
int GDALCheckBandCount(int nBands, int bIsZeroAllowed)
4793
0
{
4794
0
    if (nBands < 0 || (!bIsZeroAllowed && nBands == 0))
4795
0
    {
4796
0
        CPLError(CE_Failure, CPLE_AppDefined, "Invalid band count : %d",
4797
0
                 nBands);
4798
0
        return FALSE;
4799
0
    }
4800
0
    const char *pszMaxBandCount =
4801
0
        CPLGetConfigOption("GDAL_MAX_BAND_COUNT", "65536");
4802
0
    int nMaxBands = std::clamp(atoi(pszMaxBandCount), 0, INT_MAX - 1);
4803
0
    if (nBands > nMaxBands)
4804
0
    {
4805
0
        CPLError(CE_Failure, CPLE_AppDefined,
4806
0
                 "Invalid band count : %d. Maximum allowed currently is %d. "
4807
0
                 "Define GDAL_MAX_BAND_COUNT to a higher level if it is a "
4808
0
                 "legitimate number.",
4809
0
                 nBands, nMaxBands);
4810
0
        return FALSE;
4811
0
    }
4812
0
    return TRUE;
4813
0
}
4814
4815
CPL_C_END
4816
4817
/************************************************************************/
4818
/*                     GDALSerializeGCPListToXML()                      */
4819
/************************************************************************/
4820
4821
void GDALSerializeGCPListToXML(CPLXMLNode *psParentNode,
4822
                               const std::vector<gdal::GCP> &asGCPs,
4823
                               const OGRSpatialReference *poGCP_SRS)
4824
0
{
4825
0
    CPLString oFmt;
4826
4827
0
    CPLXMLNode *psPamGCPList =
4828
0
        CPLCreateXMLNode(psParentNode, CXT_Element, "GCPList");
4829
4830
0
    CPLXMLNode *psLastChild = nullptr;
4831
4832
0
    if (poGCP_SRS != nullptr && !poGCP_SRS->IsEmpty())
4833
0
    {
4834
0
        char *pszWKT = nullptr;
4835
0
        poGCP_SRS->exportToWkt(&pszWKT);
4836
0
        CPLSetXMLValue(psPamGCPList, "#Projection", pszWKT);
4837
0
        CPLFree(pszWKT);
4838
0
        const auto &mapping = poGCP_SRS->GetDataAxisToSRSAxisMapping();
4839
0
        CPLString osMapping;
4840
0
        for (size_t i = 0; i < mapping.size(); ++i)
4841
0
        {
4842
0
            if (!osMapping.empty())
4843
0
                osMapping += ",";
4844
0
            osMapping += CPLSPrintf("%d", mapping[i]);
4845
0
        }
4846
0
        CPLSetXMLValue(psPamGCPList, "#dataAxisToSRSAxisMapping",
4847
0
                       osMapping.c_str());
4848
4849
0
        psLastChild = psPamGCPList->psChild->psNext;
4850
0
    }
4851
4852
0
    for (const gdal::GCP &gcp : asGCPs)
4853
0
    {
4854
0
        CPLXMLNode *psXMLGCP = CPLCreateXMLNode(nullptr, CXT_Element, "GCP");
4855
4856
0
        if (psLastChild == nullptr)
4857
0
            psPamGCPList->psChild = psXMLGCP;
4858
0
        else
4859
0
            psLastChild->psNext = psXMLGCP;
4860
0
        psLastChild = psXMLGCP;
4861
4862
0
        CPLSetXMLValue(psXMLGCP, "#Id", gcp.Id());
4863
4864
0
        if (gcp.Info() != nullptr && strlen(gcp.Info()) > 0)
4865
0
            CPLSetXMLValue(psXMLGCP, "Info", gcp.Info());
4866
4867
0
        CPLSetXMLValue(psXMLGCP, "#Pixel", oFmt.Printf("%.4f", gcp.Pixel()));
4868
4869
0
        CPLSetXMLValue(psXMLGCP, "#Line", oFmt.Printf("%.4f", gcp.Line()));
4870
4871
0
        CPLSetXMLValue(psXMLGCP, "#X", oFmt.Printf("%.12E", gcp.X()));
4872
4873
0
        CPLSetXMLValue(psXMLGCP, "#Y", oFmt.Printf("%.12E", gcp.Y()));
4874
4875
0
        if (gcp.Z() != 0.0)
4876
0
            CPLSetXMLValue(psXMLGCP, "#Z", oFmt.Printf("%.12E", gcp.Z()));
4877
0
    }
4878
0
}
4879
4880
/************************************************************************/
4881
/*                   GDALDeserializeGCPListFromXML()                    */
4882
/************************************************************************/
4883
4884
void GDALDeserializeGCPListFromXML(const CPLXMLNode *psGCPList,
4885
                                   std::vector<gdal::GCP> &asGCPs,
4886
                                   OGRSpatialReference **ppoGCP_SRS)
4887
0
{
4888
0
    if (ppoGCP_SRS)
4889
0
    {
4890
0
        const char *pszRawProj =
4891
0
            CPLGetXMLValue(psGCPList, "Projection", nullptr);
4892
4893
0
        *ppoGCP_SRS = nullptr;
4894
0
        if (pszRawProj && pszRawProj[0])
4895
0
        {
4896
0
            *ppoGCP_SRS = new OGRSpatialReference();
4897
0
            (*ppoGCP_SRS)
4898
0
                ->SetFromUserInput(
4899
0
                    pszRawProj,
4900
0
                    OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS);
4901
4902
0
            const char *pszMapping =
4903
0
                CPLGetXMLValue(psGCPList, "dataAxisToSRSAxisMapping", nullptr);
4904
0
            if (pszMapping)
4905
0
            {
4906
0
                char **papszTokens =
4907
0
                    CSLTokenizeStringComplex(pszMapping, ",", FALSE, FALSE);
4908
0
                std::vector<int> anMapping;
4909
0
                for (int i = 0; papszTokens && papszTokens[i]; i++)
4910
0
                {
4911
0
                    anMapping.push_back(atoi(papszTokens[i]));
4912
0
                }
4913
0
                CSLDestroy(papszTokens);
4914
0
                (*ppoGCP_SRS)->SetDataAxisToSRSAxisMapping(anMapping);
4915
0
            }
4916
0
            else
4917
0
            {
4918
0
                (*ppoGCP_SRS)
4919
0
                    ->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
4920
0
            }
4921
0
        }
4922
0
    }
4923
4924
0
    asGCPs.clear();
4925
0
    for (const CPLXMLNode *psXMLGCP = psGCPList->psChild; psXMLGCP;
4926
0
         psXMLGCP = psXMLGCP->psNext)
4927
0
    {
4928
0
        if (!EQUAL(psXMLGCP->pszValue, "GCP") || psXMLGCP->eType != CXT_Element)
4929
0
            continue;
4930
4931
0
        gdal::GCP gcp;
4932
0
        gcp.SetId(CPLGetXMLValue(psXMLGCP, "Id", ""));
4933
0
        gcp.SetInfo(CPLGetXMLValue(psXMLGCP, "Info", ""));
4934
4935
0
        const auto ParseDoubleValue =
4936
0
            [psXMLGCP](const char *pszParameter, double &dfVal)
4937
0
        {
4938
0
            const char *pszVal =
4939
0
                CPLGetXMLValue(psXMLGCP, pszParameter, nullptr);
4940
0
            if (!pszVal)
4941
0
            {
4942
0
                CPLError(CE_Failure, CPLE_AppDefined, "GCP#%s is missing",
4943
0
                         pszParameter);
4944
0
                return false;
4945
0
            }
4946
0
            char *endptr = nullptr;
4947
0
            dfVal = CPLStrtod(pszVal, &endptr);
4948
0
            if (endptr == pszVal)
4949
0
            {
4950
0
                CPLError(CE_Failure, CPLE_AppDefined,
4951
0
                         "GCP#%s=%s is an invalid value", pszParameter, pszVal);
4952
0
                return false;
4953
0
            }
4954
0
            return true;
4955
0
        };
4956
4957
0
        bool bOK = true;
4958
0
        if (!ParseDoubleValue("Pixel", gcp.Pixel()))
4959
0
            bOK = false;
4960
0
        if (!ParseDoubleValue("Line", gcp.Line()))
4961
0
            bOK = false;
4962
0
        if (!ParseDoubleValue("X", gcp.X()))
4963
0
            bOK = false;
4964
0
        if (!ParseDoubleValue("Y", gcp.Y()))
4965
0
            bOK = false;
4966
0
        const char *pszZ = CPLGetXMLValue(psXMLGCP, "Z", nullptr);
4967
0
        if (pszZ == nullptr)
4968
0
        {
4969
            // Note: GDAL 1.10.1 and older generated #GCPZ,
4970
            // but could not read it back.
4971
0
            pszZ = CPLGetXMLValue(psXMLGCP, "GCPZ", "0.0");
4972
0
        }
4973
0
        char *endptr = nullptr;
4974
0
        gcp.Z() = CPLStrtod(pszZ, &endptr);
4975
0
        if (endptr == pszZ)
4976
0
        {
4977
0
            CPLError(CE_Failure, CPLE_AppDefined,
4978
0
                     "GCP#Z=%s is an invalid value", pszZ);
4979
0
            bOK = false;
4980
0
        }
4981
4982
0
        if (bOK)
4983
0
        {
4984
0
            asGCPs.emplace_back(std::move(gcp));
4985
0
        }
4986
0
    }
4987
0
}
4988
4989
/************************************************************************/
4990
/*                   GDALSerializeOpenOptionsToXML()                    */
4991
/************************************************************************/
4992
4993
void GDALSerializeOpenOptionsToXML(CPLXMLNode *psParentNode,
4994
                                   CSLConstList papszOpenOptions)
4995
0
{
4996
0
    if (papszOpenOptions != nullptr)
4997
0
    {
4998
0
        CPLXMLNode *psOpenOptions =
4999
0
            CPLCreateXMLNode(psParentNode, CXT_Element, "OpenOptions");
5000
0
        CPLXMLNode *psLastChild = nullptr;
5001
5002
0
        for (CSLConstList papszIter = papszOpenOptions; *papszIter != nullptr;
5003
0
             papszIter++)
5004
0
        {
5005
0
            const char *pszRawValue;
5006
0
            char *pszKey = nullptr;
5007
0
            CPLXMLNode *psOOI;
5008
5009
0
            pszRawValue = CPLParseNameValue(*papszIter, &pszKey);
5010
5011
0
            psOOI = CPLCreateXMLNode(nullptr, CXT_Element, "OOI");
5012
0
            if (psLastChild == nullptr)
5013
0
                psOpenOptions->psChild = psOOI;
5014
0
            else
5015
0
                psLastChild->psNext = psOOI;
5016
0
            psLastChild = psOOI;
5017
5018
0
            CPLSetXMLValue(psOOI, "#key", pszKey);
5019
0
            CPLCreateXMLNode(psOOI, CXT_Text, pszRawValue);
5020
5021
0
            CPLFree(pszKey);
5022
0
        }
5023
0
    }
5024
0
}
5025
5026
/************************************************************************/
5027
/*                 GDALDeserializeOpenOptionsFromXML()                  */
5028
/************************************************************************/
5029
5030
char **GDALDeserializeOpenOptionsFromXML(const CPLXMLNode *psParentNode)
5031
0
{
5032
0
    char **papszOpenOptions = nullptr;
5033
0
    const CPLXMLNode *psOpenOptions =
5034
0
        CPLGetXMLNode(psParentNode, "OpenOptions");
5035
0
    if (psOpenOptions != nullptr)
5036
0
    {
5037
0
        const CPLXMLNode *psOOI;
5038
0
        for (psOOI = psOpenOptions->psChild; psOOI != nullptr;
5039
0
             psOOI = psOOI->psNext)
5040
0
        {
5041
0
            if (!EQUAL(psOOI->pszValue, "OOI") || psOOI->eType != CXT_Element ||
5042
0
                psOOI->psChild == nullptr ||
5043
0
                psOOI->psChild->psNext == nullptr ||
5044
0
                psOOI->psChild->eType != CXT_Attribute ||
5045
0
                psOOI->psChild->psChild == nullptr)
5046
0
                continue;
5047
5048
0
            char *pszName = psOOI->psChild->psChild->pszValue;
5049
0
            char *pszValue = psOOI->psChild->psNext->pszValue;
5050
0
            if (pszName != nullptr && pszValue != nullptr)
5051
0
                papszOpenOptions =
5052
0
                    CSLSetNameValue(papszOpenOptions, pszName, pszValue);
5053
0
        }
5054
0
    }
5055
0
    return papszOpenOptions;
5056
0
}
5057
5058
/************************************************************************/
5059
/*                     GDALRasterIOGetResampleAlg()                     */
5060
/************************************************************************/
5061
5062
GDALRIOResampleAlg GDALRasterIOGetResampleAlg(const char *pszResampling)
5063
0
{
5064
0
    GDALRIOResampleAlg eResampleAlg = GRIORA_NearestNeighbour;
5065
0
    if (STARTS_WITH_CI(pszResampling, "NEAR"))
5066
0
        eResampleAlg = GRIORA_NearestNeighbour;
5067
0
    else if (EQUAL(pszResampling, "BILINEAR"))
5068
0
        eResampleAlg = GRIORA_Bilinear;
5069
0
    else if (EQUAL(pszResampling, "CUBIC"))
5070
0
        eResampleAlg = GRIORA_Cubic;
5071
0
    else if (EQUAL(pszResampling, "CUBICSPLINE"))
5072
0
        eResampleAlg = GRIORA_CubicSpline;
5073
0
    else if (EQUAL(pszResampling, "LANCZOS"))
5074
0
        eResampleAlg = GRIORA_Lanczos;
5075
0
    else if (EQUAL(pszResampling, "AVERAGE"))
5076
0
        eResampleAlg = GRIORA_Average;
5077
0
    else if (EQUAL(pszResampling, "RMS"))
5078
0
        eResampleAlg = GRIORA_RMS;
5079
0
    else if (EQUAL(pszResampling, "MODE"))
5080
0
        eResampleAlg = GRIORA_Mode;
5081
0
    else if (EQUAL(pszResampling, "GAUSS"))
5082
0
        eResampleAlg = GRIORA_Gauss;
5083
0
    else
5084
0
        CPLError(CE_Warning, CPLE_NotSupported,
5085
0
                 "GDAL_RASTERIO_RESAMPLING = %s not supported", pszResampling);
5086
0
    return eResampleAlg;
5087
0
}
5088
5089
/************************************************************************/
5090
/*                   GDALRasterIOGetResampleAlgStr()                    */
5091
/************************************************************************/
5092
5093
const char *GDALRasterIOGetResampleAlg(GDALRIOResampleAlg eResampleAlg)
5094
0
{
5095
0
    const char *pszRet = "Unknown";
5096
0
    switch (eResampleAlg)
5097
0
    {
5098
0
        case GRIORA_NearestNeighbour:
5099
0
            pszRet = "NearestNeighbour";
5100
0
            break;
5101
0
        case GRIORA_Bilinear:
5102
0
            return "Bilinear";
5103
0
        case GRIORA_Cubic:
5104
0
            return "Cubic";
5105
0
        case GRIORA_CubicSpline:
5106
0
            return "CubicSpline";
5107
0
        case GRIORA_Lanczos:
5108
0
            return "Lanczos";
5109
0
        case GRIORA_Average:
5110
0
            return "Average";
5111
0
        case GRIORA_RMS:
5112
0
            return "RMS";
5113
0
        case GRIORA_Mode:
5114
0
            return "Mode";
5115
0
        case GRIORA_Gauss:
5116
0
            return "Gauss";
5117
0
        case GRIORA_RESERVED_START:
5118
0
        case GRIORA_RESERVED_END:
5119
0
            break;
5120
0
    }
5121
0
    return pszRet;
5122
0
}
5123
5124
/************************************************************************/
5125
/*                 GDALRasterIOExtraArgSetResampleAlg()                 */
5126
/************************************************************************/
5127
5128
void GDALRasterIOExtraArgSetResampleAlg(GDALRasterIOExtraArg *psExtraArg,
5129
                                        int nXSize, int nYSize, int nBufXSize,
5130
                                        int nBufYSize)
5131
0
{
5132
0
    if ((nBufXSize != nXSize || nBufYSize != nYSize) &&
5133
0
        psExtraArg->eResampleAlg == GRIORA_NearestNeighbour)
5134
0
    {
5135
0
        const char *pszResampling =
5136
0
            CPLGetConfigOption("GDAL_RASTERIO_RESAMPLING", nullptr);
5137
0
        if (pszResampling != nullptr)
5138
0
        {
5139
0
            psExtraArg->eResampleAlg =
5140
0
                GDALRasterIOGetResampleAlg(pszResampling);
5141
0
        }
5142
0
    }
5143
0
}
5144
5145
/************************************************************************/
5146
/*                    GDALCanFileAcceptSidecarFile()                    */
5147
/************************************************************************/
5148
5149
int GDALCanFileAcceptSidecarFile(const char *pszFilename)
5150
0
{
5151
0
    if (strstr(pszFilename, "/vsicurl/") && strchr(pszFilename, '?'))
5152
0
        return FALSE;
5153
    // Do no attempt reading side-car files on /vsisubfile/ (#6241)
5154
0
    if (strncmp(pszFilename, "/vsisubfile/", strlen("/vsisubfile/")) == 0)
5155
0
        return FALSE;
5156
0
    return TRUE;
5157
0
}
5158
5159
/************************************************************************/
5160
/*                 GDALCanReliablyUseSiblingFileList()                  */
5161
/************************************************************************/
5162
5163
/* Try to address https://github.com/OSGeo/gdal/issues/2903 */
5164
/* - On Apple HFS+ filesystem, filenames are stored in a variant of UTF-8 NFD */
5165
/*   (normalization form decomposed). The filesystem takes care of converting */
5166
/*   precomposed form as often coming from user interface to this NFD variant */
5167
/*   See
5168
 * https://stackoverflow.com/questions/6153345/different-utf8-encoding-in-filenames-os-x
5169
 */
5170
/*   And readdir() will return such NFD variant encoding. Consequently comparing
5171
 */
5172
/*   the user filename with ones with readdir() is not reliable */
5173
/* - APFS preserves both case and normalization of the filename on disk in all
5174
 */
5175
/*   variants. In macOS High Sierra, APFS is normalization-insensitive in both
5176
 */
5177
/*   the case-insensitive and case-sensitive variants, using a hash-based native
5178
 */
5179
/*   normalization scheme. APFS preserves the normalization of the filename and
5180
 */
5181
/*   uses hashes of the normalized form of the filename to provide normalization
5182
 */
5183
/*   insensitivity. */
5184
/*   From
5185
 * https://developer.apple.com/library/archive/documentation/FileManagement/Conceptual/APFS_Guide/FAQ/FAQ.html
5186
 */
5187
/*   Issues might still arise if the file has been created using one of the
5188
 * UTF-8 */
5189
/*   encoding (likely the decomposed one if using MacOS specific API), but the
5190
 */
5191
/*   string passed to GDAL for opening would be with another one (likely the
5192
 * precomposed one) */
5193
bool GDALCanReliablyUseSiblingFileList(const char *pszFilename)
5194
0
{
5195
#ifdef __APPLE__
5196
    for (int i = 0; pszFilename[i] != 0; ++i)
5197
    {
5198
        if (reinterpret_cast<const unsigned char *>(pszFilename)[i] > 127)
5199
        {
5200
            // non-ASCII character found
5201
5202
            // if this is a network storage, assume no issue
5203
            if (!VSIIsLocal(pszFilename))
5204
            {
5205
                return true;
5206
            }
5207
            return false;
5208
        }
5209
    }
5210
    return true;
5211
#else
5212
0
    (void)pszFilename;
5213
0
    return true;
5214
0
#endif
5215
0
}
5216
5217
/************************************************************************/
5218
/*                  GDALAdjustNoDataCloseToFloatMax()                   */
5219
/************************************************************************/
5220
5221
double GDALAdjustNoDataCloseToFloatMax(double dfVal)
5222
0
{
5223
0
    const auto kMaxFloat = cpl::NumericLimits<float>::max();
5224
0
    if (std::fabs(dfVal - -kMaxFloat) < 1e-10 * kMaxFloat)
5225
0
        return -kMaxFloat;
5226
0
    if (std::fabs(dfVal - kMaxFloat) < 1e-10 * kMaxFloat)
5227
0
        return kMaxFloat;
5228
0
    return dfVal;
5229
0
}
5230
5231
/************************************************************************/
5232
/*                        GDALCopyNoDataValue()                         */
5233
/************************************************************************/
5234
5235
/** Copy the nodata value from the source band to the target band if
5236
 * it can be exactly represented in the output data type.
5237
 *
5238
 * @param poDstBand Destination band.
5239
 * @param poSrcBand Source band band.
5240
 * @param[out] pbCannotBeExactlyRepresented Pointer to a boolean, or nullptr.
5241
 *             If the value cannot be exactly represented on the output data
5242
 *             type, *pbCannotBeExactlyRepresented will be set to true.
5243
 *
5244
 * @return true if the nodata value was successfully set.
5245
 */
5246
bool GDALCopyNoDataValue(GDALRasterBand *poDstBand, GDALRasterBand *poSrcBand,
5247
                         bool *pbCannotBeExactlyRepresented)
5248
0
{
5249
0
    if (pbCannotBeExactlyRepresented)
5250
0
        *pbCannotBeExactlyRepresented = false;
5251
0
    int bSuccess;
5252
0
    const auto eSrcDataType = poSrcBand->GetRasterDataType();
5253
0
    const auto eDstDataType = poDstBand->GetRasterDataType();
5254
0
    if (eSrcDataType == GDT_Int64)
5255
0
    {
5256
0
        const auto nNoData = poSrcBand->GetNoDataValueAsInt64(&bSuccess);
5257
0
        if (bSuccess)
5258
0
        {
5259
0
            if (eDstDataType == GDT_Int64)
5260
0
            {
5261
0
                return poDstBand->SetNoDataValueAsInt64(nNoData) == CE_None;
5262
0
            }
5263
0
            else if (eDstDataType == GDT_UInt64)
5264
0
            {
5265
0
                if (nNoData >= 0)
5266
0
                {
5267
0
                    return poDstBand->SetNoDataValueAsUInt64(
5268
0
                               static_cast<uint64_t>(nNoData)) == CE_None;
5269
0
                }
5270
0
            }
5271
0
            else if (nNoData ==
5272
0
                     static_cast<int64_t>(static_cast<double>(nNoData)))
5273
0
            {
5274
0
                const double dfValue = static_cast<double>(nNoData);
5275
0
                if (GDALIsValueExactAs(dfValue, eDstDataType))
5276
0
                    return poDstBand->SetNoDataValue(dfValue) == CE_None;
5277
0
            }
5278
0
        }
5279
0
    }
5280
0
    else if (eSrcDataType == GDT_UInt64)
5281
0
    {
5282
0
        const auto nNoData = poSrcBand->GetNoDataValueAsUInt64(&bSuccess);
5283
0
        if (bSuccess)
5284
0
        {
5285
0
            if (eDstDataType == GDT_UInt64)
5286
0
            {
5287
0
                return poDstBand->SetNoDataValueAsUInt64(nNoData) == CE_None;
5288
0
            }
5289
0
            else if (eDstDataType == GDT_Int64)
5290
0
            {
5291
0
                if (nNoData <
5292
0
                    static_cast<uint64_t>(cpl::NumericLimits<int64_t>::max()))
5293
0
                {
5294
0
                    return poDstBand->SetNoDataValueAsInt64(
5295
0
                               static_cast<int64_t>(nNoData)) == CE_None;
5296
0
                }
5297
0
            }
5298
0
            else if (nNoData ==
5299
0
                     static_cast<uint64_t>(static_cast<double>(nNoData)))
5300
0
            {
5301
0
                const double dfValue = static_cast<double>(nNoData);
5302
0
                if (GDALIsValueExactAs(dfValue, eDstDataType))
5303
0
                    return poDstBand->SetNoDataValue(dfValue) == CE_None;
5304
0
            }
5305
0
        }
5306
0
    }
5307
0
    else
5308
0
    {
5309
0
        const auto dfNoData = poSrcBand->GetNoDataValue(&bSuccess);
5310
0
        if (bSuccess)
5311
0
        {
5312
0
            if (eDstDataType == GDT_Int64)
5313
0
            {
5314
0
                if (dfNoData >= static_cast<double>(
5315
0
                                    cpl::NumericLimits<int64_t>::lowest()) &&
5316
0
                    dfNoData <= static_cast<double>(
5317
0
                                    cpl::NumericLimits<int64_t>::max()) &&
5318
0
                    dfNoData ==
5319
0
                        static_cast<double>(static_cast<int64_t>(dfNoData)))
5320
0
                {
5321
0
                    return poDstBand->SetNoDataValueAsInt64(
5322
0
                               static_cast<int64_t>(dfNoData)) == CE_None;
5323
0
                }
5324
0
            }
5325
0
            else if (eDstDataType == GDT_UInt64)
5326
0
            {
5327
0
                if (dfNoData >= static_cast<double>(
5328
0
                                    cpl::NumericLimits<uint64_t>::lowest()) &&
5329
0
                    dfNoData <= static_cast<double>(
5330
0
                                    cpl::NumericLimits<uint64_t>::max()) &&
5331
0
                    dfNoData ==
5332
0
                        static_cast<double>(static_cast<uint64_t>(dfNoData)))
5333
0
                {
5334
0
                    return poDstBand->SetNoDataValueAsInt64(
5335
0
                               static_cast<uint64_t>(dfNoData)) == CE_None;
5336
0
                }
5337
0
            }
5338
0
            else
5339
0
            {
5340
0
                return poDstBand->SetNoDataValue(dfNoData) == CE_None;
5341
0
            }
5342
0
        }
5343
0
    }
5344
0
    if (pbCannotBeExactlyRepresented)
5345
0
        *pbCannotBeExactlyRepresented = true;
5346
0
    return false;
5347
0
}
5348
5349
/************************************************************************/
5350
/*                   GDALGetNoDataValueCastToDouble()                   */
5351
/************************************************************************/
5352
5353
double GDALGetNoDataValueCastToDouble(int64_t nVal)
5354
0
{
5355
0
    const double dfVal = static_cast<double>(nVal);
5356
0
    if (static_cast<int64_t>(dfVal) != nVal)
5357
0
    {
5358
0
        CPLError(CE_Warning, CPLE_AppDefined,
5359
0
                 "GetNoDataValue() returns an approximate value of the "
5360
0
                 "true nodata value = " CPL_FRMT_GIB ". Use "
5361
0
                 "GetNoDataValueAsInt64() instead",
5362
0
                 static_cast<GIntBig>(nVal));
5363
0
    }
5364
0
    return dfVal;
5365
0
}
5366
5367
double GDALGetNoDataValueCastToDouble(uint64_t nVal)
5368
0
{
5369
0
    const double dfVal = static_cast<double>(nVal);
5370
0
    if (static_cast<uint64_t>(dfVal) != nVal)
5371
0
    {
5372
0
        CPLError(CE_Warning, CPLE_AppDefined,
5373
0
                 "GetNoDataValue() returns an approximate value of the "
5374
0
                 "true nodata value = " CPL_FRMT_GUIB ". Use "
5375
0
                 "GetNoDataValueAsUInt64() instead",
5376
0
                 static_cast<GUIntBig>(nVal));
5377
0
    }
5378
0
    return dfVal;
5379
0
}
5380
5381
/************************************************************************/
5382
/*                  GDALGetCompressionFormatForJPEG()                   */
5383
/************************************************************************/
5384
5385
//! @cond Doxygen_Suppress
5386
std::string GDALGetCompressionFormatForJPEG(VSILFILE *fp)
5387
0
{
5388
0
    std::string osRet;
5389
0
    const auto nSavedPos = VSIFTellL(fp);
5390
0
    GByte abyMarkerHeader[4];
5391
0
    if (VSIFSeekL(fp, 0, SEEK_SET) == 0 &&
5392
0
        VSIFReadL(abyMarkerHeader, 2, 1, fp) == 1 &&
5393
0
        abyMarkerHeader[0] == 0xFF && abyMarkerHeader[1] == 0xD8)
5394
0
    {
5395
0
        osRet = "JPEG";
5396
0
        bool bHasAPP14Adobe = false;
5397
0
        GByte abyAPP14AdobeMarkerData[14 - 2] = {0};
5398
0
        int nNumComponents = 0;
5399
0
        while (true)
5400
0
        {
5401
0
            const auto nCurPos = VSIFTellL(fp);
5402
0
            if (VSIFReadL(abyMarkerHeader, 4, 1, fp) != 1)
5403
0
                break;
5404
0
            if (abyMarkerHeader[0] != 0xFF)
5405
0
                break;
5406
0
            const GByte markerType = abyMarkerHeader[1];
5407
0
            const size_t nMarkerSize =
5408
0
                abyMarkerHeader[2] * 256 + abyMarkerHeader[3];
5409
0
            if (nMarkerSize < 2)
5410
0
                break;
5411
0
            if (markerType >= 0xC0 && markerType <= 0xCF &&
5412
0
                markerType != 0xC4 && markerType != 0xC8 && markerType != 0xCC)
5413
0
            {
5414
0
                switch (markerType)
5415
0
                {
5416
0
                    case 0xC0:
5417
0
                        osRet += ";frame_type=SOF0_baseline";
5418
0
                        break;
5419
0
                    case 0xC1:
5420
0
                        osRet += ";frame_type=SOF1_extended_sequential";
5421
0
                        break;
5422
0
                    case 0xC2:
5423
0
                        osRet += ";frame_type=SOF2_progressive_huffman";
5424
0
                        break;
5425
0
                    case 0xC3:
5426
0
                        osRet += ";frame_type=SOF3_lossless_huffman;libjpeg_"
5427
0
                                 "supported=no";
5428
0
                        break;
5429
0
                    case 0xC5:
5430
0
                        osRet += ";frame_type="
5431
0
                                 "SOF5_differential_sequential_huffman;"
5432
0
                                 "libjpeg_supported=no";
5433
0
                        break;
5434
0
                    case 0xC6:
5435
0
                        osRet += ";frame_type=SOF6_differential_progressive_"
5436
0
                                 "huffman;libjpeg_supported=no";
5437
0
                        break;
5438
0
                    case 0xC7:
5439
0
                        osRet += ";frame_type="
5440
0
                                 "SOF7_differential_lossless_huffman;"
5441
0
                                 "libjpeg_supported=no";
5442
0
                        break;
5443
0
                    case 0xC9:
5444
0
                        osRet += ";frame_type="
5445
0
                                 "SOF9_extended_sequential_arithmetic";
5446
0
                        break;
5447
0
                    case 0xCA:
5448
0
                        osRet += ";frame_type=SOF10_progressive_arithmetic";
5449
0
                        break;
5450
0
                    case 0xCB:
5451
0
                        osRet += ";frame_type="
5452
0
                                 "SOF11_lossless_arithmetic;libjpeg_"
5453
0
                                 "supported=no";
5454
0
                        break;
5455
0
                    case 0xCD:
5456
0
                        osRet += ";frame_type=SOF13_differential_sequential_"
5457
0
                                 "arithmetic;libjpeg_supported=no";
5458
0
                        break;
5459
0
                    case 0xCE:
5460
0
                        osRet += ";frame_type=SOF14_differential_progressive_"
5461
0
                                 "arithmetic;libjpeg_supported=no";
5462
0
                        break;
5463
0
                    case 0xCF:
5464
0
                        osRet += ";frame_type=SOF15_differential_lossless_"
5465
0
                                 "arithmetic;libjpeg_supported=no";
5466
0
                        break;
5467
0
                    default:
5468
0
                        break;
5469
0
                }
5470
0
                GByte abySegmentBegin[6];
5471
0
                if (VSIFReadL(abySegmentBegin, sizeof(abySegmentBegin), 1,
5472
0
                              fp) != 1)
5473
0
                    break;
5474
0
                osRet += ";bit_depth=";
5475
0
                osRet += CPLSPrintf("%d", abySegmentBegin[0]);
5476
0
                nNumComponents = abySegmentBegin[5];
5477
0
                osRet += ";num_components=";
5478
0
                osRet += CPLSPrintf("%d", nNumComponents);
5479
0
                if (nNumComponents == 3)
5480
0
                {
5481
0
                    GByte abySegmentNext[3 * 3];
5482
0
                    if (VSIFReadL(abySegmentNext, sizeof(abySegmentNext), 1,
5483
0
                                  fp) != 1)
5484
0
                        break;
5485
0
                    if (abySegmentNext[0] == 1 && abySegmentNext[1] == 0x11 &&
5486
0
                        abySegmentNext[3] == 2 && abySegmentNext[4] == 0x11 &&
5487
0
                        abySegmentNext[6] == 3 && abySegmentNext[7] == 0x11)
5488
0
                    {
5489
                        // no subsampling
5490
0
                        osRet += ";subsampling=4:4:4";
5491
0
                    }
5492
0
                    else if (abySegmentNext[0] == 1 &&
5493
0
                             abySegmentNext[1] == 0x22 &&
5494
0
                             abySegmentNext[3] == 2 &&
5495
0
                             abySegmentNext[4] == 0x11 &&
5496
0
                             abySegmentNext[6] == 3 &&
5497
0
                             abySegmentNext[7] == 0x11)
5498
0
                    {
5499
                        // classic subsampling
5500
0
                        osRet += ";subsampling=4:2:0";
5501
0
                    }
5502
0
                    else if (abySegmentNext[0] == 1 &&
5503
0
                             abySegmentNext[1] == 0x21 &&
5504
0
                             abySegmentNext[3] == 2 &&
5505
0
                             abySegmentNext[4] == 0x11 &&
5506
0
                             abySegmentNext[6] == 3 &&
5507
0
                             abySegmentNext[7] == 0x11)
5508
0
                    {
5509
0
                        osRet += ";subsampling=4:2:2";
5510
0
                    }
5511
0
                }
5512
0
            }
5513
0
            else if (markerType == 0xEE && nMarkerSize == 14)
5514
0
            {
5515
0
                if (VSIFReadL(abyAPP14AdobeMarkerData,
5516
0
                              sizeof(abyAPP14AdobeMarkerData), 1, fp) == 1 &&
5517
0
                    memcmp(abyAPP14AdobeMarkerData, "Adobe", strlen("Adobe")) ==
5518
0
                        0)
5519
0
                {
5520
0
                    bHasAPP14Adobe = true;
5521
0
                }
5522
0
            }
5523
0
            else if (markerType == 0xDA)
5524
0
            {
5525
                // Start of scan
5526
0
                break;
5527
0
            }
5528
0
            VSIFSeekL(fp, nCurPos + nMarkerSize + 2, SEEK_SET);
5529
0
        }
5530
0
        std::string osColorspace;
5531
0
        if (bHasAPP14Adobe)
5532
0
        {
5533
0
            if (abyAPP14AdobeMarkerData[11] == 0)
5534
0
            {
5535
0
                if (nNumComponents == 3)
5536
0
                    osColorspace = "RGB";
5537
0
                else if (nNumComponents == 4)
5538
0
                    osColorspace = "CMYK";
5539
0
            }
5540
0
            else if (abyAPP14AdobeMarkerData[11] == 1)
5541
0
            {
5542
0
                osColorspace = "YCbCr";
5543
0
            }
5544
0
            else if (abyAPP14AdobeMarkerData[11] == 2)
5545
0
            {
5546
0
                osColorspace = "YCCK";
5547
0
            }
5548
0
        }
5549
0
        else
5550
0
        {
5551
0
            if (nNumComponents == 3)
5552
0
                osColorspace = "YCbCr";
5553
0
            else if (nNumComponents == 4)
5554
0
                osColorspace = "CMYK";
5555
0
        }
5556
0
        osRet += ";colorspace=";
5557
0
        if (!osColorspace.empty())
5558
0
            osRet += osColorspace;
5559
0
        else
5560
0
            osRet += "unknown";
5561
0
    }
5562
0
    if (VSIFSeekL(fp, nSavedPos, SEEK_SET) != 0)
5563
0
    {
5564
0
        CPLError(CE_Failure, CPLE_AppDefined,
5565
0
                 "VSIFSeekL(fp, nSavedPos, SEEK_SET) failed");
5566
0
    }
5567
0
    return osRet;
5568
0
}
5569
5570
std::string GDALGetCompressionFormatForJPEG(const void *pBuffer,
5571
                                            size_t nBufferSize)
5572
0
{
5573
0
    VSILFILE *fp = VSIFileFromMemBuffer(
5574
0
        nullptr, static_cast<GByte *>(const_cast<void *>(pBuffer)), nBufferSize,
5575
0
        false);
5576
0
    std::string osRet = GDALGetCompressionFormatForJPEG(fp);
5577
0
    VSIFCloseL(fp);
5578
0
    return osRet;
5579
0
}
5580
5581
//! @endcond
5582
5583
/************************************************************************/
5584
/*                   GDALGetNoDataReplacementValue()                    */
5585
/************************************************************************/
5586
5587
/**
5588
 * \brief Returns a replacement value for a nodata value or 0 if dfNoDataValue
5589
 *        is out of range for the specified data type (dt).
5590
 *        For UInt64 and Int64 data type this function cannot reliably trusted
5591
 *        because their nodata values might not always be representable exactly
5592
 *        as a double, in particular the maximum absolute value for those types
5593
 *        is 2^53.
5594
 *
5595
 * The replacement value is a value that can be used in a computation
5596
 * whose result would match by accident the nodata value, whereas it is
5597
 * meant to be valid. For example, for a dataset with a nodata value of 0,
5598
 * when averaging -1 and 1, one would get normally a value of 0. The
5599
 * replacement nodata value can then be substituted to that 0 value to still
5600
 * get a valid value, as close as practical to the true value, while being
5601
 * different from the nodata value.
5602
 *
5603
 * @param dt Data type
5604
 * @param dfNoDataValue The no data value
5605
5606
 * @since GDAL 3.9
5607
 */
5608
double GDALGetNoDataReplacementValue(GDALDataType dt, double dfNoDataValue)
5609
0
{
5610
5611
    // The logic here is to check if the value is out of range for the
5612
    // specified data type and return a replacement value if it is, return
5613
    // 0 otherwise.
5614
0
    double dfReplacementVal = dfNoDataValue;
5615
0
    if (dt == GDT_UInt8)
5616
0
    {
5617
0
        if (GDALClampDoubleValue(dfNoDataValue,
5618
0
                                 cpl::NumericLimits<uint8_t>::lowest(),
5619
0
                                 cpl::NumericLimits<uint8_t>::max()))
5620
0
        {
5621
0
            return 0;
5622
0
        }
5623
0
        if (dfNoDataValue == cpl::NumericLimits<unsigned char>::max())
5624
0
            dfReplacementVal = cpl::NumericLimits<unsigned char>::max() - 1;
5625
0
        else
5626
0
            dfReplacementVal = dfNoDataValue + 1;
5627
0
    }
5628
0
    else if (dt == GDT_Int8)
5629
0
    {
5630
0
        if (GDALClampDoubleValue(dfNoDataValue,
5631
0
                                 cpl::NumericLimits<int8_t>::lowest(),
5632
0
                                 cpl::NumericLimits<int8_t>::max()))
5633
0
        {
5634
0
            return 0;
5635
0
        }
5636
0
        if (dfNoDataValue == cpl::NumericLimits<GInt8>::max())
5637
0
            dfReplacementVal = cpl::NumericLimits<GInt8>::max() - 1;
5638
0
        else
5639
0
            dfReplacementVal = dfNoDataValue + 1;
5640
0
    }
5641
0
    else if (dt == GDT_UInt16)
5642
0
    {
5643
0
        if (GDALClampDoubleValue(dfNoDataValue,
5644
0
                                 cpl::NumericLimits<uint16_t>::lowest(),
5645
0
                                 cpl::NumericLimits<uint16_t>::max()))
5646
0
        {
5647
0
            return 0;
5648
0
        }
5649
0
        if (dfNoDataValue == cpl::NumericLimits<GUInt16>::max())
5650
0
            dfReplacementVal = cpl::NumericLimits<GUInt16>::max() - 1;
5651
0
        else
5652
0
            dfReplacementVal = dfNoDataValue + 1;
5653
0
    }
5654
0
    else if (dt == GDT_Int16)
5655
0
    {
5656
0
        if (GDALClampDoubleValue(dfNoDataValue,
5657
0
                                 cpl::NumericLimits<int16_t>::lowest(),
5658
0
                                 cpl::NumericLimits<int16_t>::max()))
5659
0
        {
5660
0
            return 0;
5661
0
        }
5662
0
        if (dfNoDataValue == cpl::NumericLimits<GInt16>::max())
5663
0
            dfReplacementVal = cpl::NumericLimits<GInt16>::max() - 1;
5664
0
        else
5665
0
            dfReplacementVal = dfNoDataValue + 1;
5666
0
    }
5667
0
    else if (dt == GDT_UInt32)
5668
0
    {
5669
0
        if (GDALClampDoubleValue(dfNoDataValue,
5670
0
                                 cpl::NumericLimits<uint32_t>::lowest(),
5671
0
                                 cpl::NumericLimits<uint32_t>::max()))
5672
0
        {
5673
0
            return 0;
5674
0
        }
5675
0
        if (dfNoDataValue == cpl::NumericLimits<GUInt32>::max())
5676
0
            dfReplacementVal = cpl::NumericLimits<GUInt32>::max() - 1;
5677
0
        else
5678
0
            dfReplacementVal = dfNoDataValue + 1;
5679
0
    }
5680
0
    else if (dt == GDT_Int32)
5681
0
    {
5682
0
        if (GDALClampDoubleValue(dfNoDataValue,
5683
0
                                 cpl::NumericLimits<int32_t>::lowest(),
5684
0
                                 cpl::NumericLimits<int32_t>::max()))
5685
0
        {
5686
0
            return 0;
5687
0
        }
5688
0
        if (dfNoDataValue == cpl::NumericLimits<int32_t>::max())
5689
0
            dfReplacementVal = cpl::NumericLimits<int32_t>::max() - 1;
5690
0
        else
5691
0
            dfReplacementVal = dfNoDataValue + 1;
5692
0
    }
5693
0
    else if (dt == GDT_UInt64)
5694
0
    {
5695
        // Implicit conversion from 'unsigned long' to 'double' changes value from 18446744073709551615 to 18446744073709551616
5696
        // so we take the next lower value representable as a double 18446744073709549567
5697
0
        static const double dfMaxUInt64Value{
5698
0
            std::nextafter(
5699
0
                static_cast<double>(cpl::NumericLimits<uint64_t>::max()), 0) -
5700
0
            1};
5701
5702
0
        if (GDALClampDoubleValue(dfNoDataValue,
5703
0
                                 cpl::NumericLimits<uint64_t>::lowest(),
5704
0
                                 cpl::NumericLimits<uint64_t>::max()))
5705
0
        {
5706
0
            return 0;
5707
0
        }
5708
5709
0
        if (dfNoDataValue >=
5710
0
            static_cast<double>(cpl::NumericLimits<uint64_t>::max()))
5711
0
            dfReplacementVal = dfMaxUInt64Value;
5712
0
        else
5713
0
            dfReplacementVal = dfNoDataValue + 1;
5714
0
    }
5715
0
    else if (dt == GDT_Int64)
5716
0
    {
5717
        // Implicit conversion from 'long' to 'double' changes value from 9223372036854775807 to 9223372036854775808
5718
        // so we take the next lower value representable as a double 9223372036854774784
5719
0
        static const double dfMaxInt64Value{
5720
0
            std::nextafter(
5721
0
                static_cast<double>(cpl::NumericLimits<int64_t>::max()), 0) -
5722
0
            1};
5723
5724
0
        if (GDALClampDoubleValue(dfNoDataValue,
5725
0
                                 cpl::NumericLimits<int64_t>::lowest(),
5726
0
                                 cpl::NumericLimits<int64_t>::max()))
5727
0
        {
5728
0
            return 0;
5729
0
        }
5730
5731
0
        if (dfNoDataValue >=
5732
0
            static_cast<double>(cpl::NumericLimits<int64_t>::max()))
5733
0
            dfReplacementVal = dfMaxInt64Value;
5734
0
        else
5735
0
            dfReplacementVal = dfNoDataValue + 1;
5736
0
    }
5737
0
    else if (dt == GDT_Float16)
5738
0
    {
5739
5740
0
        if (GDALClampDoubleValue(dfNoDataValue,
5741
0
                                 cpl::NumericLimits<GFloat16>::lowest(),
5742
0
                                 cpl::NumericLimits<GFloat16>::max()))
5743
0
        {
5744
0
            return 0;
5745
0
        }
5746
5747
0
        if (dfNoDataValue == cpl::NumericLimits<GFloat16>::max())
5748
0
        {
5749
0
            using std::nextafter;
5750
0
            dfReplacementVal =
5751
0
                nextafter(static_cast<GFloat16>(dfNoDataValue), GFloat16(0.0f));
5752
0
        }
5753
0
        else
5754
0
        {
5755
0
            using std::nextafter;
5756
0
            dfReplacementVal = nextafter(static_cast<GFloat16>(dfNoDataValue),
5757
0
                                         cpl::NumericLimits<GFloat16>::max());
5758
0
        }
5759
0
    }
5760
0
    else if (dt == GDT_Float32)
5761
0
    {
5762
5763
0
        if (GDALClampDoubleValue(dfNoDataValue,
5764
0
                                 cpl::NumericLimits<float>::lowest(),
5765
0
                                 cpl::NumericLimits<float>::max()))
5766
0
        {
5767
0
            return 0;
5768
0
        }
5769
5770
0
        if (dfNoDataValue == cpl::NumericLimits<float>::max())
5771
0
        {
5772
0
            dfReplacementVal =
5773
0
                std::nextafter(static_cast<float>(dfNoDataValue), 0.0f);
5774
0
        }
5775
0
        else
5776
0
        {
5777
0
            dfReplacementVal = std::nextafter(static_cast<float>(dfNoDataValue),
5778
0
                                              cpl::NumericLimits<float>::max());
5779
0
        }
5780
0
    }
5781
0
    else if (dt == GDT_Float64)
5782
0
    {
5783
0
        if (GDALClampDoubleValue(dfNoDataValue,
5784
0
                                 cpl::NumericLimits<double>::lowest(),
5785
0
                                 cpl::NumericLimits<double>::max()))
5786
0
        {
5787
0
            return 0;
5788
0
        }
5789
5790
0
        if (dfNoDataValue == cpl::NumericLimits<double>::max())
5791
0
        {
5792
0
            dfReplacementVal = std::nextafter(dfNoDataValue, 0.0);
5793
0
        }
5794
0
        else
5795
0
        {
5796
0
            dfReplacementVal = std::nextafter(
5797
0
                dfNoDataValue, cpl::NumericLimits<double>::max());
5798
0
        }
5799
0
    }
5800
5801
0
    return dfReplacementVal;
5802
0
}
5803
5804
/************************************************************************/
5805
/*                       GDALGetCacheDirectory()                        */
5806
/************************************************************************/
5807
5808
/** Return the root path of the GDAL cache.
5809
 *
5810
 * If the GDAL_CACHE_DIRECTORY configuration option is set, its value will
5811
 * be returned.
5812
 * Otherwise if the XDG_CACHE_HOME environment variable is set,
5813
 * ${XDG_CACHE_HOME}/.gdal will be returned.
5814
 * Otherwise ${HOME}/.gdal on Unix or$ ${USERPROFILE}/.gdal on Windows will
5815
 * be returned.
5816
 * Otherwise ${CPL_TMPDIR|TMPDIR|TEMP}/.gdal_${USERNAME|USER} will be returned.
5817
 * Otherwise empty string will be returned.
5818
 *
5819
 * @since GDAL 3.11
5820
 */
5821
std::string GDALGetCacheDirectory()
5822
0
{
5823
0
    if (const char *pszGDAL_CACHE_DIRECTORY =
5824
0
            CPLGetConfigOption("GDAL_CACHE_DIRECTORY", nullptr))
5825
0
    {
5826
0
        return pszGDAL_CACHE_DIRECTORY;
5827
0
    }
5828
5829
0
    if (const char *pszXDG_CACHE_HOME =
5830
0
            CPLGetConfigOption("XDG_CACHE_HOME", nullptr))
5831
0
    {
5832
0
        return CPLFormFilenameSafe(pszXDG_CACHE_HOME, "gdal", nullptr);
5833
0
    }
5834
5835
#ifdef _WIN32
5836
    const char *pszHome = CPLGetConfigOption("USERPROFILE", nullptr);
5837
#else
5838
0
    const char *pszHome = CPLGetConfigOption("HOME", nullptr);
5839
0
#endif
5840
0
    if (pszHome != nullptr)
5841
0
    {
5842
0
        return CPLFormFilenameSafe(pszHome, ".gdal", nullptr);
5843
0
    }
5844
0
    else
5845
0
    {
5846
0
        const char *pszDir = CPLGetConfigOption("CPL_TMPDIR", nullptr);
5847
5848
0
        if (pszDir == nullptr)
5849
0
            pszDir = CPLGetConfigOption("TMPDIR", nullptr);
5850
5851
0
        if (pszDir == nullptr)
5852
0
            pszDir = CPLGetConfigOption("TEMP", nullptr);
5853
5854
0
        const char *pszUsername = CPLGetConfigOption("USERNAME", nullptr);
5855
0
        if (pszUsername == nullptr)
5856
0
            pszUsername = CPLGetConfigOption("USER", nullptr);
5857
5858
0
        if (pszDir != nullptr && pszUsername != nullptr)
5859
0
        {
5860
0
            return CPLFormFilenameSafe(
5861
0
                pszDir, CPLSPrintf(".gdal_%s", pszUsername), nullptr);
5862
0
        }
5863
0
    }
5864
0
    return std::string();
5865
0
}
5866
5867
/************************************************************************/
5868
/*                     GDALDoesFileOrDatasetExist()                     */
5869
/************************************************************************/
5870
5871
/** Return whether a file already exists.
5872
 */
5873
bool GDALDoesFileOrDatasetExist(const char *pszName, const char **ppszType,
5874
                                GDALDriver **ppDriver)
5875
0
{
5876
0
    {
5877
0
        CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
5878
0
        GDALDriverH hDriver = GDALIdentifyDriver(pszName, nullptr);
5879
0
        if (hDriver)
5880
0
        {
5881
0
            if (ppszType)
5882
0
                *ppszType = "Dataset";
5883
0
            if (ppDriver)
5884
0
                *ppDriver = GDALDriver::FromHandle(hDriver);
5885
0
            return true;
5886
0
        }
5887
0
    }
5888
5889
0
    VSIStatBufL sStat;
5890
0
    if (VSIStatL(pszName, &sStat) == 0)
5891
0
    {
5892
0
        if (ppszType)
5893
0
            *ppszType = VSI_ISDIR(sStat.st_mode) ? "Directory" : "File";
5894
0
        return true;
5895
0
    }
5896
5897
0
    return false;
5898
0
}
5899
5900
/************************************************************************/
5901
/*                       GDALGeoTransform::Apply                        */
5902
/************************************************************************/
5903
5904
bool GDALGeoTransform::Apply(const OGREnvelope &env,
5905
                             GDALRasterWindow &window) const
5906
0
{
5907
0
    if (!IsAxisAligned())
5908
0
    {
5909
0
        return false;
5910
0
    }
5911
5912
0
    double dfLeft, dfRight, dfTop, dfBottom;
5913
0
    Apply(env.MinX, env.MinY, &dfLeft, &dfBottom);
5914
0
    Apply(env.MaxX, env.MaxY, &dfRight, &dfTop);
5915
5916
0
    if (dfLeft > dfRight)
5917
0
        std::swap(dfLeft, dfRight);
5918
0
    if (dfTop > dfBottom)
5919
0
        std::swap(dfTop, dfBottom);
5920
5921
0
    constexpr double EPSILON = 1e-5;
5922
0
    dfTop = std::floor(dfTop + EPSILON);
5923
0
    dfBottom = std::ceil(dfBottom - EPSILON);
5924
0
    dfLeft = std::floor(dfLeft + EPSILON);
5925
0
    dfRight = std::ceil(dfRight - EPSILON);
5926
5927
0
    if (!(dfLeft >= INT_MIN && dfLeft <= INT_MAX &&
5928
0
          dfRight - dfLeft <= INT_MAX && dfTop >= INT_MIN && dfTop <= INT_MAX &&
5929
0
          dfBottom - dfLeft <= INT_MAX))
5930
0
    {
5931
0
        return false;
5932
0
    }
5933
0
    window.nXOff = static_cast<int>(dfLeft);
5934
0
    window.nXSize = static_cast<int>(dfRight - dfLeft);
5935
0
    window.nYOff = static_cast<int>(dfTop);
5936
0
    window.nYSize = static_cast<int>(dfBottom - dfTop);
5937
5938
0
    return true;
5939
0
}
5940
5941
bool GDALGeoTransform::Apply(const GDALRasterWindow &window,
5942
                             OGREnvelope &env) const
5943
0
{
5944
0
    if (!IsAxisAligned())
5945
0
    {
5946
0
        return false;
5947
0
    }
5948
5949
0
    double dfLeft = window.nXOff;
5950
0
    double dfRight = window.nXOff + window.nXSize;
5951
0
    double dfTop = window.nYOff;
5952
0
    double dfBottom = window.nYOff + window.nYSize;
5953
5954
0
    Apply(dfLeft, dfBottom, &env.MinX, &env.MinY);
5955
0
    Apply(dfRight, dfTop, &env.MaxX, &env.MaxY);
5956
5957
0
    if (env.MaxX < env.MinX)
5958
0
        std::swap(env.MinX, env.MaxX);
5959
0
    if (env.MaxY < env.MinY)
5960
0
        std::swap(env.MinY, env.MaxY);
5961
5962
0
    return true;
5963
0
}
5964
5965
/************************************************************************/
5966
/*                        GDALGeoTransform::Init                        */
5967
/************************************************************************/
5968
5969
bool GDALGeoTransform::Init(const char *pszText, const char *pszSep)
5970
0
{
5971
0
    CPLStringList aosGeoTransform(
5972
0
        CSLTokenizeString2(pszText, pszSep, CSLT_HONOURSTRINGS));
5973
0
    if (aosGeoTransform.size() != 6)
5974
0
    {
5975
0
        return false;
5976
0
    }
5977
5978
0
    for (int i = 0; i < 6; i++)
5979
0
    {
5980
0
        (*this)[i] = CPLAtof(aosGeoTransform[i]);
5981
0
    }
5982
5983
0
    return true;
5984
0
}
5985
5986
/************************************************************************/
5987
/*                      GDALGeoTransform::ToString                      */
5988
/************************************************************************/
5989
5990
std::string GDALGeoTransform::ToString(const char *pszSep) const
5991
0
{
5992
0
    return CPLSPrintf("%.17g%s%.17g%s%.17g%s%.17g%s%.17g%s%.17g", (*this)[0],
5993
0
                      pszSep, (*this)[1], pszSep, (*this)[2], pszSep,
5994
0
                      (*this)[3], pszSep, (*this)[4], pszSep, (*this)[5]);
5995
0
}