Coverage Report

Created: 2025-11-15 08:43

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/raw/rrasterdataset.cpp
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  GDAL
4
 * Purpose:  Implements R Raster Format.
5
 * Author:   Even Rouault, <even dot rouault at spatialys dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2016, Even Rouault, <even dot rouault at spatialys dot com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#include "cpl_port.h"
14
15
#include "gdal_frmts.h"
16
#include "gdal_rat.h"
17
#include "gdal_priv.h"
18
19
#include "rawdataset.h"
20
#include "ogr_spatialref.h"
21
22
#include <algorithm>
23
#include <cmath>
24
#include <limits>
25
#include <memory>
26
27
/************************************************************************/
28
/* ==================================================================== */
29
/*                           RRASTERDataset                             */
30
/* ==================================================================== */
31
/************************************************************************/
32
33
class RRASTERDataset final : public RawDataset
34
{
35
    bool m_bHeaderDirty = false;
36
    CPLString m_osGriFilename{};
37
    bool m_bGeoTransformValid = false;
38
    GDALGeoTransform m_gt{};
39
    VSILFILE *m_fpImage = nullptr;
40
    OGRSpatialReference m_oSRS{};
41
    std::shared_ptr<GDALRasterAttributeTable> m_poRAT{};
42
    std::shared_ptr<GDALColorTable> m_poCT{};
43
    bool m_bNativeOrder = true;
44
    CPLString m_osCreator{};
45
    CPLString m_osCreated{};
46
    CPLString m_osBandOrder{};
47
    CPLString m_osLegend{};
48
    bool m_bInitRaster = false;
49
    bool m_bSignedByte = false;
50
51
    static bool ComputeSpacings(const CPLString &osBandOrder, int nCols,
52
                                int nRows, int l_nBands, GDALDataType eDT,
53
                                int &nPixelOffset, int &nLineOffset,
54
                                vsi_l_offset &nBandOffset);
55
    void RewriteHeader();
56
57
    CPL_DISALLOW_COPY_ASSIGN(RRASTERDataset)
58
59
    CPLErr Close() override;
60
61
  public:
62
    RRASTERDataset();
63
    ~RRASTERDataset() override;
64
65
    char **GetFileList() override;
66
67
    static GDALDataset *Open(GDALOpenInfo *);
68
    static int Identify(GDALOpenInfo *);
69
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
70
                               int nBandsIn, GDALDataType eType,
71
                               char **papszOptions);
72
    static GDALDataset *CreateCopy(const char *pszFilename,
73
                                   GDALDataset *poSrcDS, int bStrict,
74
                                   char **papszOptions,
75
                                   GDALProgressFunc pfnProgress,
76
                                   void *pProgressData);
77
78
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
79
    CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
80
    const OGRSpatialReference *GetSpatialRef() const override;
81
    CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
82
83
    CPLErr SetMetadata(char **papszMetadata,
84
                       const char *pszDomain = "") override;
85
    CPLErr SetMetadataItem(const char *pszName, const char *pszValue,
86
                           const char *pszDomain = "") override;
87
88
    void SetHeaderDirty()
89
0
    {
90
0
        m_bHeaderDirty = true;
91
0
    }
92
93
    void InitImageIfNeeded();
94
95
    inline bool IsSignedByte() const
96
0
    {
97
0
        return m_bSignedByte;
98
0
    }
99
};
100
101
/************************************************************************/
102
/* ==================================================================== */
103
/*                         RRASTERRasterBand                            */
104
/* ==================================================================== */
105
/************************************************************************/
106
107
class RRASTERRasterBand final : public RawRasterBand
108
{
109
    friend class RRASTERDataset;
110
111
    bool m_bHasNoDataValue = false;
112
    double m_dfNoDataValue = 0.0;
113
    double m_dfMin = std::numeric_limits<double>::infinity();
114
    double m_dfMax = -std::numeric_limits<double>::infinity();
115
    std::shared_ptr<GDALRasterAttributeTable> m_poRAT{};
116
    std::shared_ptr<GDALColorTable> m_poCT{};
117
118
    CPL_DISALLOW_COPY_ASSIGN(RRASTERRasterBand)
119
120
  public:
121
    RRASTERRasterBand(GDALDataset *poDS, int nBand, VSILFILE *fpRaw,
122
                      vsi_l_offset nImgOffset, int nPixelOffset,
123
                      int nLineOffset, GDALDataType eDataType,
124
                      int bNativeOrder);
125
126
    void SetMinMax(double dfMin, double dfMax);
127
    double GetMinimum(int *pbSuccess = nullptr) override;
128
    double GetMaximum(int *pbSuccess = nullptr) override;
129
130
    double GetNoDataValue(int *pbSuccess = nullptr) override;
131
    CPLErr SetNoDataValue(double dfNoData) override;
132
133
    GDALColorTable *GetColorTable() override;
134
    CPLErr SetColorTable(GDALColorTable *poNewCT) override;
135
136
    GDALRasterAttributeTable *GetDefaultRAT() override;
137
    CPLErr SetDefaultRAT(const GDALRasterAttributeTable *poRAT) override;
138
139
    void SetDescription(const char *pszDesc) override;
140
141
  protected:
142
    CPLErr IWriteBlock(int, int, void *) override;
143
    CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
144
                     GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace,
145
                     GDALRasterIOExtraArg *psExtraArg) override;
146
};
147
148
/************************************************************************/
149
/*                           RRASTERDataset()                           */
150
/************************************************************************/
151
152
RRASTERRasterBand::RRASTERRasterBand(GDALDataset *poDSIn, int nBandIn,
153
                                     VSILFILE *fpRawIn,
154
                                     vsi_l_offset nImgOffsetIn,
155
                                     int nPixelOffsetIn, int nLineOffsetIn,
156
                                     GDALDataType eDataTypeIn,
157
                                     int bNativeOrderIn)
158
174k
    : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
159
174k
                    nLineOffsetIn, eDataTypeIn, bNativeOrderIn,
160
174k
                    RawRasterBand::OwnFP::NO)
161
174k
{
162
174k
}
163
164
/************************************************************************/
165
/*                             SetMinMax()                              */
166
/************************************************************************/
167
168
void RRASTERRasterBand::SetMinMax(double dfMin, double dfMax)
169
149
{
170
149
    m_dfMin = dfMin;
171
149
    m_dfMax = dfMax;
172
149
}
173
174
/************************************************************************/
175
/*                            GetMinimum()                              */
176
/************************************************************************/
177
178
double RRASTERRasterBand::GetMinimum(int *pbSuccess)
179
0
{
180
0
    if (m_dfMin <= m_dfMax)
181
0
    {
182
0
        if (pbSuccess)
183
0
            *pbSuccess = TRUE;
184
0
        return m_dfMin;
185
0
    }
186
0
    return RawRasterBand::GetMinimum(pbSuccess);
187
0
}
188
189
/************************************************************************/
190
/*                            GetMaximum()                              */
191
/************************************************************************/
192
193
double RRASTERRasterBand::GetMaximum(int *pbSuccess)
194
0
{
195
0
    if (m_dfMin <= m_dfMax)
196
0
    {
197
0
        if (pbSuccess)
198
0
            *pbSuccess = TRUE;
199
0
        return m_dfMax;
200
0
    }
201
0
    return RawRasterBand::GetMaximum(pbSuccess);
202
0
}
203
204
/************************************************************************/
205
/*                           GetColorTable()                            */
206
/************************************************************************/
207
208
GDALColorTable *RRASTERRasterBand::GetColorTable()
209
0
{
210
0
    return m_poCT.get();
211
0
}
212
213
/************************************************************************/
214
/*                           SetColorTable()                            */
215
/************************************************************************/
216
217
CPLErr RRASTERRasterBand::SetColorTable(GDALColorTable *poNewCT)
218
0
{
219
0
    RRASTERDataset *poGDS = static_cast<RRASTERDataset *>(poDS);
220
0
    if (poGDS->GetAccess() != GA_Update)
221
0
        return CE_Failure;
222
223
0
    if (poNewCT == nullptr)
224
0
        m_poCT.reset();
225
0
    else
226
0
        m_poCT.reset(poNewCT->Clone());
227
228
0
    poGDS->SetHeaderDirty();
229
230
0
    return CE_None;
231
0
}
232
233
/************************************************************************/
234
/*                           GetDefaultRAT()                            */
235
/************************************************************************/
236
237
GDALRasterAttributeTable *RRASTERRasterBand::GetDefaultRAT()
238
0
{
239
0
    return m_poRAT.get();
240
0
}
241
242
/************************************************************************/
243
/*                            SetDefaultRAT()                           */
244
/************************************************************************/
245
246
CPLErr RRASTERRasterBand::SetDefaultRAT(const GDALRasterAttributeTable *poRAT)
247
0
{
248
0
    RRASTERDataset *poGDS = static_cast<RRASTERDataset *>(poDS);
249
0
    if (poGDS->GetAccess() != GA_Update)
250
0
        return CE_Failure;
251
252
0
    if (poRAT == nullptr)
253
0
        m_poRAT.reset();
254
0
    else
255
0
        m_poRAT.reset(poRAT->Clone());
256
257
0
    poGDS->SetHeaderDirty();
258
259
0
    return CE_None;
260
0
}
261
262
/************************************************************************/
263
/*                           SetDescription()                           */
264
/************************************************************************/
265
266
void RRASTERRasterBand::SetDescription(const char *pszDesc)
267
0
{
268
0
    RRASTERDataset *poGDS = static_cast<RRASTERDataset *>(poDS);
269
0
    if (poGDS->GetAccess() != GA_Update)
270
0
        return;
271
272
0
    GDALRasterBand::SetDescription(pszDesc);
273
274
0
    poGDS->SetHeaderDirty();
275
0
}
276
277
/************************************************************************/
278
/*                           GetNoDataValue()                           */
279
/************************************************************************/
280
281
double RRASTERRasterBand::GetNoDataValue(int *pbSuccess)
282
35.5k
{
283
35.5k
    if (pbSuccess)
284
35.4k
        *pbSuccess = m_bHasNoDataValue;
285
35.5k
    return m_dfNoDataValue;
286
35.5k
}
287
288
/************************************************************************/
289
/*                           SetNoDataValue()                           */
290
/************************************************************************/
291
292
CPLErr RRASTERRasterBand::SetNoDataValue(double dfNoData)
293
0
{
294
0
    RRASTERDataset *poGDS = static_cast<RRASTERDataset *>(poDS);
295
0
    if (poGDS->GetAccess() != GA_Update)
296
0
        return CE_Failure;
297
298
0
    m_bHasNoDataValue = true;
299
0
    m_dfNoDataValue = dfNoData;
300
0
    poGDS->SetHeaderDirty();
301
0
    return CE_None;
302
0
}
303
304
/************************************************************************/
305
/*                             GetMinMax()                              */
306
/************************************************************************/
307
308
template <class T>
309
static void GetMinMax(const T *buffer, int nBufXSize, int nBufYSize,
310
                      GSpacing nPixelSpace, GSpacing nLineSpace,
311
                      double dfNoDataValue, double &dfMin, double &dfMax)
