LCOV - code coverage report
Current view: top level - frmts/r - rdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 174 254 68.5 %
Date: 2024-05-02 22:57:13 Functions: 14 14 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  R Format Driver
       4             :  * Purpose:  Read/write R stats package object format.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2009, Frank Warmerdam <warmerdam@pobox.com>
       9             :  * Copyright (c) 2009-2010, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ****************************************************************************/
      29             : 
      30             : #include "cpl_port.h"
      31             : #include "rdataset.h"
      32             : 
      33             : #include <cstddef>
      34             : #include <cstdlib>
      35             : #include <cstring>
      36             : #if HAVE_FCNTL_H
      37             : #include <fcntl.h>
      38             : #endif
      39             : 
      40             : #include <algorithm>
      41             : #include <limits>
      42             : #include <string>
      43             : #include <utility>
      44             : 
      45             : #include "cpl_conv.h"
      46             : #include "cpl_error.h"
      47             : #include "cpl_progress.h"
      48             : #include "cpl_string.h"
      49             : #include "cpl_vsi.h"
      50             : #include "gdal.h"
      51             : #include "gdal_frmts.h"
      52             : #include "gdal_pam.h"
      53             : #include "gdal_priv.h"
      54             : 
      55             : // constexpr int R_NILSXP = 0;
      56             : constexpr int R_LISTSXP = 2;
      57             : constexpr int R_CHARSXP = 9;
      58             : constexpr int R_INTSXP = 13;
      59             : constexpr int R_REALSXP = 14;
      60             : constexpr int R_STRSXP = 16;
      61             : 
      62             : namespace
      63             : {
      64             : 
      65             : // TODO(schwehr): Move this to port/? for general use.
      66          20 : bool SafeMult(GIntBig a, GIntBig b, GIntBig *result)
      67             : {
      68          20 :     if (a == 0 || b == 0)
      69             :     {
      70           0 :         *result = 0;
      71           0 :         return true;
      72             :     }
      73             : 
      74          20 :     bool result_positive = (a >= 0 && b >= 0) || (a < 0 && b < 0);
      75          20 :     if (result_positive)
      76             :     {
      77             :         // Cannot convert min() to positive.
      78          40 :         if (a == std::numeric_limits<GIntBig>::min() ||
      79          20 :             b == std::numeric_limits<GIntBig>::min())
      80             :         {
      81           0 :             *result = 0;
      82           0 :             return false;
      83             :         }
      84          20 :         if (a < 0)
      85             :         {
      86           0 :             a = -a;
      87           0 :             b = -b;
      88             :         }
      89          20 :         if (a > std::numeric_limits<GIntBig>::max() / b)
      90             :         {
      91           0 :             *result = 0;
      92           0 :             return false;
      93             :         }
      94          20 :         *result = a * b;
      95          20 :         return true;
      96             :     }
      97             : 
      98           0 :     if (b < a)
      99           0 :         std::swap(a, b);
     100           0 :     if (a < (std::numeric_limits<GIntBig>::min() + 1) / b)
     101             :     {
     102           0 :         *result = 0;
     103           0 :         return false;
     104             :     }
     105             : 
     106           0 :     *result = a * b;
     107           0 :     return true;
     108             : }
     109             : 
     110             : }  // namespace
     111             : 
     112             : /************************************************************************/
     113             : /*                            RRasterBand()                             */
     114             : /************************************************************************/
     115             : 
     116           7 : RRasterBand::RRasterBand(RDataset *poDSIn, int nBandIn,
     117           7 :                          const double *padfMatrixValuesIn)
     118           7 :     : padfMatrixValues(padfMatrixValuesIn)
     119             : {
     120           7 :     poDS = poDSIn;
     121           7 :     nBand = nBandIn;
     122             : 
     123           7 :     eDataType = GDT_Float64;
     124             : 
     125           7 :     nBlockXSize = poDSIn->nRasterXSize;
     126           7 :     nBlockYSize = 1;
     127           7 : }
     128             : 
     129             : /************************************************************************/
     130             : /*                             IReadBlock()                             */
     131             : /************************************************************************/
     132             : 
     133          45 : CPLErr RRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
     134             :                                void *pImage)
     135             : {
     136          45 :     memcpy(pImage, padfMatrixValues + nBlockYOff * nBlockXSize,
     137          45 :            nBlockXSize * 8);
     138          45 :     return CE_None;
     139             : }
     140             : 
     141             : /************************************************************************/
     142             : /* ==================================================================== */
     143             : /*                              RDataset()                              */
     144             : /* ==================================================================== */
     145             : /************************************************************************/
     146             : 
     147             : /************************************************************************/
     148             : /*                              RDataset()                              */
     149             : /************************************************************************/
     150             : 
     151          10 : RDataset::RDataset()
     152          10 :     : fp(nullptr), bASCII(FALSE), nStartOfData(0), padfMatrixValues(nullptr)
     153             : {
     154          10 : }
     155             : 
     156             : /************************************************************************/
     157             : /*                             ~RDataset()                              */
     158             : /************************************************************************/
     159             : 
     160          20 : RDataset::~RDataset()
     161             : {
     162          10 :     FlushCache(true);
     163          10 :     CPLFree(padfMatrixValues);
     164             : 
     165          10 :     if (fp)
     166          10 :         VSIFCloseL(fp);
     167          20 : }
     168             : 
     169             : /************************************************************************/
     170             : /*                             ASCIIFGets()                             */
     171             : /*                                                                      */
     172             : /*      Fetch one line from an ASCII source into osLastStringRead.      */
     173             : /************************************************************************/
     174             : 
     175        1385 : const char *RDataset::ASCIIFGets()
     176             : 
     177             : {
     178        1385 :     char chNextChar = '\0';
     179             : 
     180        1385 :     osLastStringRead.resize(0);
     181             : 
     182        3952 :     do
     183             :     {
     184        5337 :         chNextChar = '\n';
     185        5337 :         VSIFReadL(&chNextChar, 1, 1, fp);
     186        5337 :         if (chNextChar != '\n')
     187        3952 :             osLastStringRead += chNextChar;
     188        5337 :     } while (chNextChar != '\n' && chNextChar != '\0');
     189             : 
     190        2770 :     return osLastStringRead;
     191             : }
     192             : 
     193             : /************************************************************************/
     194             : /*                            ReadInteger()                             */
     195             : /************************************************************************/
     196             : 
     197         190 : int RDataset::ReadInteger()
     198             : 
     199             : {
     200         190 :     if (bASCII)
     201             :     {
     202          95 :         return atoi(ASCIIFGets());
     203             :     }
     204             : 
     205          95 :     GInt32 nValue = 0;
     206             : 
     207          95 :     if (VSIFReadL(&nValue, 4, 1, fp) != 1)
     208           0 :         return -1;
     209          95 :     CPL_MSBPTR32(&nValue);
     210             : 
     211          95 :     return nValue;
     212             : }
     213             : 
     214             : /************************************************************************/
     215             : /*                             ReadFloat()                              */
     216             : /************************************************************************/
     217             : 
     218        1280 : double RDataset::ReadFloat()
     219             : 
     220             : {
     221        1280 :     if (bASCII)
     222             :     {
     223        1280 :         return CPLAtof(ASCIIFGets());
     224             :     }
     225             : 
     226           0 :     double dfValue = 0.0;
     227             : 
     228           0 :     if (VSIFReadL(&dfValue, 8, 1, fp) != 1)
     229           0 :         return -1;
     230           0 :     CPL_MSBPTR64(&dfValue);
     231             : 
     232           0 :     return dfValue;
     233             : }
     234             : 
     235             : /************************************************************************/
     236             : /*                             ReadString()                             */
     237             : /************************************************************************/
     238             : 
     239          20 : const char *RDataset::ReadString()
     240             : 
     241             : {
     242          20 :     if (ReadInteger() % 256 != R_CHARSXP)
     243             :     {
     244           0 :         osLastStringRead = "";
     245           0 :         return "";
     246             :     }
     247             : 
     248          20 :     const int nLenSigned = ReadInteger();
     249          20 :     if (nLenSigned < 0)
     250             :     {
     251           0 :         osLastStringRead = "";
     252           0 :         return "";
     253             :     }
     254          20 :     const size_t nLen = static_cast<size_t>(nLenSigned);
     255             : 
     256          20 :     char *pachWrkBuf = static_cast<char *>(VSIMalloc(nLen));
     257          20 :     if (pachWrkBuf == nullptr)
     258             :     {
     259           0 :         osLastStringRead = "";
     260           0 :         return "";
     261             :     }
     262          20 :     if (VSIFReadL(pachWrkBuf, 1, nLen, fp) != nLen)
     263             :     {
     264           0 :         osLastStringRead = "";
     265           0 :         CPLFree(pachWrkBuf);
     266           0 :         return "";
     267             :     }
     268             : 
     269          20 :     if (bASCII)
     270             :     {
     271             :         // Suck up newline and any extra junk.
     272          10 :         ASCIIFGets();
     273             :     }
     274             : 
     275          20 :     osLastStringRead.assign(pachWrkBuf, nLen);
     276          20 :     CPLFree(pachWrkBuf);
     277             : 
     278          20 :     return osLastStringRead;
     279             : }
     280             : 
     281             : /************************************************************************/
     282             : /*                              ReadPair()                              */
     283             : /************************************************************************/
     284             : 
     285          30 : bool RDataset::ReadPair(CPLString &osObjName, int &nObjCode)
     286             : 
     287             : {
     288          30 :     nObjCode = ReadInteger();
     289          30 :     if (nObjCode == 254)
     290          10 :         return true;
     291             : 
     292          20 :     if ((nObjCode % 256) != R_LISTSXP)
     293             :     {
     294           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     295             :                  "Did not find expected object pair object.");
     296           0 :         return false;
     297             :     }
     298             : 
     299          20 :     int nPairCount = ReadInteger();
     300          20 :     if (nPairCount != 1)
     301             :     {
     302           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     303             :                  "Did not find expected pair count of 1.");
     304           0 :         return false;
     305             :     }
     306             : 
     307             :     // Read the object name.
     308          20 :     const char *pszName = ReadString();
     309          20 :     if (pszName == nullptr || pszName[0] == '\0')
     310           0 :         return false;
     311             : 
     312          20 :     osObjName = pszName;
     313             : 
     314             :     // Confirm that we have a numeric matrix object.
     315          20 :     nObjCode = ReadInteger();
     316             : 
     317          20 :     return true;
     318             : }
     319             : 
     320             : /************************************************************************/
     321             : /*                              Identify()                              */
     322             : /************************************************************************/
     323             : 
     324       50105 : int RDataset::Identify(GDALOpenInfo *poOpenInfo)
     325             : {
     326       50105 :     if (poOpenInfo->nHeaderBytes < 50)
     327       46002 :         return FALSE;
     328             : 
     329             :     // If the extension is .rda and the file type is gzip
     330             :     // compressed we assume it is a gzipped R binary file.
     331        4137 :     if (memcmp(poOpenInfo->pabyHeader, "\037\213\b", 3) == 0 &&
     332          34 :         EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "rda"))
     333           6 :         return TRUE;
     334             : 
     335             :     // Is this an ASCII or XDR binary R file?
     336        4097 :     if (!STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "RDA2\nA\n") &&
     337        4087 :         !STARTS_WITH_CI((const char *)poOpenInfo->pabyHeader, "RDX2\nX\n"))
     338        4083 :         return FALSE;
     339             : 
     340          14 :     return TRUE;
     341             : }
     342             : 
     343             : /************************************************************************/
     344             : /*                                Open()                                */
     345             : /************************************************************************/
     346             : 
     347          10 : GDALDataset *RDataset::Open(GDALOpenInfo *poOpenInfo)
     348             : {
     349             : #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
     350             :     if (poOpenInfo->pabyHeader == nullptr)
     351             :         return nullptr;
     352             : #else
     353             :     // During fuzzing, do not use Identify to reject crazy content.
     354          10 :     if (!Identify(poOpenInfo))
     355           0 :         return nullptr;
     356             : #endif
     357             : 
     358             :     // Confirm the requested access is supported.
     359          10 :     if (poOpenInfo->eAccess == GA_Update)
     360             :     {
     361           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     362             :                  "The R driver does not support update access to existing"
     363             :                  " datasets.");
     364           0 :         return nullptr;
     365             :     }
     366             : 
     367             :     // Do we need to route the file through the decompression machinery?
     368          10 :     const bool bCompressed =
     369          10 :         memcmp(poOpenInfo->pabyHeader, "\037\213\b", 3) == 0;
     370             :     const CPLString osAdjustedFilename =
     371          30 :         std::string(bCompressed ? "/vsigzip/" : "") + poOpenInfo->pszFilename;
     372             : 
     373             :     // Establish this as a dataset and open the file using VSI*L.
     374          20 :     auto poDS = std::make_unique<RDataset>();
     375             : 
     376          10 :     poDS->fp = VSIFOpenL(osAdjustedFilename, "r");
     377          10 :     if (poDS->fp == nullptr)
     378             :     {
     379           0 :         return nullptr;
     380             :     }
     381             : 
     382          10 :     poDS->bASCII = STARTS_WITH_CI(
     383             :         reinterpret_cast<char *>(poOpenInfo->pabyHeader), "RDA2\nA\n");
     384             : 
     385             :     // Confirm this is a version 2 file.
     386          10 :     VSIFSeekL(poDS->fp, 7, SEEK_SET);
     387          10 :     if (poDS->ReadInteger() != R_LISTSXP)
     388             :     {
     389           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     390             :                  "It appears %s is not a version 2 R object file after all!",
     391             :                  poOpenInfo->pszFilename);
     392           0 :         return nullptr;
     393             :     }
     394             : 
     395             :     // Skip the version values.
     396          10 :     poDS->ReadInteger();
     397          10 :     poDS->ReadInteger();
     398             : 
     399             :     // Confirm we have a numeric vector object in a pairlist.
     400          20 :     CPLString osObjName;
     401          10 :     int nObjCode = 0;
     402             : 
     403          10 :     if (!poDS->ReadPair(osObjName, nObjCode))
     404             :     {
     405           0 :         return nullptr;
     406             :     }
     407             : 
     408          10 :     if (nObjCode % 256 != R_REALSXP)
     409             :     {
     410           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     411             :                  "Failed to find expected numeric vector object.");
     412           0 :         return nullptr;
     413             :     }
     414             : 
     415          10 :     poDS->SetMetadataItem("R_OBJECT_NAME", osObjName);
     416             : 
     417             :     // Read the count.
     418          10 :     const int nValueCount = poDS->ReadInteger();
     419          10 :     if (nValueCount < 0)
     420             :     {
     421           0 :         CPLError(CE_Failure, CPLE_AppDefined, "nValueCount < 0: %d",
     422             :                  nValueCount);
     423           0 :         return nullptr;
     424             :     }
     425             : 
     426          10 :     poDS->nStartOfData = VSIFTellL(poDS->fp);
     427             : 
     428             :     // TODO(schwehr): Factor in the size of doubles.
     429             :     VSIStatBufL stat;
     430             :     const int dStatSuccess =
     431          10 :         VSIStatExL(osAdjustedFilename, &stat, VSI_STAT_SIZE_FLAG);
     432          20 :     if (dStatSuccess != 0 || static_cast<vsi_l_offset>(nValueCount) >
     433          10 :                                  stat.st_size - poDS->nStartOfData)
     434             :     {
     435           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     436             :                  "Corrupt file.  "
     437             :                  "Object claims to be larger than available bytes. "
     438             :                  "%d > " CPL_FRMT_GUIB,
     439           0 :                  nValueCount, stat.st_size - poDS->nStartOfData);
     440           0 :         return nullptr;
     441             :     }
     442             : 
     443             :     // Read/Skip ahead to attributes.
     444          10 :     if (poDS->bASCII)
     445             :     {
     446          10 :         poDS->padfMatrixValues =
     447           5 :             static_cast<double *>(VSIMalloc2(nValueCount, sizeof(double)));
     448           5 :         if (poDS->padfMatrixValues == nullptr)
     449             :         {
     450           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot allocate %d doubles",
     451             :                      nValueCount);
     452           0 :             return nullptr;
     453             :         }
     454        1285 :         for (int iValue = 0; iValue < nValueCount; iValue++)
     455        1280 :             poDS->padfMatrixValues[iValue] = poDS->ReadFloat();
     456             :     }
     457             :     else
     458             :     {
     459           5 :         VSIFSeekL(poDS->fp, 8 * nValueCount, SEEK_CUR);
     460             :     }
     461             : 
     462             :     // Read pairs till we run out, trying to find a few items that
     463             :     // have special meaning to us.
     464          10 :     poDS->nRasterXSize = 0;
     465          10 :     poDS->nRasterYSize = 0;
     466          10 :     int nBandCount = 0;
     467             : 
     468          20 :     while (poDS->ReadPair(osObjName, nObjCode) && nObjCode != 254)
     469             :     {
     470          10 :         if (osObjName == "dim" && nObjCode % 256 == R_INTSXP)
     471             :         {
     472          10 :             const int nCount = poDS->ReadInteger();
     473          10 :             if (nCount == 2)
     474             :             {
     475           0 :                 poDS->nRasterXSize = poDS->ReadInteger();
     476           0 :                 poDS->nRasterYSize = poDS->ReadInteger();
     477           0 :                 nBandCount = 1;
     478             :             }
     479          10 :             else if (nCount == 3)
     480             :             {
     481          10 :                 poDS->nRasterXSize = poDS->ReadInteger();
     482          10 :                 poDS->nRasterYSize = poDS->ReadInteger();
     483          10 :                 nBandCount = poDS->ReadInteger();
     484             :             }
     485             :             else
     486             :             {
     487           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     488             :                          "R 'dim' dimension wrong.");
     489           0 :                 return nullptr;
     490             :             }
     491             :         }
     492           0 :         else if (nObjCode % 256 == R_REALSXP)
     493             :         {
     494           0 :             int nCount = poDS->ReadInteger();
     495           0 :             while (nCount > 0 && !VSIFEofL(poDS->fp))
     496             :             {
     497           0 :                 nCount--;
     498           0 :                 poDS->ReadFloat();
     499             :             }
     500             :         }
     501           0 :         else if (nObjCode % 256 == R_INTSXP)
     502             :         {
     503           0 :             int nCount = poDS->ReadInteger();
     504           0 :             while (nCount > 0 && !VSIFEofL(poDS->fp))
     505             :             {
     506           0 :                 nCount--;
     507           0 :                 poDS->ReadInteger();
     508             :             }
     509             :         }
     510           0 :         else if (nObjCode % 256 == R_STRSXP)
     511             :         {
     512           0 :             int nCount = poDS->ReadInteger();
     513           0 :             while (nCount > 0 && !VSIFEofL(poDS->fp))
     514             :             {
     515           0 :                 nCount--;
     516           0 :                 poDS->ReadString();
     517             :             }
     518             :         }
     519           0 :         else if (nObjCode % 256 == R_CHARSXP)
     520             :         {
     521           0 :             poDS->ReadString();
     522             :         }
     523             :     }
     524             : 
     525          10 :     if (poDS->nRasterXSize == 0)
     526             :     {
     527           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     528             :                  "Failed to find dim dimension information for R dataset.");
     529           0 :         return nullptr;
     530             :     }
     531             : 
     532          20 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
     533          10 :         !GDALCheckBandCount(nBandCount, TRUE))
     534             :     {
     535           0 :         return nullptr;
     536             :     }
     537             : 
     538          10 :     GIntBig result = 0;
     539          10 :     bool ok = SafeMult(nBandCount, poDS->nRasterXSize, &result);
     540          10 :     ok &= SafeMult(result, poDS->nRasterYSize, &result);
     541             : 
     542          10 :     if (!ok || nValueCount < result)
     543             :     {
     544           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Not enough pixel data.");
     545           0 :         return nullptr;
     546             :     }
     547             : 
     548             :     // Create the raster band object(s).
     549          24 :     for (int iBand = 0; iBand < nBandCount; iBand++)
     550             :     {
     551           0 :         std::unique_ptr<GDALRasterBand> poBand;
     552             : 
     553          14 :         if (poDS->bASCII)
     554           7 :             poBand = std::make_unique<RRasterBand>(
     555           7 :                 poDS.get(), iBand + 1,
     556          14 :                 poDS->padfMatrixValues + static_cast<size_t>(iBand) *
     557           7 :                                              poDS->nRasterXSize *
     558          14 :                                              poDS->nRasterYSize);
     559             :         else
     560             :         {
     561          14 :             poBand = RawRasterBand::Create(
     562           7 :                 poDS.get(), iBand + 1, poDS->fp,
     563          14 :                 poDS->nStartOfData +
     564           7 :                     static_cast<vsi_l_offset>(poDS->nRasterXSize) *
     565           7 :                         poDS->nRasterYSize * 8 * iBand,
     566           7 :                 8, poDS->nRasterXSize * 8, GDT_Float64,
     567             :                 RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
     568           7 :                 RawRasterBand::OwnFP::NO);
     569           7 :             if (!poBand)
     570           0 :                 return nullptr;
     571             :         }
     572          14 :         poDS->SetBand(iBand + 1, std::move(poBand));
     573             :     }
     574             : 
     575             :     // Initialize any PAM information.
     576          10 :     poDS->SetDescription(poOpenInfo->pszFilename);
     577          10 :     poDS->TryLoadXML();
     578             : 
     579             :     // Check for overviews.
     580          10 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     581             : 
     582          10 :     return poDS.release();
     583             : }
     584             : 
     585             : /************************************************************************/
     586             : /*                        GDALRegister_R()                              */
     587             : /************************************************************************/
     588             : 
     589        1512 : void GDALRegister_R()
     590             : 
     591             : {
     592        1512 :     if (GDALGetDriverByName("R") != nullptr)
     593         295 :         return;
     594             : 
     595        1217 :     GDALDriver *poDriver = new GDALDriver();
     596             : 
     597        1217 :     poDriver->SetDescription("R");
     598        1217 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     599        1217 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "R Object Data Store");
     600        1217 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/r.html");
     601        1217 :     poDriver->SetMetadataItem(GDAL_DMD_EXTENSION, "rda");
     602        1217 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES, "Float32");
     603        1217 :     poDriver->SetMetadataItem(
     604             :         GDAL_DMD_CREATIONOPTIONLIST,
     605             :         "<CreationOptionList>"
     606             :         "   <Option name='ASCII' type='boolean' description='For ASCII output, "
     607             :         "default NO'/>"
     608             :         "   <Option name='COMPRESS' type='boolean' description='Produced "
     609             :         "Compressed output, default YES'/>"
     610        1217 :         "</CreationOptionList>");
     611             : 
     612        1217 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     613             : 
     614        1217 :     poDriver->pfnOpen = RDataset::Open;
     615        1217 :     poDriver->pfnIdentify = RDataset::Identify;
     616        1217 :     poDriver->pfnCreateCopy = RCreateCopy;
     617             : 
     618        1217 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     619             : }

Generated by: LCOV version 1.14