LCOV - code coverage report
Current view: top level - frmts/raw - rrasterdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 690 752 91.8 %
Date: 2025-06-28 21:28:23 Functions: 43 44 97.7 %

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

Generated by: LCOV version 1.14