312
0
{
313
0
    for (int iY = 0; iY < nBufYSize; iY++)
314
0
    {
315
0
        for (int iX = 0; iX < nBufXSize; iX++)
316
0
        {
317
0
            const double dfVal = buffer[iY * nLineSpace + iX * nPixelSpace];
318
0
            if (dfVal != dfNoDataValue && !std::isnan(dfVal))
319
0
            {
320
0
                dfMin = std::min(dfMin, dfVal);
321
0
                dfMax = std::max(dfMax, dfVal);
322
0
            }
323
0
        }
324
0
    }
325
0
}
Unexecuted instantiation: rrasterdataset.cpp:void GetMinMax<unsigned char>(unsigned char const*, int, int, long long, long long, double, double&, double&)
Unexecuted instantiation: rrasterdataset.cpp:void GetMinMax<signed char>(signed char const*, int, int, long long, long long, double, double&, double&)
Unexecuted instantiation: rrasterdataset.cpp:void GetMinMax<unsigned short>(unsigned short const*, int, int, long long, long long, double, double&, double&)
Unexecuted instantiation: rrasterdataset.cpp:void GetMinMax<short>(short const*, int, int, long long, long long, double, double&, double&)
Unexecuted instantiation: rrasterdataset.cpp:void GetMinMax<unsigned int>(unsigned int const*, int, int, long long, long long, double, double&, double&)
Unexecuted instantiation: rrasterdataset.cpp:void GetMinMax<int>(int const*, int, int, long long, long long, double, double&, double&)
Unexecuted instantiation: rrasterdataset.cpp:void GetMinMax<float>(float const*, int, int, long long, long long, double, double&, double&)
Unexecuted instantiation: rrasterdataset.cpp:void GetMinMax<double>(double const*, int, int, long long, long long, double, double&, double&)
326
327
static void GetMinMax(const void *pBuffer, GDALDataType eDT, int nBufXSize,
328
                      int nBufYSize, GSpacing nPixelSpace, GSpacing nLineSpace,
329
                      double dfNoDataValue, double &dfMin, double &dfMax)
330
0
{
331
0
    switch (eDT)
332
0
    {
333
0
        case GDT_Byte:
334
0
            GetMinMax(static_cast<const GByte *>(pBuffer), nBufXSize, nBufYSize,
335
0
                      nPixelSpace, nLineSpace, dfNoDataValue, dfMin, dfMax);
336
0
            break;
337
0
        case GDT_Int8:
338
0
            GetMinMax(static_cast<const GInt8 *>(pBuffer), nBufXSize, nBufYSize,
339
0
                      nPixelSpace, nLineSpace, dfNoDataValue, dfMin, dfMax);
340
0
            break;
341
0
        case GDT_UInt16:
342
0
            GetMinMax(static_cast<const GUInt16 *>(pBuffer), nBufXSize,
343
0
                      nBufYSize, nPixelSpace, nLineSpace, dfNoDataValue, dfMin,
344
0
                      dfMax);
345
0
            break;
346
0
        case GDT_Int16:
347
0
            GetMinMax(static_cast<const GInt16 *>(pBuffer), nBufXSize,
348
0
                      nBufYSize, nPixelSpace, nLineSpace, dfNoDataValue, dfMin,
349
0
                      dfMax);
350
0
            break;
351
0
        case GDT_UInt32:
352
0
            GetMinMax(static_cast<const GUInt32 *>(pBuffer), nBufXSize,
353
0
                      nBufYSize, nPixelSpace, nLineSpace, dfNoDataValue, dfMin,
354
0
                      dfMax);
355
0
            break;
356
0
        case GDT_Int32:
357
0
            GetMinMax(static_cast<const GInt32 *>(pBuffer), nBufXSize,
358
0
                      nBufYSize, nPixelSpace, nLineSpace, dfNoDataValue, dfMin,
359
0
                      dfMax);
360
0
            break;
361
0
        case GDT_Float32:
362
0
            GetMinMax(static_cast<const float *>(pBuffer), nBufXSize, nBufYSize,
363
0
                      nPixelSpace, nLineSpace, dfNoDataValue, dfMin, dfMax);
364
0
            break;
365
0
        case GDT_Float64:
366
0
            GetMinMax(static_cast<const double *>(pBuffer), nBufXSize,
367
0
                      nBufYSize, nPixelSpace, nLineSpace, dfNoDataValue, dfMin,
368
0
                      dfMax);
369
0
            break;
370
0
        default:
371
0
            CPLAssert(false);
372
0
            break;
373
0
    }
374
0
}
375
376
/************************************************************************/
377
/*                            IWriteBlock()                             */
378
/************************************************************************/
379
380
CPLErr RRASTERRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
381
                                      void *pImage)
382
0
{
383
0
    RRASTERDataset *poGDS = static_cast<RRASTERDataset *>(poDS);
384
0
    poGDS->InitImageIfNeeded();
385
386
0
    int bGotNoDataValue = false;
387
0
    double dfNoDataValue = GetNoDataValue(&bGotNoDataValue);
388
0
    if (!bGotNoDataValue)
389
0
        dfNoDataValue = std::numeric_limits<double>::quiet_NaN();
390
0
    GetMinMax(pImage, poGDS->IsSignedByte() ? GDT_Int8 : eDataType, nBlockXSize,
391
0
              nBlockYSize, 1, nBlockXSize, dfNoDataValue, m_dfMin, m_dfMax);
392
0
    return RawRasterBand::IWriteBlock(nBlockXOff, nBlockYOff, pImage);
393
0
}
394
395
/************************************************************************/
396
/*                             IRasterIO()                              */
397
/************************************************************************/
398
399
CPLErr RRASTERRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
400
                                    int nXSize, int nYSize, void *pData,
401
                                    int nBufXSize, int nBufYSize,
402
                                    GDALDataType eBufType, GSpacing nPixelSpace,
403
                                    GSpacing nLineSpace,
404
                                    GDALRasterIOExtraArg *psExtraArg)
405
406
963k
{
407
963k
    if (eRWFlag == GF_Write)
408
0
    {
409
0
        RRASTERDataset *poGDS = static_cast<RRASTERDataset *>(poDS);
410
0
        poGDS->InitImageIfNeeded();
411
412
0
        const int nDTSize = std::max(1, GDALGetDataTypeSizeBytes(eDataType));
413
0
        int bGotNoDataValue = false;
414
0
        double dfNoDataValue = GetNoDataValue(&bGotNoDataValue);
415
0
        if (!bGotNoDataValue)
416
0
            dfNoDataValue = std::numeric_limits<double>::quiet_NaN();
417
0
        GetMinMax(pData, poGDS->IsSignedByte() ? GDT_Int8 : eDataType,
418
0
                  nBufXSize, nBufYSize, nPixelSpace / nDTSize,
419
0
                  nLineSpace / nDTSize, dfNoDataValue, m_dfMin, m_dfMax);
420
0
    }
421
963k
    return RawRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
422
963k
                                    pData, nBufXSize, nBufYSize, eBufType,
423
963k
                                    nPixelSpace, nLineSpace, psExtraArg);
