Coverage Report

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