LCOV - code coverage report
Current view: top level - frmts/lerc - lercdriver.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 210 238 88.2 %
Date: 2026-07-09 08:35:43 Functions: 16 16 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GDAL
       4             :  * Purpose:  LERC driver
       5             :  * Author:   Even Rouault <even dot rouault at spatialys.com>
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2026, Even Rouault <even dot rouault at spatialys.com>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "lercdrivercore.h"
      14             : 
      15             : #include "cpl_vsi.h"
      16             : #include "cpl_vsi_virtual.h"
      17             : 
      18             : #include "gdal_frmts.h"
      19             : #include "gdal_pam.h"
      20             : #include "gdal_priv.h"
      21             : 
      22             : #include "Lerc_c_api.h"
      23             : 
      24             : #include <algorithm>
      25             : #include <limits>
      26             : 
      27             : #ifndef LERC_AT_LEAST_VERSION
      28             : #define LERC_AT_LEAST_VERSION(maj, min, patch) 0
      29             : #endif
      30             : 
      31             : namespace gdal
      32             : {
      33             : class LERCBand;
      34             : 
      35             : /************************************************************************/
      36             : /*                             LERCDataset                              */
      37             : /************************************************************************/
      38             : 
      39          22 : class LERCDataset final : public GDALPamDataset
      40             : {
      41             :   public:
      42          11 :     LERCDataset() = default;
      43             :     ~LERCDataset() override;
      44             : 
      45             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
      46             : 
      47             :   private:
      48             :     friend class LERCBand;
      49             :     friend class LERCMaskBand;
      50             : 
      51             :     bool m_bUseNoData = true;
      52             :     double m_dfNoDataValue = std::numeric_limits<double>::quiet_NaN();
      53             :     int m_nLercDataType = 0;
      54             :     int m_nDepth = 0;
      55             :     int m_nLercBands = 0;
      56             :     int m_nMaskCount = 0;
      57             :     unsigned m_nBlobSize = 0;
      58             :     std::unique_ptr<unsigned char, VSIFreeReleaser> m_pabyBlob{};
      59             :     std::unique_ptr<unsigned char, VSIFreeReleaser> m_pabyDecodedImage{};
      60             :     std::unique_ptr<unsigned char, VSIFreeReleaser> m_pabyDecodedMask{};
      61             : 
      62             :     const unsigned char *GetDecodedImage();
      63             :     const unsigned char *GetDecodedMask();
      64             : };
      65             : 
      66             : LERCDataset::~LERCDataset() = default;
      67             : 
      68             : /************************************************************************/
      69             : /*                               LERCBand                               */
      70             : /************************************************************************/
      71             : 
      72             : class LERCBand final : public GDALPamRasterBand
      73             : {
      74             :   public:
      75             :     LERCBand(LERCDataset *poDSIn, int nBandIn, GDALDataType eDT);
      76             : 
      77             :     double GetNoDataValue(int *pbHasNoData) override;
      78             :     int GetMaskFlags() override;
      79             :     GDALRasterBand *GetMaskBand() override;
      80             : 
      81             :   protected:
      82             :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
      83             : 
      84             :   private:
      85             :     std::unique_ptr<GDALRasterBand> m_poMaskBand{};
      86             : };
      87             : 
      88             : /************************************************************************/
      89             : /*                             LERCMaskBand                             */
      90             : /************************************************************************/
      91             : 
      92             : class LERCMaskBand final : public GDALRasterBand
      93             : {
      94             :   public:
      95           3 :     LERCMaskBand(LERCDataset *poDSIn, int nMaskIdx) : m_nMaskIdx(nMaskIdx)
      96             :     {
      97           3 :         poDS = poDSIn;
      98           3 :         nBand = 0;
      99           3 :         eDataType = GDT_Byte;
     100           3 :         nRasterXSize = poDS->GetRasterXSize();
     101           3 :         nRasterYSize = poDS->GetRasterYSize();
     102           3 :         nBlockXSize = nRasterXSize;
     103           3 :         nBlockYSize = 1;
     104           3 :     }
     105             : 
     106             :   protected:
     107             :     CPLErr IReadBlock(int nBlockXOff, int nBlockYOff, void *pData) override;
     108             : 
     109             :   private:
     110             :     const int m_nMaskIdx;
     111             : };
     112             : 
     113             : /************************************************************************/
     114             : /*                    LERCDataset::GetDecodedImage()                    */
     115             : /************************************************************************/
     116             : 
     117        1466 : const unsigned char *LERCDataset::GetDecodedImage()
     118             : {
     119        1466 :     if (!m_pabyBlob)
     120        1455 :         return m_pabyDecodedImage.get();
     121             : 
     122          11 :     m_pabyDecodedImage.reset(static_cast<unsigned char *>(VSI_MALLOC_VERBOSE(
     123             :         static_cast<size_t>(nRasterXSize) * nRasterYSize * m_nDepth *
     124             :         m_nLercBands *
     125             :         GDALGetDataTypeSizeBytes(GetRasterBand(1)->GetRasterDataType()))));
     126          11 :     if (m_nMaskCount)
     127             :     {
     128           3 :         m_pabyDecodedMask.reset(static_cast<unsigned char *>(VSI_MALLOC_VERBOSE(
     129             :             static_cast<size_t>(nRasterXSize) * nRasterYSize * m_nMaskCount)));
     130           3 :         if (!m_pabyDecodedMask)
     131           0 :             m_pabyDecodedImage.reset();
     132             :     }
     133             : 
     134          11 :     if (m_pabyDecodedImage)
     135             :     {
     136          11 :         const lerc_status status = lerc_decode(
     137          11 :             m_pabyBlob.get(), m_nBlobSize,
     138             : #if LERC_AT_LEAST_VERSION(3, 0, 0)
     139             :             m_nMaskCount,
     140             : #endif
     141             :             m_pabyDecodedMask.get(), m_nDepth, nRasterXSize, nRasterYSize,
     142          11 :             m_nLercBands, m_nLercDataType, m_pabyDecodedImage.get());
     143          11 :         if (status != 0)
     144             :         {
     145           0 :             m_pabyDecodedImage.reset();
     146           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     147             :                      "lerc_decode() failed with status %d", status);
     148             :         }
     149             :     }
     150          11 :     m_pabyBlob.reset();
     151             : 
     152          11 :     return m_pabyDecodedImage.get();
     153             : }
     154             : 
     155             : /************************************************************************/
     156             : /*                    LERCDataset::GetDecodedMask()                     */
     157             : /************************************************************************/
     158             : 
     159         120 : const unsigned char *LERCDataset::GetDecodedMask()
     160             : {
     161         120 :     GetDecodedImage();
     162         120 :     return m_pabyDecodedMask.get();
     163             : }
     164             : 
     165             : /************************************************************************/
     166             : /*                         LERCBand::LERCBand()                         */
     167             : /************************************************************************/
     168             : 
     169          13 : LERCBand::LERCBand(LERCDataset *poDSIn, int nBandIn, GDALDataType eDT)
     170             : {
     171          13 :     poDS = poDSIn;
     172          13 :     nBand = nBandIn;
     173          13 :     eDataType = eDT;
     174          13 :     nRasterXSize = poDS->GetRasterXSize();
     175          13 :     nRasterYSize = poDS->GetRasterYSize();
     176          13 :     nBlockXSize = nRasterXSize;
     177          13 :     nBlockYSize = 1;
     178          13 : }
     179             : 
     180             : /************************************************************************/
     181             : /*                    SetNodataWhereMaskIsInvalid()                     */
     182             : /************************************************************************/
     183             : 
     184             : template <class T>
     185          48 : static void SetNodataWhereMaskIsInvalid(T *pLine, const unsigned char *pSrcMask,
     186             :                                         int nPixels, T nodataValue)
     187             : {
     188        1104 :     for (int i = 0; i < nPixels; ++i)
     189             :     {
     190        1056 :         if (!pSrcMask[i])
     191             :         {
     192         256 :             pLine[i] = nodataValue;
     193             :         }
     194             :     }
     195          48 : }
     196             : 
     197             : /************************************************************************/
     198             : /*                        LERCBand::IReadBlock()                        */
     199             : /************************************************************************/
     200             : 
     201        1346 : CPLErr LERCBand::IReadBlock(int, int nBlockYOff, void *pData)
     202             : {
     203        1346 :     auto poGDS = cpl::down_cast<LERCDataset *>(poDS);
     204        1346 :     const unsigned char *pabyImage = poGDS->GetDecodedImage();
     205        1346 :     if (!pabyImage)
     206           0 :         return CE_Failure;
     207             : 
     208        1346 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     209        4038 :     GDALCopyWords64(
     210        2692 :         pabyImage + ((nBand - 1) + static_cast<size_t>(nBlockYOff) *
     211        2692 :                                        nRasterXSize * poGDS->GetRasterCount()) *
     212        1346 :                         nDTSize,
     213        1346 :         eDataType, poGDS->GetRasterCount() * nDTSize, pData, eDataType, nDTSize,
     214        1346 :         nRasterXSize);
     215             : 
     216        1346 :     if (poGDS->m_bUseNoData &&
     217        1322 :         (eDataType == GDT_Float32 || eDataType == GDT_Float64) &&
     218          48 :         poGDS->m_nMaskCount > 0)
     219             :     {
     220          48 :         const unsigned char *pabyMask = poGDS->GetDecodedMask();
     221          48 :         if (pabyMask)
     222             :         {
     223          48 :             const unsigned char *pSrcMask =
     224             :                 pabyMask +
     225          48 :                 static_cast<size_t>(poGDS->m_nMaskCount == 1 ? 0 : nBand - 1) *
     226          48 :                     nRasterXSize * nRasterYSize +
     227          48 :                 static_cast<size_t>(nBlockYOff) * nRasterXSize;
     228          48 :             if (eDataType == GDT_Float32)
     229             :             {
     230          24 :                 SetNodataWhereMaskIsInvalid(
     231             :                     static_cast<float *>(pData), pSrcMask, nRasterXSize,
     232          24 :                     static_cast<float>(poGDS->m_dfNoDataValue));
     233             :             }
     234             :             else
     235             :             {
     236          24 :                 SetNodataWhereMaskIsInvalid(static_cast<double *>(pData),
     237             :                                             pSrcMask, nRasterXSize,
     238             :                                             poGDS->m_dfNoDataValue);
     239             :             }
     240             :         }
     241             :     }
     242             : 
     243        1346 :     return CE_None;
     244             : }
     245             : 
     246             : /************************************************************************/
     247             : /*                      LERCBand::GetNoDataValue()                      */
     248             : /************************************************************************/
     249             : 
     250          17 : double LERCBand::GetNoDataValue(int *pbHasNoData)
     251             : {
     252          17 :     auto poGDS = cpl::down_cast<LERCDataset *>(poDS);
     253          17 :     if (poGDS->m_bUseNoData &&
     254          16 :         (eDataType == GDT_Float32 || eDataType == GDT_Float64) &&
     255           2 :         poGDS->m_nMaskCount > 0)
     256             :     {
     257           2 :         if (pbHasNoData)
     258           2 :             *pbHasNoData = true;
     259           2 :         return std::numeric_limits<double>::quiet_NaN();
     260             :     }
     261          15 :     if (pbHasNoData)
     262          15 :         *pbHasNoData = false;
     263          15 :     return 0;
     264             : }
     265             : 
     266             : /************************************************************************/
     267             : /*                       LERCBand::GetMaskFlags()                       */
     268             : /************************************************************************/
     269             : 
     270          10 : int LERCBand::GetMaskFlags()
     271             : {
     272          10 :     auto poGDS = cpl::down_cast<LERCDataset *>(poDS);
     273          10 :     if (poGDS->m_bUseNoData &&
     274           9 :         (eDataType == GDT_Float32 || eDataType == GDT_Float64) &&
     275           2 :         poGDS->m_nMaskCount > 0)
     276             :     {
     277           2 :         return GMF_NODATA;
     278             :     }
     279           8 :     if (poGDS->m_nMaskCount == 0)
     280           7 :         return GMF_ALL_VALID;
     281           1 :     if (poGDS->m_nMaskCount == 1)
     282           1 :         return GMF_PER_DATASET;
     283           0 :     return 0;
     284             : }
     285             : 
     286             : /************************************************************************/
     287             : /*                       LERCBand::GetMaskBand()                        */
     288             : /************************************************************************/
     289             : 
     290          13 : GDALRasterBand *LERCBand::GetMaskBand()
     291             : {
     292          13 :     auto poGDS = cpl::down_cast<LERCDataset *>(poDS);
     293          13 :     if (poGDS->m_nMaskCount == 0)
     294           7 :         return GDALPamRasterBand::GetMaskBand();
     295           6 :     if (!m_poMaskBand)
     296           3 :         m_poMaskBand = std::make_unique<LERCMaskBand>(
     297           6 :             poGDS, poGDS->m_nMaskCount == 1 ? 0 : nBand - 1);
     298           6 :     return m_poMaskBand.get();
     299             : }
     300             : 
     301             : /************************************************************************/
     302             : /*                      LERCMaskBand::IReadBlock()                      */
     303             : /************************************************************************/
     304             : 
     305          72 : CPLErr LERCMaskBand::IReadBlock(int, int nBlockYOff, void *pData)
     306             : {
     307          72 :     auto poGDS = cpl::down_cast<LERCDataset *>(poDS);
     308          72 :     const unsigned char *pabyMask = poGDS->GetDecodedMask();
     309          72 :     if (!pabyMask)
     310           0 :         return CE_Failure;
     311             : 
     312          72 :     const unsigned char *pSrc =
     313             :         pabyMask +
     314          72 :         static_cast<size_t>(m_nMaskIdx) * nRasterXSize * nRasterYSize +
     315          72 :         static_cast<size_t>(nBlockYOff) * nRasterXSize;
     316          72 :     unsigned char *pDst = static_cast<unsigned char *>(pData);
     317        1656 :     for (int i = 0; i < nRasterXSize; ++i)
     318             :     {
     319        1584 :         pDst[i] = pSrc[i] ? 255 : 0;
     320             :     }
     321             : 
     322          72 :     return CE_None;
     323             : }
     324             : 
     325             : /************************************************************************/
     326             : /*                         LERCDataset::Open()                          */
     327             : /************************************************************************/
     328             : 
     329          11 : GDALDataset *LERCDataset::Open(GDALOpenInfo *poOpenInfo)
     330             : {
     331          11 :     if (poOpenInfo->eAccess == GA_Update || poOpenInfo->fpL == nullptr)
     332           0 :         return nullptr;
     333             : 
     334             :     VSIStatBufL sStat;
     335          11 :     if (VSIStatL(poOpenInfo->pszFilename, &sStat) != 0)
     336           0 :         return nullptr;
     337             : 
     338          11 :     const unsigned nBlobSize = static_cast<unsigned>(std::min<uint64_t>(
     339          11 :         sStat.st_size, std::numeric_limits<unsigned>::max()));
     340          11 :     if (nBlobSize != static_cast<uint64_t>(sStat.st_size))
     341             :     {
     342           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Too large file");
     343           0 :         return nullptr;
     344             :     }
     345             : 
     346          11 :     const auto nRAMSize = CPLGetUsablePhysicalRAM();
     347          11 :     if (nRAMSize > 0 && sStat.st_size > nRAMSize)
     348             :     {
     349           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     350             :                  "Too large file compared to usable RAM");
     351           0 :         return nullptr;
     352             :     }
     353             :     std::unique_ptr<unsigned char, VSIFreeReleaser> pabyBlob(
     354          22 :         static_cast<unsigned char *>(VSI_MALLOC_VERBOSE(nBlobSize)));
     355          11 :     if (!pabyBlob)
     356           0 :         return nullptr;
     357          22 :     if (poOpenInfo->fpL->Seek(0, SEEK_SET) != 0 ||
     358          11 :         poOpenInfo->fpL->Read(pabyBlob.get(), nBlobSize) != nBlobSize)
     359             :     {
     360           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot read file");
     361           0 :         return nullptr;
     362             :     }
     363             : 
     364             :     unsigned int infoArray[9];
     365             :     /* Info returned in infoArray is { version, dataType, nDim/nDepth, nCols,
     366             :         nRows, nBands, nValidPixels, blobSize,
     367             :         and starting with liblerc 3.0 nRequestedMasks } */
     368             : 
     369             :     double arrayRange[3];
     370             :     /* Info returned in infoArray is { zMin, zMax, maxZErrUsed } */
     371             : 
     372             :     int lerc_ret =
     373          11 :         lerc_getBlobInfo(pabyBlob.get(), nBlobSize, infoArray, arrayRange,
     374          11 :                          CPL_ARRAYSIZE(infoArray), CPL_ARRAYSIZE(arrayRange));
     375          11 :     if (lerc_ret != 0)
     376             :     {
     377           0 :         CPLError(CE_Failure, CPLE_AppDefined, "lerc_getBlobInfo() failed");
     378           0 :         return nullptr;
     379             :     }
     380             : 
     381          11 :     GDALDataType eDT = GDT_Unknown;
     382          11 :     const unsigned nLercDataType = infoArray[1];
     383          11 :     switch (nLercDataType)
     384             :     {
     385           1 :         case 0:
     386           1 :             eDT = GDT_Int8;
     387           1 :             break;
     388           3 :         case 1:
     389           3 :             eDT = GDT_UInt8;
     390           3 :             break;
     391           1 :         case 2:
     392           1 :             eDT = GDT_Int16;
     393           1 :             break;
     394           1 :         case 3:
     395           1 :             eDT = GDT_UInt16;
     396           1 :             break;
     397           1 :         case 4:
     398           1 :             eDT = GDT_Int32;
     399           1 :             break;
     400           1 :         case 5:
     401           1 :             eDT = GDT_UInt32;
     402           1 :             break;
     403           2 :         case 6:
     404           2 :             eDT = GDT_Float32;
     405           2 :             break;
     406           1 :         case 7:
     407           1 :             eDT = GDT_Float64;
     408           1 :             break;
     409           0 :         default:
     410           0 :             CPLError(CE_Failure, CPLE_NotSupported,
     411             :                      "Unhandled LERC data type %u", nLercDataType);
     412           0 :             return nullptr;
     413             :     }
     414             : 
     415          11 :     const unsigned nDepth = infoArray[2];
     416          11 :     const unsigned nCols = infoArray[3];
     417          11 :     const unsigned nRows = infoArray[4];
     418          11 :     const unsigned nLercBands = infoArray[5];
     419          11 :     if (nCols == 0 || nRows == 0 || nDepth == 0 || nLercBands == 0 ||
     420          11 :         nCols > static_cast<unsigned>(INT_MAX) ||
     421          11 :         nRows > static_cast<unsigned>(INT_MAX) ||
     422          11 :         nDepth > static_cast<unsigned>(INT_MAX) ||
     423          11 :         nLercBands > static_cast<unsigned>(INT_MAX))
     424             :     {
     425           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     426             :                  "Invalid dimensions: cols=%u x rows=%u x depth=%u x bands=%u",
     427             :                  nCols, nRows, nDepth, nLercBands);
     428           0 :         return nullptr;
     429             :     }
     430          11 :     const int nDTSize = std::max(1, GDALGetDataTypeSizeBytes(eDT));
     431             :     // nCols * nRows * nDepth limitation is due to liblerc assuming it fits on int
     432             :     // Cf third_party/LercLib/Lerc.cpp#L381
     433          11 :     if (nCols > std::numeric_limits<int>::max() / nRows ||
     434          11 :         nCols * nRows > std::numeric_limits<int>::max() / nDepth ||
     435          11 :         nDepth > static_cast<unsigned>(std::numeric_limits<int>::max()) /
     436          11 :                      nLercBands ||
     437          11 :         static_cast<size_t>(nCols) * nRows >
     438          11 :             (std::numeric_limits<size_t>::max() / 2) / nDTSize ||
     439          11 :         static_cast<size_t>(nCols) * nRows * nDTSize >
     440          11 :             (std::numeric_limits<size_t>::max() / 2) /
     441          33 :                 (static_cast<size_t>(nDepth) * nLercBands) ||
     442          11 :         (nRAMSize > 0 &&
     443          11 :          static_cast<GIntBig>(nCols) * nRows * nDTSize * nDepth * nLercBands >
     444          11 :              nRAMSize - sStat.st_size))
     445             :     {
     446           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     447             :                  "Too large dimensions cols=%u x rows=%u x depth=%u x bands=%u",
     448             :                  nCols, nRows, nDepth, nLercBands);
     449           0 :         return nullptr;
     450             :     }
     451             : 
     452          11 :     const int nGDALBands =
     453          11 :         static_cast<int>(nDepth) * static_cast<int>(nLercBands);
     454             : 
     455          11 :     if (nLercBands != 1)
     456             :     {
     457           0 :         CPLError(CE_Failure, CPLE_NotSupported,
     458             :                  "LercBands=%u != 1 not supported", nLercBands);
     459           0 :         return nullptr;
     460             :     }
     461             : 
     462          22 :     auto poDS = std::make_unique<LERCDataset>();
     463          11 :     poDS->nRasterXSize = static_cast<int>(nCols);
     464          11 :     poDS->nRasterYSize = static_cast<int>(nRows);
     465          22 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
     466          11 :         !GDALCheckBandCount(nGDALBands, /* bIsZeroAllowed =*/false))
     467             :     {
     468           0 :         return nullptr;
     469             :     }
     470             : 
     471          11 :     poDS->m_nLercDataType = nLercDataType;
     472          11 :     poDS->m_nBlobSize = nBlobSize;
     473          11 :     poDS->m_nDepth = static_cast<int>(nDepth);
     474          11 :     poDS->m_nLercBands = static_cast<int>(nLercBands);
     475             :     // Use by WMS driver in MRF/LERC mode
     476          11 :     const char *pszNDV = CSLFetchNameValue(poOpenInfo->papszOpenOptions, "NDV");
     477          11 :     if (pszNDV)
     478             :     {
     479           1 :         if (EQUAL(pszNDV, "none"))
     480           1 :             poDS->m_bUseNoData = false;
     481             :         else
     482           0 :             poDS->m_dfNoDataValue = CPLAtof(pszNDV);
     483             :     }
     484             : #if LERC_AT_LEAST_VERSION(3, 0, 0)
     485             :     poDS->m_nMaskCount = static_cast<int>(infoArray[8]);
     486             :     if (poDS->m_nMaskCount != 0 && poDS->m_nMaskCount != nGDALBands)
     487             :     {
     488             :         CPLError(CE_Failure, CPLE_NotSupported,
     489             :                  "Mask count=%d != 0 and != band count=%d not supported",
     490             :                  poDS->m_nMaskCount, nGDALBands);
     491             :         return nullptr;
     492             :     }
     493             : #else
     494          11 :     const unsigned nValidPixels = infoArray[6];
     495          11 :     const size_t nTotalPixels = static_cast<size_t>(nCols) * nRows;
     496          22 :     if (nTotalPixels <= std::numeric_limits<unsigned>::max() &&
     497          11 :         nValidPixels < nTotalPixels)
     498           3 :         poDS->m_nMaskCount = 1;
     499             : #endif
     500          11 :     poDS->m_pabyBlob = std::move(pabyBlob);
     501             : 
     502          24 :     for (int i = 0; i < nGDALBands; ++i)
     503             :     {
     504          13 :         poDS->SetBand(i + 1, std::make_unique<LERCBand>(poDS.get(), i, eDT));
     505             :     }
     506             : 
     507          11 :     if (nGDALBands > 1)
     508             :     {
     509           1 :         poDS->GDALDataset::SetMetadataItem("INTERLEAVE", "PIXEL",
     510             :                                            "IMAGE_STRUCTURE");
     511             :     }
     512          11 :     poDS->GDALDataset::SetMetadataItem("COMPRESSION", "LERC",
     513             :                                        "IMAGE_STRUCTURE");
     514          11 :     poDS->GDALDataset::SetMetadataItem(
     515             :         "MAX_Z_ERROR", CPLSPrintf("%g", arrayRange[2]), "IMAGE_STRUCTURE");
     516             : 
     517             :     // Initialize any PAM information.
     518          11 :     poDS->SetDescription(poOpenInfo->pszFilename);
     519          11 :     poDS->TryLoadXML(poOpenInfo->GetSiblingFiles());
     520          11 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo);
     521             : 
     522          11 :     return poDS.release();
     523             : }
     524             : 
     525             : }  // namespace gdal
     526             : 
     527             : /************************************************************************/
     528             : /*                         GDALRegister_LERC()                          */
     529             : /************************************************************************/
     530             : 
     531        2138 : void GDALRegister_LERC()
     532             : {
     533        2138 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
     534         263 :         return;
     535             : 
     536        3750 :     auto poDriver = std::make_unique<GDALDriver>();
     537        1875 :     LERCDriverSetCommonMetadata(poDriver.get());
     538             : 
     539             : #ifdef LERC_VERSION_MAJOR
     540             : #define XSTRINGIFY(X) #X
     541             : #define STRINGIFY(X) XSTRINGIFY(X)
     542             :     poDriver->SetMetadataItem(
     543             :         "LIBLERC_VERSION",
     544             :         STRINGIFY(LERC_VERSION_MAJOR) "." STRINGIFY(
     545             :             LERC_VERSION_MINOR) "." STRINGIFY(LERC_VERSION_PATCH));
     546             : #endif
     547             : 
     548        1875 :     poDriver->pfnOpen = gdal::LERCDataset::Open;
     549        1875 :     GetGDALDriverManager()->RegisterDriver(poDriver.release());
     550             : }

Generated by: LCOV version 1.14