424
963k
}
425
426
/************************************************************************/
427
/*                           RRASTERDataset()                           */
428
/************************************************************************/
429
430
RRASTERDataset::RRASTERDataset()
431
13.0k
{
432
13.0k
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
433
13.0k
}
434
435
/************************************************************************/
436
/*                          ~RRASTERDataset()                           */
437
/************************************************************************/
438
439
RRASTERDataset::~RRASTERDataset()
440
441
13.0k
{
442
13.0k
    RRASTERDataset::Close();
443
13.0k
}
444
445
/************************************************************************/
446
/*                              Close()                                 */
447
/************************************************************************/
448
449
CPLErr RRASTERDataset::Close()
450
24.8k
{
451
24.8k
    CPLErr eErr = CE_None;
452
24.8k
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
453
13.0k
    {
454
13.0k
        if (m_fpImage)
455
11.8k
        {
456
11.8k
            InitImageIfNeeded();
457
11.8k
            if (RRASTERDataset::FlushCache(true) != CE_None)
458
0
                eErr = CE_Failure;
459
11.8k
            if (VSIFCloseL(m_fpImage) != CE_None)
460
0
                eErr = CE_Failure;
461
11.8k
        }
462
13.0k
        if (m_bHeaderDirty)
463
0
            RewriteHeader();
464
465
13.0k
        if (GDALPamDataset::Close() != CE_None)
466
0
            eErr = CE_Failure;
467
13.0k
    }
468
24.8k
    return eErr;
469
24.8k
}
470
471
/************************************************************************/
472
/*                        InitImageIfNeeded()                           */
473
/************************************************************************/
474
475
void RRASTERDataset::InitImageIfNeeded()
476
11.8k
{
477
11.8k
    CPLAssert(m_fpImage);
478
11.8k
    if (!m_bInitRaster)
479
11.8k
        return;
480
0
    m_bInitRaster = false;
481
0
    int bGotNoDataValue = false;
482
0
    double dfNoDataValue = GetRasterBand(1)->GetNoDataValue(&bGotNoDataValue);
483
0
    const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
484
0
    const int nDTSize = GDALGetDataTypeSizeBytes(eDT);
485
0
    if (dfNoDataValue == 0.0)
486
0
    {
487
0
        VSIFTruncateL(m_fpImage, static_cast<vsi_l_offset>(nRasterXSize) *
488
0
                                     nRasterYSize * nBands * nDTSize);
489
0
    }
490
0
    else
491
0
    {
492
0
        GByte abyNoDataValue[16];
493
0
        GDALCopyWords(&dfNoDataValue, GDT_Float64, 0, abyNoDataValue, eDT, 0,
494
0
                      1);
495
0
        for (GUIntBig i = 0;
496
0
             i < static_cast<GUIntBig>(nRasterXSize) * nRasterYSize * nBands;
497
0
             i++)
498
0
        {
499
0
            VSIFWriteL(abyNoDataValue, 1, nDTSize, m_fpImage);
500
0
        }
501
0
    }
502
0
}
503
504
/************************************************************************/
505
/*                           RewriteHeader()                            */
506
/************************************************************************/
507
508
void RRASTERDataset::RewriteHeader()
509
0
{
510
0
    VSILFILE *fp = VSIFOpenL(GetDescription(), "wb");
511
0
    if (!fp)
512
0
        return;
513
514
0
    VSIFPrintfL(fp, "[general]\n");
515
0
    if (!m_osCreator.empty())
516
0
        VSIFPrintfL(fp, "creator=%s\n", m_osCreator.c_str());
517
0
    if (!m_osCreated.empty())
518
0
        VSIFPrintfL(fp, "created=%s\n", m_osCreated.c_str());
519
520
0
    VSIFPrintfL(fp, "[data]\n");
521
0
    GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
522
0
    VSIFPrintfL(fp, "datatype=%s\n",
523
0
                (eDT == GDT_Int8 || m_bSignedByte) ? "INT1S"
524
0
                : (eDT == GDT_Byte)                ? "INT1U"
525
0
                : (eDT == GDT_UInt16)              ? "INT2U"
526
0
                : (eDT == GDT_UInt32)              ? "INT4U"
527
0
                : (eDT == GDT_Int16)               ? "INT2S"
528
0
                : (eDT == GDT_Int32)               ? "INT4S"
529
0
                : (eDT == GDT_Float32)             ? "FLT4S"
530
0
                                                   :
531
0
                                       /*(eDT == GDT_Float64) ?*/ "FLT8S");
532
533
0
    int bGotNoDataValue = false;
534
0
    double dfNoDataValue = GetRasterBand(1)->GetNoDataValue(&bGotNoDataValue);
535
0
    if (bGotNoDataValue)
536
0
        VSIFPrintfL(fp, "nodatavalue=%.17g\n", dfNoDataValue);
537
538
0
    VSIFPrintfL(fp, "byteorder=%s\n",
539
0
                (m_bNativeOrder ^ CPL_IS_LSB) ? "big" : "little");
540
0
    VSIFPrintfL(fp, "nbands=%d\n", nBands);
541
0
    if (nBands > 1)
542
0
        VSIFPrintfL(fp, "bandorder=%s\n", m_osBandOrder.c_str());
543
0
    CPLString osMinValue, osMaxValue;
544
0
    for (int i = 1; i <= nBands; i++)
545
0
    {
546
0
        RRASTERRasterBand *poBand =
547
0
            static_cast<RRASTERRasterBand *>(GetRasterBand(i));
548
0
        if (i > 1)
549
0
        {
550
0
            osMinValue += ":";
551
0
            osMaxValue += ":";
552
0
        }
553
0
        if (poBand->m_dfMin > poBand->m_dfMax)
554
0
        {
555
0
            osMinValue.clear();
556
0
            break;
557
0
        }
558
0
        osMinValue += CPLSPrintf("%.17g", poBand->m_dfMin);
559
0
        osMaxValue += CPLSPrintf("%.17g", poBand->m_dfMax);
560
0
    }
561
0
    if (!osMinValue.empty())
562
0
    {
563
0
        VSIFPrintfL(fp, "minvalue=%s\n", osMinValue.c_str());
564
0
        VSIFPrintfL(fp, "maxvalue=%s\n", osMaxValue.c_str());
565
0
    }
566
567
0
    GDALColorTable *poCT = GetRasterBand(1)->GetColorTable();
568
0
    GDALRasterAttributeTable *poRAT = GetRasterBand(1)->GetDefaultRAT();
569
0
    if (poCT == nullptr && poRAT == nullptr)
570
0
    {
571
0
        VSIFPrintfL(fp, "categorical=FALSE\n");
572
0
    }
573
0
    else
574
0
    {
575
0
        VSIFPrintfL(fp, "categorical=TRUE\n");
576
0
        if (poCT && poRAT)
577
0
        {
578
0
            CPLError(CE_Warning, CPLE_AppDefined,
579
0
                     "Both color table and raster attribute table defined. "
580
0
                     "Writing only the later");
581
0
        }
582
0
        if (poRAT)
583
0
        {
584
0
            CPLString osRatNames;
585
0
            CPLString osRatTypes;
586
0
            for (int i = 0; i < poRAT->GetColumnCount(); i++)
587
0
            {
588
0
                if (!osRatNames.empty())
589
0
                {
590
0
                    osRatNames += ":";
591
0
                    osRatTypes += ":";
592
0
                }
593
0
                osRatNames +=
594
0
                    CPLString(poRAT->GetNameOfCol(i)).replaceAll(':', '.');
595
0
                GDALRATFieldType eColType = poRAT->GetTypeOfCol(i);
596
0
                if (eColType == GFT_Integer)
597
0
                    osRatTypes += "integer";
598
0
                else if (eColType == GFT_Real)
599
0
                    osRatTypes += "numeric";
600
0
                else
601
0
                    osRatTypes += "character";
602
0
            }
603
0
            VSIFPrintfL(fp, "ratnames=%s\n", osRatNames.c_str());
604
0
            VSIFPrintfL(fp, "rattypes=%s\n", osRatTypes.c_str());
605
0
            CPLString osRatValues;
606
0
            for (int i = 0; i < poRAT->GetColumnCount(); i++)
607
0
            {
608
0
                GDALRATFieldType eColType = poRAT->GetTypeOfCol(i);
609
0
                for (int j = 0; j < poRAT->GetRowCount(); j++)
610
0
                {
611
0
                    if (i != 0 || j != 0)
612
0
                        osRatValues += ":";
613
0
                    if (eColType == GFT_Integer)
614
0
                    {
615
0
                        osRatValues +=
616
0
                            CPLSPrintf("%d", poRAT->GetValueAsInt(j, i));
617
0
                    }
618
0
                    else if (eColType == GFT_Real)
619
0
                    {
620
0
                        osRatValues +=
621
0
                            CPLSPrintf("%.17g", poRAT->GetValueAsDouble(j, i));
622
0
                    }
623
0
                    else
624
0
                    {
625
0
                        const char *pszVal = poRAT->GetValueAsString(j, i);
626
0
                        if (pszVal)
627
0
                        {
628
0
                            osRatValues +=
629
0
                                CPLString(pszVal).replaceAll(':', '.');
630
0
                        }
631
0
                    }
632
0
                }
633
0
            }
634
0
            VSIFPrintfL(fp, "ratvalues=%s\n", osRatValues.c_str());
635
0
        }
636
0
        else
637
0
        {
638
0
            bool bNeedsAlpha = false;
639
0
            for (int i = 0; i < poCT->GetColorEntryCount(); i++)
640
0
            {
641
0
                if (poCT->GetColorEntry(i)->c4 != 255)
642
0
                {
643
0
                    bNeedsAlpha = true;
644
0
                    break;
645
0
                }
646
0
            }
647
0
            if (!bNeedsAlpha)
648
0
            {
649
0
                VSIFPrintfL(fp, "ratnames=%s\n", "ID:red:green:blue");
650
0
                VSIFPrintfL(fp, "rattypes=%s\n",
651
0
                            "integer:integer:integer:integer");
652
0
            }
653
0
            else
654
0
            {
655
0
                VSIFPrintfL(fp, "ratnames=%s\n", "ID:red:green:blue:alpha");
656
0
                VSIFPrintfL(fp, "rattypes=%s\n",
657
0
                            "integer:integer:integer:integer:integer");
658
0
            }
659
660
0
            CPLString osRatID;
661
0
            CPLString osRatR;
662
0
            CPLString osRatG;
663
0
            CPLString osRatB;
664
0
            CPLString osRatA;
665
0
            for (int i = 0; i < poCT->GetColorEntryCount(); i++)
666
0
            {
667
0
                const GDALColorEntry *psEntry = poCT->GetColorEntry(i);
668
0
                if (i > 0)
669
0
                {
670
0
                    osRatID += ":";
671
0
                    osRatR += ":";
672
0
                    osRatG += ":";
673
0
                    osRatB += ":";
674
0
                    osRatA += ":";
675
0
                }
676
0
                osRatID += CPLSPrintf("%d", i);
677
0
                osRatR += CPLSPrintf("%d", psEntry->c1);
678
0
                osRatG += CPLSPrintf("%d", psEntry->c2);
679
0
                osRatB += CPLSPrintf("%d", psEntry->c3);
680
0
                osRatA += CPLSPrintf("%d", psEntry->c4);
681
0
            }
682
0
            if (!bNeedsAlpha)
683
0
            {
684
0
                VSIFPrintfL(fp, "ratvalues=%s:%s:%s:%s\n", osRatID.c_str(),
685
0
                            osRatR.c_str(), osRatG.c_str(), osRatB.c_str());
686
0
            }
687
0
            else
688
0
            {
689
0
                VSIFPrintfL(fp, "ratvalues=%s:%s:%s:%s:%s\n", osRatID.c_str(),
690
0
                            osRatR.c_str(), osRatG.c_str(), osRatB.c_str(),
691
0
                            osRatA.c_str());
692
0
            }
693
0
        }
694
0
    }
695
696
0
    if (!m_osLegend.empty())
697
0
        VSIFPrintfL(fp, "[legend]\n%s", m_osLegend.c_str());
698
699
0
    CPLString osLayerName;
700
0
    bool bGotSignificantBandDesc = false;
701
0
    for (int i = 1; i <= nBands; i++)
702
0
    {
703
0
        GDALRasterBand *poBand = GetRasterBand(i);
704
0
        const char *pszDesc = poBand->GetDescription();
705
0
        if (EQUAL(pszDesc, ""))
706
0
        {
707
0
            GDALColorInterp eInterp = poBand->GetColorInterpretation();
708
0
            if (eInterp == GCI_RedBand)
709
0
            {
710
0
                bGotSignificantBandDesc = true;
711
0
                pszDesc = "red";
712
0
            }
713
0
            else if (eInterp == GCI_GreenBand)
714
0
            {
715
0
                bGotSignificantBandDesc = true;
716
0
                pszDesc = "green";
717
0
            }
718
0
            else if (eInterp == GCI_BlueBand)
719
0
            {
720
0
                bGotSignificantBandDesc = true;
721
0
                pszDesc = "blue";
722
0
            }
723
0
            else if (eInterp == GCI_AlphaBand)
724
0
            {
725
0
                bGotSignificantBandDesc = true;
726
0
                pszDesc = "alpha";
727
0
            }
728
0
            else
729
0
            {
730
0
                pszDesc = CPLSPrintf("Band%d", i);
731
0
            }
732
0
        }
733
0
        else
734
0
        {
735
0
            bGotSignificantBandDesc = true;
736
0
        }
737
0
        if (i > 1)
738
0
            osLayerName += ":";
739
0
        osLayerName += CPLString(pszDesc).replaceAll(':', '.');
740
0
    }
741
0
    if (bGotSignificantBandDesc)
742
0
    {
743
0
        VSIFPrintfL(fp, "[description]\n");
744
0
        VSIFPrintfL(fp, "layername=%s\n", osLayerName.c_str());
745
0
    }
746
747
    // Put the [georeference] section at end. Otherwise the wkt= entry
748
    // could cause GDAL < 3.5 to be unable to open the file due to the
749
    // Identify() function not using enough bytes
750
0
    VSIFPrintfL(fp, "[georeference]\n");
751
0
    VSIFPrintfL(fp, "nrows=%d\n", nRasterYSize);
752
0
    VSIFPrintfL(fp, "ncols=%d\n", nRasterXSize);
753
754
0
    VSIFPrintfL(fp, "xmin=%.17g\n", m_gt[0]);
755
0
    VSIFPrintfL(fp, "ymin=%.17g\n", m_gt[3] + nRasterYSize * m_gt[5]);
756
0
    VSIFPrintfL(fp, "xmax=%.17g\n", m_gt[0] + nRasterXSize * m_gt[1]);
757
0
    VSIFPrintfL(fp, "ymax=%.17g\n", m_gt[3]);
758
759
0
    if (!m_oSRS.IsEmpty())
760
0
    {
761
0
        char *pszProj4 = nullptr;
762
0
        m_oSRS.exportToProj4(&pszProj4);
763
0
        if (pszProj4)
764
0
        {
765
0
            VSIFPrintfL(fp, "projection=%s\n", pszProj4);
766
0
            VSIFree(pszProj4);
767
0
        }
768
0
        char *pszWKT = nullptr;
769
0
        const char *const apszOptions[] = {"FORMAT=WKT2_2019", nullptr};
770
0
        m_oSRS.exportToWkt(&pszWKT, apszOptions);
771
0
        if (pszWKT)
772
0
        {
773
0
            VSIFPrintfL(fp, "wkt=%s\n", pszWKT);
774
0
            VSIFree(pszWKT);
775
0
        }
776
0
    }
777
778
0
    VSIFCloseL(fp);
779
0
}
780
781
/************************************************************************/
782
/*                            GetFileList()                             */
783
/************************************************************************/
784
785
char **RRASTERDataset::GetFileList()
786
787
23.6k
{
788
23.6k
    char **papszFileList = RawDataset::GetFileList();
789
790
23.6k
    papszFileList = CSLAddString(papszFileList, m_osGriFilename);
791
792
23.6k
    return papszFileList;
793
23.6k
}
794
795
/************************************************************************/
796
/*                          GetGeoTransform()                           */
797
/************************************************************************/
798
799
CPLErr RRASTERDataset::GetGeoTransform(GDALGeoTransform &gt) const
800
11.8k
{
801
11.8k
    if (m_bGeoTransformValid)
802
11.8k
    {
803
11.8k
        gt = m_gt;
804
11.8k
        return CE_None;
805
11.8k
    }
806
0
    return CE_Failure;
807
11.8k
}
808
809
/************************************************************************/
810
/*                          SetGeoTransform()                           */
811
/************************************************************************/
812
813
CPLErr RRASTERDataset::SetGeoTransform(const GDALGeoTransform &gt)
814
815
0
{
816
0
    if (GetAccess() != GA_Update)
817
0
    {
818
0
        CPLError(CE_Failure, CPLE_NotSupported,
819
0
                 "Cannot set geotransform on a read-only dataset");
820
0
        return CE_Failure;
821
0
    }
822
823
    // We only support non-rotated images with info in the .HDR file.
824
0
    if (gt[2] != 0.0 || gt[4] != 0.0)
825
0
    {
826
0
        CPLError(CE_Warning, CPLE_NotSupported,
827
0
                 "Rotated / skewed images not supported");
828
0
        return GDALPamDataset::SetGeoTransform(gt);
829
0
    }
830
831
    // Record new geotransform.
832
0
    m_bGeoTransformValid = true;
833
0
    m_gt = gt;
834
0
    SetHeaderDirty();
835
836
0
    return CE_None;
837
0
}
838
839
/************************************************************************/
840
/*                           GetSpatialRef()                            */
841
/************************************************************************/
842
843
const OGRSpatialReference *RRASTERDataset::GetSpatialRef() const
844
11.8k
{
845
11.8k
    return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
846
11.8k
}
847
848
/************************************************************************/
849
/*                           SetSpatialRef()                            */
850
/************************************************************************/
851
852
CPLErr RRASTERDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
853
0
{
854
0
    if (GetAccess() != GA_Update)
855
0
    {
856
0
        CPLError(CE_Failure, CPLE_NotSupported,
857
0
                 "Cannot set projection on a read-only dataset");
858
0
        return CE_Failure;
859
0
    }
860
861
0
    m_oSRS.Clear();
862
0
    if (poSRS)
863
0
        m_oSRS = *poSRS;
864
0
    SetHeaderDirty();
865
866
0
    return CE_None;
867
0
}
868
869
/************************************************************************/
870
/*                            SetMetadata()                             */
871
/************************************************************************/
872
873
CPLErr RRASTERDataset::SetMetadata(char **papszMetadata, const char *pszDomain)
874
0
{
875
0
    if (pszDomain == nullptr || EQUAL(pszDomain, ""))
876
0
    {
877
0
        m_osCreator = CSLFetchNameValueDef(papszMetadata, "CREATOR", "");
878
0
        m_osCreated = CSLFetchNameValueDef(papszMetadata, "CREATED", "");
879
0
        SetHeaderDirty();
880
0
    }
881
0
    return GDALDataset::SetMetadata(papszMetadata, pszDomain);
882
0
}
883
884
/************************************************************************/
885
/*                          SetMetadataItem()                           */
886
/************************************************************************/
887
888
CPLErr RRASTERDataset::SetMetadataItem(const char *pszName,
889
                                       const char *pszValue,
890
                                       const char *pszDomain)
891
0
{
892
0
    if (pszDomain == nullptr || EQUAL(pszDomain, ""))
893
0
    {
894
0
        if (EQUAL(pszName, "CREATOR"))
895
0
        {
896
0
            m_osCreator = pszValue ? pszValue : "";
897
0
            SetHeaderDirty();
898
0
        }
899
0
        if (EQUAL(pszName, "CREATED"))
900
0
        {
901
0
            m_osCreated = pszValue ? pszValue : "";
902
0
            SetHeaderDirty();
903
0
        }
904
0
    }
905
0
    return GDALDataset::SetMetadataItem(pszName, pszValue, pszDomain);
906
0
}
907
908
/************************************************************************/
909
/*                            Identify()                                */
910
/************************************************************************/
911
912
int RRASTERDataset::Identify(GDALOpenInfo *poOpenInfo)
913
914
651k
{
915
651k
    if (poOpenInfo->nHeaderBytes < 40 || poOpenInfo->fpL == nullptr ||
916
93.0k
        !poOpenInfo->IsExtensionEqualToCI("grd"))
917
625k
    {
918
625k
        return FALSE;
919
625k
    }
920
921
    // We need to ingest enough bytes as the wkt= entry can be quite large
922
26.2k
    if (poOpenInfo->nHeaderBytes <= 1024)
923
18.2k
        poOpenInfo->TryToIngest(4096);
924
925
26.2k
    if (strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
926
26.2k
               "ncols") == nullptr ||
927
26.0k
        strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
928
26.0k
               "nrows") == nullptr ||
929
26.0k
        strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
930
26.0k
               "xmin") == nullptr ||
931
26.0k
        strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
932
26.0k
               "ymin") == nullptr ||
933
26.0k
        strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
934
26.0k
               "xmax") == nullptr ||
935
26.0k
        strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
936
26.0k
               "ymax") == nullptr ||
937
26.0k
        strstr(reinterpret_cast<const char *>(poOpenInfo->pabyHeader),
938
26.0k
               "datatype") == nullptr)
939
167
    {
940
167
        return FALSE;
941
167
    }
942
943
26.0k
    return TRUE;
944
26.2k
}
945
946
/************************************************************************/
947
/*                          ComputeSpacing()                            */
948
/************************************************************************/
949
950
bool RRASTERDataset::ComputeSpacings(const CPLString &osBandOrder, int nCols,
951
                                     int nRows, int l_nBands, GDALDataType eDT,
952
                                     int &nPixelOffset, int &nLineOffset,
953
                                     vsi_l_offset &nBandOffset)
954
12.2k
{
955
12.2k
    nPixelOffset = 0;
956
12.2k
    nLineOffset = 0;
957
12.2k
    nBandOffset = 0;
958
12.2k
    const int nPixelSize = GDALGetDataTypeSizeBytes(eDT);
959
12.2k
    if (l_nBands == 1 || EQUAL(osBandOrder, "BIL"))
960
11.6k
    {
961
11.6k
        nPixelOffset = nPixelSize;
962
11.6k
        if (l_nBands != 0 && nPixelSize != 0 &&
963
11.6k
            nCols > INT_MAX / (l_nBands * nPixelSize))
964
2
        {
965
2
            CPLError(CE_Failure, CPLE_AppDefined, "Too many columns");
966
2
            return false;
967
2
        }
968
11.6k
        nLineOffset = nPixelSize * nCols * l_nBands;
969
11.6k
        nBandOffset = static_cast<vsi_l_offset>(nPixelSize) * nCols;
970
11.6k
    }
971
628
    else if (EQUAL(osBandOrder, "BIP"))
972
523
    {
973
523
        if (l_nBands != 0 && nPixelSize != 0 &&
974
523
            nCols > INT_MAX / (l_nBands * nPixelSize))
975
4
        {
976
4
            CPLError(CE_Failure, CPLE_AppDefined, "Too many columns");
977
4
            return false;
978
4
        }
979
519
        nPixelOffset = nPixelSize * l_nBands;
980
519
        nLineOffset = nPixelSize * nCols * l_nBands;
981
519
        nBandOffset = nPixelSize;
982
519
    }
983
105
    else if (EQUAL(osBandOrder, "BSQ"))
984
76
    {
985
76
        if (nPixelSize != 0 && nCols > INT_MAX / nPixelSize)
986
0
        {
987
0
            CPLError(CE_Failure, CPLE_AppDefined, "Too many columns");
988
0
            return false;
989
0
        }
990
76
        nPixelOffset = nPixelSize;
991
76
        nLineOffset = nPixelSize * nCols;
992
76
        nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
993
76
    }
994
29
    else if (l_nBands > 1)
995
29
    {
996
29
        CPLError(CE_Failure, CPLE_AppDefined, "Unknown bandorder");
997
29
        return false;
998
29
    }
999
12.2k
    return true;
1000
12.2k
}
1001
1002
/************************************************************************/
1003
/*                            CastToFloat()                             */
1004
/************************************************************************/
1005
1006
static float CastToFloat(double dfVal)
1007
2.63k
{
1008
2.63k
    if (std::isinf(dfVal) || std::isnan(dfVal) ||
1009
2.22k
        (dfVal >= -std::numeric_limits<float>::max() &&
1010
2.21k
         dfVal <= std::numeric_limits<float>::max()))
1011
1.61k
    {
1012
1.61k
        return static_cast<float>(dfVal);
1013
1.61k
    }
1014
1.01k
    return std::numeric_limits<float>::quiet_NaN();
1015
2.63k
}
1016
1017
/************************************************************************/
1018
/*                                Open()                                */
1019
/************************************************************************/
1020
1021
GDALDataset *RRASTERDataset::Open(GDALOpenInfo *poOpenInfo)
1022
13.0k
{
1023
13.0k
    if (!Identify(poOpenInfo))
1024
0
        return nullptr;
1025
1026
13.0k
    auto poDS = std::make_unique<RRASTERDataset>();
1027
1028
13.0k
    const char *pszLine = nullptr;
1029
13.0k
    int nRows = 0;
1030
13.0k
    int nCols = 0;
1031
13.0k
    double dfXMin = 0.0;
1032
13.0k
    double dfYMin = 0.0;
1033
13.0k
    double dfXMax = 0.0;
1034
13.0k
    double dfYMax = 0.0;
1035
13.0k
    int l_nBands = 1;
1036
13.0k
    CPLString osDataType;
1037
13.0k
    CPLString osProjection;
1038
13.0k
    CPLString osWkt;
1039
13.0k
    CPLString osByteOrder;
1040
13.0k
    CPLString osNoDataValue("NA");
1041
13.0k
    CPLString osMinValue;
1042
13.0k
    CPLString osMaxValue;
1043
13.0k
    CPLString osLayerName;
1044
13.0k
    CPLString osRatNames;
1045
13.0k
    CPLString osRatTypes;
1046
13.0k
    CPLString osRatValues;
1047
13.0k
    bool bInLegend = false;
1048
13.0k
    VSIRewindL(poOpenInfo->fpL);
1049
823k
    while ((pszLine = CPLReadLine2L(poOpenInfo->fpL, 1024 * 1024, nullptr)) !=
1050
823k
           nullptr)
1051
810k
    {
1052
810k
        if (pszLine[0] == '[')
1053
18.1k
        {
1054
18.1k
            bInLegend = EQUAL(pszLine, "[legend]");
1055
18.1k
            continue;
1056
18.1k
        }
1057
792k
        if (bInLegend)
1058
10.7k
        {
1059
10.7k
            poDS->m_osLegend += pszLine;
1060
10.7k
            poDS->m_osLegend += "\n";
1061
10.7k
        }
1062
792k
        char *pszKey = nullptr;
1063
792k
        const char *pszValue = CPLParseNameValue(pszLine, &pszKey);
1064
792k
        if (pszKey && pszValue)
1065
489k
        {
1066
489k
            if (EQUAL(pszKey, "creator"))
1067
4.91k
                poDS->m_osCreator = pszValue;
1068
489k
            if (EQUAL(pszKey, "created"))
1069
4.22k
                poDS->m_osCreated = pszValue;
1070
484k
            else if (EQUAL(pszKey, "ncols"))
1071
35.3k
                nCols = atoi(pszValue);
1072
449k
            else if (EQUAL(pszKey, "nrows"))
1073
33.7k
                nRows = atoi(pszValue);
1074
415k
            else if (EQUAL(pszKey, "xmin"))
1075
23.9k
                dfXMin = CPLAtof(pszValue);
1076
391k
            else if (EQUAL(pszKey, "ymin"))
1077
24.0k
                dfYMin = CPLAtof(pszValue);
1078
367k
            else if (EQUAL(pszKey, "xmax"))
1079
33.0k
                dfXMax = CPLAtof(pszValue);
1080
334k
            else if (EQUAL(pszKey, "ymax"))
1081
6.29k
                dfYMax = CPLAtof(pszValue);
1082
328k
            else if (EQUAL(pszKey, "projection"))
1083
25.2k
                osProjection = pszValue;
1084
303k
            else if (EQUAL(pszKey, "wkt"))
1085
3.03k
                osWkt = pszValue;
1086
300k
            else if (EQUAL(pszKey, "nbands"))
1087
4.22k
                l_nBands = atoi(pszValue);
1088
295k
            else if (EQUAL(pszKey, "bandorder"))
1089
4.23k
                poDS->m_osBandOrder = pszValue;
1090
291k
            else if (EQUAL(pszKey, "datatype"))
1091
31.7k
                osDataType = pszValue;
1092
259k
            else if (EQUAL(pszKey, "byteorder"))
1093
5.35k
                osByteOrder = pszValue;
1094
254k
            else if (EQUAL(pszKey, "nodatavalue"))
1095
1.47k
                osNoDataValue = pszValue;
1096
253k
            else if (EQUAL(pszKey, "minvalue"))
1097
1.09k
                osMinValue = pszValue;
1098
251k
            else if (EQUAL(pszKey, "maxvalue"))
1099
2.09k
                osMaxValue = pszValue;
1100
249k
            else if (EQUAL(pszKey, "ratnames"))
1101
10.4k
                osRatNames = pszValue;
1102
239k
            else if (EQUAL(pszKey, "rattypes"))
1103
8.32k
                osRatTypes = pszValue;
1104
231k
            else if (EQUAL(pszKey, "ratvalues"))
1105
5.31k
                osRatValues = pszValue;
1106
225k
            else if (EQUAL(pszKey, "layername"))
1107
1.65k
                osLayerName = pszValue;
1108
489k
        }
1109
792k
        CPLFree(pszKey);
1110
792k
    }
1111
13.0k
    if (!GDALCheckDatasetDimensions(nCols, nRows))
1112
304
        return nullptr;
1113
12.7k
    if (!GDALCheckBandCount(l_nBands, FALSE))
1114
17
        return nullptr;
1115
1116
12.7k
    GDALDataType eDT = GDT_Unknown;
1117
12.7k
    if (EQUAL(osDataType, "LOG1S"))
1118
0
        eDT = GDT_Byte;  // mapping TBC
1119
12.7k
    else if (EQUAL(osDataType, "INT1S"))
1120
95
        eDT = GDT_Int8;
1121
12.6k
    else if (EQUAL(osDataType, "INT2S"))
1122
62
        eDT = GDT_Int16;
1123
12.5k
    else if (EQUAL(osDataType, "INT4S"))
1124
29
        eDT = GDT_Int32;
1125
    // else if( EQUAL(osDataType, "INT8S") )
1126
    //     eDT = GDT_UInt64; // unhandled
1127
12.5k
    else if (EQUAL(osDataType, "INT1U"))
1128
4.92k
        eDT = GDT_Byte;
1129
7.59k
    else if (EQUAL(osDataType, "INT2U"))
1130
4.53k
        eDT = GDT_UInt16;
1131
3.05k
    else if (EQUAL(osDataType, "INT4U"))  // Not documented
1132
1.55k
        eDT = GDT_UInt32;
1133
1.50k
    else if (EQUAL(osDataType, "FLT4S"))
1134
152
        eDT = GDT_Float32;
1135
1.35k
    else if (EQUAL(osDataType, "FLT8S"))
1136
935
        eDT = GDT_Float64;
1137
416
    else
1138
416
    {
1139
416
        CPLError(CE_Failure, CPLE_AppDefined, "Unhandled datatype=%s",
1140
416
                 osDataType.c_str());
1141
416
        return nullptr;
1142
416
    }
1143
12.2k
    if (l_nBands > 1 && poDS->m_osBandOrder.empty())
1144
10
    {
1145
10
        CPLError(CE_Failure, CPLE_AppDefined, "Missing 'bandorder'");
1146
10
        return nullptr;
1147
10
    }
1148
1149
12.2k
    bool bNativeOrder = true;
1150
12.2k
    if (EQUAL(osByteOrder, "little"))
1151
179
    {
1152
179
        bNativeOrder = CPL_TO_BOOL(CPL_IS_LSB);
1153
179
    }
1154
12.0k
    else if (EQUAL(osByteOrder, "big"))
1155
101
    {
1156
101
        bNativeOrder = CPL_TO_BOOL(!CPL_IS_LSB);
1157
101
    }
1158
11.9k
    else if (!EQUAL(osByteOrder, ""))
1159
126
    {
1160
126
        CPLError(CE_Warning, CPLE_AppDefined,
1161
126
                 "Unhandled byteorder=%s. Assuming native order",
1162
126
                 osByteOrder.c_str());
1163
126
    }
1164
1165
12.2k
    int nPixelOffset = 0;
1166
12.2k
    int nLineOffset = 0;
1167
12.2k
    vsi_l_offset nBandOffset = 0;
1168
12.2k
    if (!ComputeSpacings(poDS->m_osBandOrder, nCols, nRows, l_nBands, eDT,
1169
12.2k
                         nPixelOffset, nLineOffset, nBandOffset))
1170
35
    {
1171
35
        return nullptr;
1172
35
    }
1173
1174
12.2k
    CPLString osDirname(CPLGetDirnameSafe(poOpenInfo->pszFilename));
1175
12.2k
    CPLString osBasename(CPLGetBasenameSafe(poOpenInfo->pszFilename));
1176
12.2k
    CPLString osGRDExtension(poOpenInfo->osExtension);
1177
12.2k
    CPLString osGRIExtension((osGRDExtension[0] == 'g') ? "gri" : "GRI");
1178
12.2k
    char **papszSiblings = poOpenInfo->GetSiblingFiles();
1179
12.2k
    if (papszSiblings)
1180
12.2k
    {
1181
12.2k
        int iFile = CSLFindString(
1182
12.2k
            papszSiblings,
1183
12.2k
            CPLFormFilenameSafe(nullptr, osBasename, osGRIExtension).c_str());
1184
12.2k
        if (iFile < 0)
1185
355
            return nullptr;
1186
11.8k
        poDS->m_osGriFilename =
1187
11.8k
            CPLFormFilenameSafe(osDirname, papszSiblings[iFile], nullptr);
1188
11.8k
    }
1189
0
    else
1190
0
    {
1191
0
        poDS->m_osGriFilename =
1192
0
            CPLFormFilenameSafe(osDirname, osBasename, osGRIExtension);
1193
0
    }
1194
1195
11.8k
    VSILFILE *fpImage =
1196
11.8k
        VSIFOpenL(poDS->m_osGriFilename,
1197
11.8k
                  (poOpenInfo->eAccess == GA_Update) ? "rb+" : "rb");
1198
11.8k
    if (fpImage == nullptr)
1199
2
    {
1200
2
        CPLError(CE_Failure, CPLE_FileIO, "Cannot open %s",
1201
2
                 poDS->m_osGriFilename.c_str());
1202
2
        return nullptr;
1203
2
    }
1204
1205
11.8k
    if (!RAWDatasetCheckMemoryUsage(nCols, nRows, l_nBands,
1206
11.8k
                                    GDALGetDataTypeSizeBytes(eDT), nPixelOffset,
1207
11.8k
                                    nLineOffset, 0, nBandOffset, fpImage))
1208
68
    {
1209
68
        VSIFCloseL(fpImage);
1210
68
        return nullptr;
1211
68
    }
1212
1213
11.8k
    poDS->eAccess = poOpenInfo->eAccess;
1214
11.8k
    poDS->nRasterXSize = nCols;
1215
11.8k
    poDS->nRasterYSize = nRows;
1216
11.8k
    poDS->m_bGeoTransformValid = true;
1217
11.8k
    poDS->m_gt[0] = dfXMin;
1218
11.8k
    poDS->m_gt[1] = (dfXMax - dfXMin) / nCols;
1219
11.8k
    poDS->m_gt[2] = 0.0;
1220
11.8k
    poDS->m_gt[3] = dfYMax;
1221
11.8k
    poDS->m_gt[4] = 0.0;
1222
11.8k
    poDS->m_gt[5] = -(dfYMax - dfYMin) / nRows;
1223
11.8k
    poDS->m_fpImage = fpImage;
1224
11.8k
    poDS->m_bNativeOrder = bNativeOrder;
1225
1226
11.8k
    if (osWkt.empty())
1227
11.0k
    {
1228
11.0k
        if (!osProjection.empty())
1229
10.4k
        {
1230
10.4k
            poDS->m_oSRS.importFromProj4(osProjection.c_str());
1231
10.4k
        }
1232
11.0k
    }
1233
743
    else
1234
743
    {
1235
743
        poDS->m_oSRS.importFromWkt(osWkt.c_str());
1236
743
    }
1237
1238
11.8k
    if (!poDS->m_osCreator.empty())
1239
318
        poDS->GDALDataset::SetMetadataItem("CREATOR", poDS->m_osCreator);
1240
1241
11.8k
    if (!poDS->m_osCreated.empty())
1242
234
        poDS->GDALDataset::SetMetadataItem("CREATED", poDS->m_osCreated);
1243
1244
    // Instantiate RAT
1245
11.8k
    if (!osRatNames.empty() && !osRatTypes.empty() && !osRatValues.empty())
1246
329
    {
1247
329
        CPLStringList aosRatNames(CSLTokenizeString2(osRatNames, ":", 0));
1248
329
        CPLStringList aosRatTypes(CSLTokenizeString2(osRatTypes, ":", 0));
1249
329
        CPLStringList aosRatValues(CSLTokenizeString2(osRatValues, ":", 0));
1250
329
        if (aosRatNames.size() >= 1 &&
1251
329
            aosRatNames.size() == aosRatTypes.size() &&
1252
285
            (aosRatValues.size() % aosRatNames.size()) == 0)
1253
269
        {
1254
269
            bool bIsCompatibleOfCT = false;
1255
269
            const int nValues = aosRatValues.size() / aosRatNames.size();
1256
269
            if ((aosRatNames.size() == 4 || aosRatNames.size() == 5) &&
1257
100
                EQUAL(aosRatNames[1], "red") &&
1258
76
                EQUAL(aosRatNames[2], "green") &&
1259
65
                EQUAL(aosRatNames[3], "blue") &&
1260
55
                (aosRatNames.size() == 4 || EQUAL(aosRatNames[4], "alpha")) &&
1261
55
                EQUAL(aosRatTypes[0], "integer") &&
1262
52
                EQUAL(aosRatTypes[1], "integer") &&
1263
49
                EQUAL(aosRatTypes[2], "integer") &&
1264
47
                EQUAL(aosRatTypes[3], "integer") &&
1265
45
                (aosRatTypes.size() == 4 || EQUAL(aosRatTypes[4], "integer")))
1266
45
            {
1267
45
                bIsCompatibleOfCT = true;
1268
45
                poDS->m_poCT.reset(new GDALColorTable());
1269
16.7k
                for (int i = 0; i < nValues; i++)
1270
16.7k
                {
1271
16.7k
                    const int nIndex = atoi(aosRatValues[i]);
1272
16.7k
                    if (nIndex >= 0 && nIndex < 65536)
1273
16.7k
                    {
1274
16.7k
                        const int nRed = atoi(aosRatValues[nValues + i]);
1275
16.7k
                        const int nGreen = atoi(aosRatValues[2 * nValues + i]);
1276
16.7k
                        const int nBlue = atoi(aosRatValues[3 * nValues + i]);
1277
16.7k
                        const int nAlpha =
1278
16.7k
                            aosRatTypes.size() == 4
1279
16.7k
                                ? 255
1280
16.7k
                                : atoi(aosRatValues[4 * nValues + i]);
1281
16.7k
                        const GDALColorEntry oEntry = {
1282
16.7k
                            static_cast<short>(nRed),
1283
16.7k
                            static_cast<short>(nGreen),
1284
16.7k
                            static_cast<short>(nBlue),
1285
16.7k
                            static_cast<short>(nAlpha)};
1286
1287
16.7k
                        poDS->m_poCT->SetColorEntry(nIndex, &oEntry);
1288
16.7k
                    }
1289
11
                    else
1290
11
                    {
1291
11
                        bIsCompatibleOfCT = false;
1292
11
                        poDS->m_poCT.reset();
1293
11
                        break;
1294
11
                    }
1295
16.7k
                }
1296
45
            }
1297
1298
            // cppcheck-suppress knownConditionTrueFalse
1299
269
            if (!bIsCompatibleOfCT)
1300
235
            {
1301
235
                poDS->m_poRAT.reset(new GDALDefaultRasterAttributeTable());
1302
679
                for (int i = 0; i < aosRatNames.size(); i++)
1303
444
                {
1304
444
                    poDS->m_poRAT->CreateColumn(
1305
444
                        aosRatNames[i],
1306
444
                        EQUAL(aosRatTypes[i], "integer")   ? GFT_Integer
1307
444
                        : EQUAL(aosRatTypes[i], "numeric") ? GFT_Real
1308
279
                                                           : GFT_String,
1309
444
                        EQUAL(aosRatNames[i], "red")          ? GFU_Red
1310
444
                        : EQUAL(aosRatNames[i], "green")      ? GFU_Green
1311
401
                        : EQUAL(aosRatNames[i], "blue")       ? GFU_Blue
1312
365
                        : EQUAL(aosRatNames[i], "alpha")      ? GFU_Alpha
1313
331
                        : EQUAL(aosRatNames[i], "name")       ? GFU_Name
1314
330
                        : EQUAL(aosRatNames[i], "pixelcount") ? GFU_PixelCount
1315
330
                                                              : GFU_Generic);
1316
444
                }
1317
2.28M
                for (int i = 0; i < nValues; i++)
1318
2.28M
                {
1319
4.89M
                    for (int j = 0; j < aosRatTypes.size(); j++)
1320
2.60M
                    {
1321
2.60M
                        if (poDS->m_poRAT->GetTypeOfCol(j) == GFT_Integer)
1322
404k
                        {
1323
404k
                            poDS->m_poRAT->SetValue(
1324
404k
                                i, j, atoi(aosRatValues[j * nValues + i]));
1325
404k
                        }
1326
2.19M
                        else if (poDS->m_poRAT->GetTypeOfCol(j) == GFT_Real)
1327
555k
                        {
1328
555k
                            poDS->m_poRAT->SetValue(
1329
555k
                                i, j, CPLAtof(aosRatValues[j * nValues + i]));
1330
555k
                        }
1331
1.64M
                        else
1332
1.64M
                        {
1333
1.64M
                            poDS->m_poRAT->SetValue(
1334
1.64M
                                i, j, aosRatValues[j * nValues + i]);
1335
1.64M
                        }
1336
2.60M
                    }
1337
2.28M
                }
1338
235
            }
1339
269
        }
1340
329
    }
1341
1342
11.8k
    CPLStringList aosMinValues(CSLTokenizeString2(osMinValue, ":", 0));
1343
11.8k
    CPLStringList aosMaxValues(CSLTokenizeString2(osMaxValue, ":", 0));
1344
1345
11.8k
    CPLStringList aosLayerNames(CSLTokenizeString2(osLayerName, ":", 0));
1346
185k
    for (int i = 1; i <= l_nBands; i++)
1347
174k
    {
1348
174k
        auto poBand = std::make_unique<RRASTERRasterBand>(
1349
174k
            poDS.get(), i, fpImage, nBandOffset * (i - 1), nPixelOffset,
1350
174k
            nLineOffset, eDT, bNativeOrder);
1351
174k
        if (!poBand->IsValid())
1352
0
        {
1353
0
            return nullptr;
1354
0
        }
1355
174k
        if (!osNoDataValue.empty() && !EQUAL(osNoDataValue, "NA"))
1356
54.2k
        {
1357
54.2k
            double dfNoDataValue = CPLAtof(osNoDataValue);
1358
54.2k
            if (eDT == GDT_Float32)
1359
2.63k
                dfNoDataValue = CastToFloat(dfNoDataValue);
1360
54.2k
            poBand->m_bHasNoDataValue = true;
1361
54.2k
            poBand->m_dfNoDataValue = dfNoDataValue;
1362
54.2k
        }
1363
174k
        if (i - 1 < static_cast<int>(aosMinValues.size()) &&
1364
3.13k
            i - 1 < static_cast<int>(aosMaxValues.size()))
1365
149
        {
1366
149
            poBand->SetMinMax(CPLAtof(aosMinValues[i - 1]),
1367
149
                              CPLAtof(aosMaxValues[i - 1]));
1368
149
        }
1369
174k
        if (i - 1 < static_cast<int>(aosLayerNames.size()))
1370
11.8k
        {
1371
11.8k
            const CPLString osName(aosLayerNames[i - 1]);
1372
11.8k
            poBand->GDALRasterBand::SetDescription(osName);
1373
11.8k
            if (EQUAL(osName, "red"))
1374
226
                poBand->SetColorInterpretation(GCI_RedBand);
1375
11.5k
            else if (EQUAL(osName, "green"))
1376
790
                poBand->SetColorInterpretation(GCI_GreenBand);
1377
10.7k
            else if (EQUAL(osName, "blue"))
1378
1.26k
                poBand->SetColorInterpretation(GCI_BlueBand);
1379
9.52k
            else if (EQUAL(osName, "alpha"))
1380
13
                poBand->SetColorInterpretation(GCI_AlphaBand);
1381
11.8k
        }
1382
174k
        poBand->m_poRAT = poDS->m_poRAT;
1383
174k
        poBand->m_poCT = poDS->m_poCT;
1384
174k
        if (poBand->m_poCT)
1385
481
            poBand->SetColorInterpretation(GCI_PaletteIndex);
1386
174k
        poDS->SetBand(i, std::move(poBand));
1387
174k
    }
1388
1389
11.8k
    return poDS.release();
1390
11.8k
}
1391
1392
/************************************************************************/
1393
/*                               Create()                               */
1394
/************************************************************************/
1395
1396
GDALDataset *RRASTERDataset::Create(const char *pszFilename, int nXSize,
1397
                                    int nYSize, int nBandsIn,
1398
                                    GDALDataType eType, char **papszOptions)
1399
1400
0
{
1401
    // Verify input options.
1402
0
    if (nBandsIn <= 0)
1403
0
    {
1404
0
        CPLError(CE_Failure, CPLE_NotSupported,
1405
0
                 "RRASTER driver does not support %d bands.", nBandsIn);
1406
0
        return nullptr;
1407
0
    }
1408
1409
0
    if (eType != GDT_Byte && eType != GDT_Int8 && eType != GDT_UInt16 &&
1410
0
        eType != GDT_Int16 && eType != GDT_Int32 && eType != GDT_UInt32 &&
1411
0
        eType != GDT_Float32 && eType != GDT_Float64)
1412
0
    {
1413
0
        CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type (%s).",
1414
0
                 GDALGetDataTypeName(eType));
1415
0
        return nullptr;
1416
0
    }
1417
1418
0
    CPLString osGRDExtension(CPLGetExtensionSafe(pszFilename));
1419
0
    if (!EQUAL(osGRDExtension, "grd"))
1420
0
    {
1421
0
        CPLError(CE_Failure, CPLE_NotSupported,
1422
0
                 "RRASTER driver only supports grd extension");
1423
0
        return nullptr;
1424
0
    }
1425
1426
0
    int nPixelOffset = 0;
1427
0
    int nLineOffset = 0;
1428
0
    vsi_l_offset nBandOffset = 0;
1429
0
    CPLString osBandOrder(
1430
0
        CSLFetchNameValueDef(papszOptions, "INTERLEAVE", "BIL"));
1431
0
    if (!ComputeSpacings(osBandOrder, nXSize, nYSize, nBandsIn, eType,
1432
0
                         nPixelOffset, nLineOffset, nBandOffset))
1433
0
    {
1434
0
        return nullptr;
1435
0
    }
1436
1437
0
    const std::string osGRIExtension((osGRDExtension[0] == 'g') ? "gri"
1438
0
                                                                : "GRI");
1439
0
    const std::string osGriFilename(
1440
0
        CPLResetExtensionSafe(pszFilename, osGRIExtension.c_str()));
1441
1442
    // Try to create the file.
1443
0
    VSILFILE *fpImage = VSIFOpenL(osGriFilename.c_str(), "wb+");
1444
1445
0
    if (fpImage == nullptr)
1446
0
    {
1447
0
        CPLError(CE_Failure, CPLE_OpenFailed,
1448
0
                 "Attempt to create file `%s' failed.", osGriFilename.c_str());
1449
0
        return nullptr;
1450
0
    }
1451
1452
0
    RRASTERDataset *poDS = new RRASTERDataset;
1453
0
    poDS->eAccess = GA_Update;
1454
0
    poDS->m_bHeaderDirty = true;
1455
0
    poDS->m_osGriFilename = osGriFilename;
1456
0
    poDS->nRasterXSize = nXSize;
1457
0
    poDS->nRasterYSize = nYSize;
1458
0
    poDS->m_fpImage = fpImage;
1459
0
    poDS->m_bNativeOrder = true;
1460
0
    poDS->m_osBandOrder = osBandOrder.toupper();
1461
0
    poDS->m_bInitRaster = CPLFetchBool(papszOptions, "@INIT_RASTER", true);
1462
1463
0
    const char *pszPixelType = CSLFetchNameValue(papszOptions, "PIXELTYPE");
1464
0
    const bool bByteSigned = (eType == GDT_Byte && pszPixelType &&
1465
0
                              EQUAL(pszPixelType, "SIGNEDBYTE"));
1466
0
    if (bByteSigned)
1467
0
        poDS->m_bSignedByte = true;
1468
1469
0
    for (int i = 1; i <= nBandsIn; i++)
1470
0
    {
1471
0
        RRASTERRasterBand *poBand =
1472
0
            new RRASTERRasterBand(poDS, i, fpImage, nBandOffset * (i - 1),
1473
0
                                  nPixelOffset, nLineOffset, eType, true);
1474
0
        poDS->SetBand(i, poBand);
1475
0
    }
1476
1477
0
    return poDS;
1478
0
}
1479
1480
/************************************************************************/
1481
/*                             CreateCopy()                             */
1482
/************************************************************************/
1483
1484
GDALDataset *RRASTERDataset::CreateCopy(const char *pszFilename,
1485
                                        GDALDataset *poSrcDS, int bStrict,
1486
                                        char **papszOptions,
1487
                                        GDALProgressFunc pfnProgress,
1488
                                        void *pProgressData)
1489
1490
0
{
1491
    // Proceed with normal copying using the default createcopy  operators.
1492
0
    GDALDriver *poDriver =
1493
0
        reinterpret_cast<GDALDriver *>(GDALGetDriverByName("RRASTER"));
1494
1495
0
    char **papszAdjustedOptions = CSLDuplicate(papszOptions);
1496
0
    papszAdjustedOptions =
1497
0
        CSLSetNameValue(papszAdjustedOptions, "@INIT_RASTER", "NO");
1498
0
    GDALDataset *poOutDS = poDriver->DefaultCreateCopy(
1499
0
        pszFilename, poSrcDS, bStrict, papszAdjustedOptions, pfnProgress,
1500
0
        pProgressData);
1501
0
    CSLDestroy(papszAdjustedOptions);
1502
1503
0
    if (poOutDS != nullptr)
1504
0
        poOutDS->FlushCache(false);
1505
1506
0
    return poOutDS;
1507
0
}
1508
1509
/************************************************************************/
1510
/*                   GDALRegister_RRASTER()                             */
1511
/************************************************************************/
1512
1513
void GDALRegister_RRASTER()
1514
1515
24
{
1516
24
    if (GDALGetDriverByName("RRASTER") != nullptr)
1517
0
        return;
1518
1519
24
    GDALDriver *poDriver = new GDALDriver();
1520
1521
24
    poDriver->SetDescription("RRASTER");
1522
24
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1523
24
    poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "grd");
1524
24
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "R Raster");
1525
24
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC,
1526
24
                              "drivers/raster/rraster.html");
1527
24
    poDriver->SetMetadataItem(
1528
24
        GDAL_DMD_CREATIONDATATYPES,
1529
24
        "Byte Int8 Int16 UInt16 Int32 UInt32 Float32 Float64");
1530
1531
24
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1532
1533
24
    poDriver->SetMetadataItem(
1534
24
        GDAL_DMD_CREATIONOPTIONLIST,
1535
24
        "<CreationOptionList>"
1536
24
        "   <Option name='PIXELTYPE' type='string' description='(deprecated, "
1537
24
        "use Int8) "
1538
24
        "By setting this to SIGNEDBYTE, a new Byte file can be forced to be "
1539
24
        "written "
1540
24
        "as signed byte'/>"
1541
24
        "   <Option name='INTERLEAVE' type='string-select' default='BIL'>"
1542
24
        "       <Value>BIP</Value>"
1543
24
        "       <Value>BIL</Value>"
1544
24
        "       <Value>BSQ</Value>"
1545
24
        "   </Option>"
1546
24
        "</CreationOptionList>");
1547
1548
24
    poDriver->pfnOpen = RRASTERDataset::Open;
1549
24
    poDriver->pfnIdentify = RRASTERDataset::Identify;
1550
24
    poDriver->pfnCreate = RRASTERDataset::Create;
1551
24
    poDriver->pfnCreateCopy = RRASTERDataset::CreateCopy;
1552
1553
24
    poDriver->SetMetadataItem(GDAL_DCAP_UPDATE, "YES");
1554
24
    poDriver->SetMetadataItem(GDAL_DMD_UPDATE_ITEMS, "GeoTransform SRS NoData "
1555
24
                                                     "RasterValues "
1556
24
                                                     "DatasetMetadata");
1557
1558
24
    GetGDALDriverManager()->RegisterDriver(poDriver);
1559
24
}