LCOV - code coverage report
Current view: top level - frmts/pds - vicardataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1249 1492 83.7 %
Date: 2025-09-10 17:48:50 Functions: 53 55 96.4 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  VICAR Driver; JPL/MIPL VICAR Format
       4             :  * Purpose:  Implementation of VICARDataset
       5             :  * Author:   Sebastian Walter <sebastian dot walter at fu-berlin dot de>
       6             :  *
       7             :  * NOTE: This driver code is loosely based on the ISIS and PDS drivers.
       8             :  * It is not intended to diminish the contribution of the original authors
       9             :  ******************************************************************************
      10             :  * Copyright (c) 2014, Sebastian Walter <sebastian dot walter at fu-berlin dot
      11             :  *de>
      12             :  *
      13             :  * SPDX-License-Identifier: MIT
      14             :  ****************************************************************************/
      15             : 
      16             : constexpr int VICAR_NULL1 = 0;
      17             : constexpr int VICAR_NULL2 = -32768;
      18             : constexpr double VICAR_NULL3 = -32768.0;
      19             : 
      20             : #include "cpl_port.h"
      21             : 
      22             : #include "cpl_safemaths.hpp"
      23             : #include "cpl_vax.h"
      24             : #include "cpl_vsi_error.h"
      25             : #include "vicardataset.h"
      26             : #include "nasakeywordhandler.h"
      27             : #include "vicarkeywordhandler.h"
      28             : #include "pdsdrivercore.h"
      29             : #include "json_utils.h"
      30             : 
      31             : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
      32             : #include "gtiff.h"
      33             : #include "geotiff.h"
      34             : #include "tifvsi.h"
      35             : #include "xtiffio.h"
      36             : #include "gt_wkt_srs_priv.h"
      37             : #endif
      38             : 
      39             : #include <exception>
      40             : #include <limits>
      41             : #include <string>
      42             : 
      43             : #ifdef EMBED_RESOURCE_FILES
      44             : #include "embedded_resources.h"
      45             : #endif
      46             : 
      47             : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
      48             : /* GeoTIFF 1.0 geokeys */
      49             : 
      50             : static const geokey_t GTiffAsciiKeys[] = {GTCitationGeoKey, GeogCitationGeoKey,
      51             :                                           PCSCitationGeoKey,
      52             :                                           VerticalCitationGeoKey};
      53             : 
      54             : static const geokey_t GTiffDoubleKeys[] = {
      55             :     GeogInvFlatteningGeoKey,      GeogSemiMajorAxisGeoKey,
      56             :     GeogSemiMinorAxisGeoKey,      ProjAzimuthAngleGeoKey,
      57             :     ProjCenterLatGeoKey,          ProjCenterLongGeoKey,
      58             :     ProjFalseEastingGeoKey,       ProjFalseNorthingGeoKey,
      59             :     ProjFalseOriginEastingGeoKey, ProjFalseOriginLatGeoKey,
      60             :     ProjFalseOriginLongGeoKey,    ProjFalseOriginNorthingGeoKey,
      61             :     ProjLinearUnitSizeGeoKey,     ProjNatOriginLatGeoKey,
      62             :     ProjNatOriginLongGeoKey,      ProjOriginLatGeoKey,
      63             :     ProjOriginLongGeoKey,         ProjRectifiedGridAngleGeoKey,
      64             :     ProjScaleAtNatOriginGeoKey,   ProjScaleAtOriginGeoKey,
      65             :     ProjStdParallel1GeoKey,       ProjStdParallel2GeoKey,
      66             :     ProjStdParallelGeoKey,        ProjStraightVertPoleLongGeoKey,
      67             :     GeogLinearUnitSizeGeoKey,     GeogAngularUnitSizeGeoKey,
      68             :     GeogPrimeMeridianLongGeoKey,  ProjCenterEastingGeoKey,
      69             :     ProjCenterNorthingGeoKey,     ProjScaleAtCenterGeoKey};
      70             : 
      71             : static const geokey_t GTiffShortKeys[] = {
      72             :     GTModelTypeGeoKey,      GTRasterTypeGeoKey,      GeogAngularUnitsGeoKey,
      73             :     GeogEllipsoidGeoKey,    GeogGeodeticDatumGeoKey, GeographicTypeGeoKey,
      74             :     ProjCoordTransGeoKey,   ProjLinearUnitsGeoKey,   ProjectedCSTypeGeoKey,
      75             :     ProjectionGeoKey,       GeogPrimeMeridianGeoKey, GeogLinearUnitsGeoKey,
      76             :     GeogAzimuthUnitsGeoKey, VerticalCSTypeGeoKey,    VerticalDatumGeoKey,
      77             :     VerticalUnitsGeoKey};
      78             : #endif
      79             : 
      80             : /************************************************************************/
      81             : /*                     OGRVICARBinaryPrefixesLayer                      */
      82             : /************************************************************************/
      83             : 
      84             : class OGRVICARBinaryPrefixesLayer final : public OGRLayer
      85             : {
      86             :     VSILFILE *m_fp = nullptr;
      87             :     OGRFeatureDefn *m_poFeatureDefn = nullptr;
      88             :     int m_iRecord = 0;
      89             :     int m_nRecords = 0;
      90             :     vsi_l_offset m_nFileOffset = 0;
      91             :     vsi_l_offset m_nStride = 0;
      92             :     bool m_bError = false;
      93             :     bool m_bByteSwapIntegers = false;
      94             :     RawRasterBand::ByteOrder m_eBREALByteOrder{};
      95             : 
      96             :     enum Type
      97             :     {
      98             :         FIELD_UNKNOWN,
      99             :         FIELD_UNSIGNED_CHAR,
     100             :         FIELD_UNSIGNED_SHORT,
     101             :         FIELD_UNSIGNED_INT,
     102             :         FIELD_SHORT,
     103             :         FIELD_INT,
     104             :         FIELD_FLOAT,
     105             :         FIELD_DOUBLE,
     106             :     };
     107             : 
     108             :     static Type GetTypeFromString(const char *pszStr);
     109             : 
     110             :     struct Field
     111             :     {
     112             :         int nOffset;
     113             :         Type eType;
     114             :     };
     115             : 
     116             :     std::vector<Field> m_aoFields{};
     117             :     std::vector<GByte> m_abyRecord{};
     118             : 
     119             :     CPL_DISALLOW_COPY_ASSIGN(OGRVICARBinaryPrefixesLayer)
     120             : 
     121             :     OGRFeature *GetNextRawFeature();
     122             : 
     123             :   public:
     124             :     OGRVICARBinaryPrefixesLayer(VSILFILE *fp, int nRecords,
     125             :                                 const CPLJSONObject &oDef,
     126             :                                 vsi_l_offset nFileOffset, vsi_l_offset nStride,
     127             :                                 RawRasterBand::ByteOrder eBINTByteOrder,
     128             :                                 RawRasterBand::ByteOrder eBREALByteOrder);
     129             :     ~OGRVICARBinaryPrefixesLayer() override;
     130             : 
     131           2 :     bool HasError() const
     132             :     {
     133           2 :         return m_bError;
     134             :     }
     135             : 
     136           1 :     void ResetReading() override
     137             :     {
     138           1 :         m_iRecord = 0;
     139           1 :     }
     140             : 
     141           0 :     const OGRFeatureDefn *GetLayerDefn() const override
     142             :     {
     143           0 :         return m_poFeatureDefn;
     144             :     }
     145             : 
     146             :     OGRFeature *GetNextFeature() override;
     147             : 
     148           1 :     int TestCapability(const char *) const override
     149             :     {
     150           1 :         return false;
     151             :     }
     152             : };
     153             : 
     154             : /************************************************************************/
     155             : /*                       GetTypeFromString()                            */
     156             : /************************************************************************/
     157             : 
     158             : OGRVICARBinaryPrefixesLayer::Type
     159          16 : OGRVICARBinaryPrefixesLayer::GetTypeFromString(const char *pszStr)
     160             : {
     161          16 :     if (EQUAL(pszStr, "unsigned char") || EQUAL(pszStr, "unsigned byte"))
     162           2 :         return FIELD_UNSIGNED_CHAR;
     163          14 :     if (EQUAL(pszStr, "unsigned short"))
     164           2 :         return FIELD_UNSIGNED_SHORT;
     165          12 :     if (EQUAL(pszStr, "unsigned int"))
     166           4 :         return FIELD_UNSIGNED_INT;
     167           8 :     if (EQUAL(pszStr, "short"))
     168           2 :         return FIELD_SHORT;
     169           6 :     if (EQUAL(pszStr, "int"))
     170           2 :         return FIELD_INT;
     171           4 :     if (EQUAL(pszStr, "float"))
     172           2 :         return FIELD_FLOAT;
     173           2 :     if (EQUAL(pszStr, "double"))
     174           2 :         return FIELD_DOUBLE;
     175           0 :     return FIELD_UNKNOWN;
     176             : }
     177             : 
     178             : /************************************************************************/
     179             : /*                     OGRVICARBinaryPrefixesLayer()                    */
     180             : /************************************************************************/
     181             : 
     182           2 : OGRVICARBinaryPrefixesLayer::OGRVICARBinaryPrefixesLayer(
     183             :     VSILFILE *fp, int nRecords, const CPLJSONObject &oDef,
     184             :     vsi_l_offset nFileOffset, vsi_l_offset nStride,
     185             :     RawRasterBand::ByteOrder eBINTByteOrder,
     186           2 :     RawRasterBand::ByteOrder eBREALByteOrder)
     187             :     : m_fp(fp), m_nRecords(nRecords), m_nFileOffset(nFileOffset),
     188             :       m_nStride(nStride),
     189             : #ifdef CPL_LSB
     190           2 :       m_bByteSwapIntegers(eBINTByteOrder !=
     191             :                           RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN),
     192             : #else
     193             :       m_bByteSwapIntegers(eBINTByteOrder !=
     194             :                           RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN),
     195             : #endif
     196           2 :       m_eBREALByteOrder(eBREALByteOrder)
     197             : {
     198           2 :     m_poFeatureDefn = new OGRFeatureDefn("binary_prefixes");
     199           2 :     SetDescription(m_poFeatureDefn->GetName());
     200           2 :     m_poFeatureDefn->Reference();
     201           2 :     m_poFeatureDefn->SetGeomType(wkbNone);
     202           2 :     int nRecordSize = oDef.GetInteger("size");
     203           4 :     const auto oFields = oDef.GetObj("fields");
     204           2 :     if (oFields.IsValid() && oFields.GetType() == CPLJSONObject::Type::Array)
     205             :     {
     206           2 :         auto oFieldsArray = oFields.ToArray();
     207           2 :         int nOffset = 0;
     208          18 :         for (int i = 0; i < oFieldsArray.Size(); i++)
     209             :         {
     210          16 :             auto oField = oFieldsArray[i];
     211          16 :             if (oField.GetType() == CPLJSONObject::Type::Object)
     212             :             {
     213          32 :                 auto osName = oField.GetString("name");
     214          32 :                 auto osType = oField.GetString("type");
     215          16 :                 auto bHidden = oField.GetBool("hidden");
     216          16 :                 auto eType = GetTypeFromString(osType.c_str());
     217          16 :                 if (eType == FIELD_UNKNOWN)
     218             :                 {
     219           0 :                     CPLError(CE_Failure, CPLE_NotSupported,
     220             :                              "Field %s of type %s not supported",
     221             :                              osName.c_str(), osType.c_str());
     222           0 :                     m_bError = true;
     223           0 :                     return;
     224             :                 }
     225          16 :                 else if (!osName.empty())
     226             :                 {
     227          16 :                     OGRFieldType eFieldType(OFTMaxType);
     228             :                     Field f;
     229          16 :                     f.nOffset = nOffset;
     230          16 :                     f.eType = eType;
     231          16 :                     switch (eType)
     232             :                     {
     233           2 :                         case FIELD_UNSIGNED_CHAR:
     234           2 :                             nOffset += 1;
     235           2 :                             eFieldType = OFTInteger;
     236           2 :                             break;
     237           2 :                         case FIELD_UNSIGNED_SHORT:
     238           2 :                             nOffset += 2;
     239           2 :                             eFieldType = OFTInteger;
     240           2 :                             break;
     241           4 :                         case FIELD_UNSIGNED_INT:
     242           4 :                             nOffset += 4;
     243           4 :                             eFieldType = OFTInteger64;
     244           4 :                             break;
     245           2 :                         case FIELD_SHORT:
     246           2 :                             nOffset += 2;
     247           2 :                             eFieldType = OFTInteger;
     248           2 :                             break;
     249           2 :                         case FIELD_INT:
     250           2 :                             nOffset += 4;
     251           2 :                             eFieldType = OFTInteger;
     252           2 :                             break;
     253           2 :                         case FIELD_FLOAT:
     254           2 :                             nOffset += 4;
     255           2 :                             eFieldType = OFTReal;
     256           2 :                             break;
     257           2 :                         case FIELD_DOUBLE:
     258           2 :                             nOffset += 8;
     259           2 :                             eFieldType = OFTReal;
     260           2 :                             break;
     261           0 :                         default:
     262           0 :                             CPLAssert(false);
     263             :                             break;
     264             :                     }
     265          16 :                     if (nOffset > nRecordSize)
     266             :                     {
     267           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     268             :                                  "Field definitions not consistent with "
     269             :                                  "declared record size");
     270           0 :                         m_bError = true;
     271           0 :                         return;
     272             :                     }
     273          16 :                     if (!bHidden)
     274             :                     {
     275          14 :                         m_aoFields.push_back(f);
     276          28 :                         OGRFieldDefn oFieldDefn(osName.c_str(), eFieldType);
     277          14 :                         m_poFeatureDefn->AddFieldDefn(&oFieldDefn);
     278             :                     }
     279             :                 }
     280             :                 else
     281             :                 {
     282           0 :                     m_bError = true;
     283             :                 }
     284             :             }
     285             :             else
     286             :             {
     287           0 :                 m_bError = true;
     288             :             }
     289          16 :             if (m_bError)
     290             :             {
     291           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     292             :                          "Error while reading binary prefix definition");
     293           0 :                 return;
     294             :             }
     295             :         }
     296             :     }
     297           2 :     m_abyRecord.resize(nRecordSize);
     298             : }
     299             : 
     300             : /************************************************************************/
     301             : /*                    ~OGRVICARBinaryPrefixesLayer()                    */
     302             : /************************************************************************/
     303             : 
     304           4 : OGRVICARBinaryPrefixesLayer::~OGRVICARBinaryPrefixesLayer()
     305             : {
     306           2 :     m_poFeatureDefn->Release();
     307           4 : }
     308             : 
     309             : /************************************************************************/
     310             : /*                         GetNextRawFeature()                          */
     311             : /************************************************************************/
     312             : 
     313           3 : OGRFeature *OGRVICARBinaryPrefixesLayer::GetNextRawFeature()
     314             : {
     315           3 :     if (m_iRecord >= m_nRecords)
     316           1 :         return nullptr;
     317             : 
     318           4 :     if (VSIFSeekL(m_fp, m_nFileOffset + m_iRecord * m_nStride, SEEK_SET) != 0 ||
     319           2 :         VSIFReadL(&m_abyRecord[0], m_abyRecord.size(), 1, m_fp) != 1)
     320             :     {
     321           0 :         return nullptr;
     322             :     }
     323             : 
     324           2 :     OGRFeature *poFeature = new OGRFeature(m_poFeatureDefn);
     325          16 :     for (int i = 0; i < poFeature->GetFieldCount(); i++)
     326             :     {
     327          14 :         int nOffset = m_aoFields[i].nOffset;
     328          14 :         switch (m_aoFields[i].eType)
     329             :         {
     330           2 :             case FIELD_UNSIGNED_CHAR:
     331           2 :                 poFeature->SetField(i, m_abyRecord[nOffset]);
     332           2 :                 break;
     333           2 :             case FIELD_UNSIGNED_SHORT:
     334             :             {
     335             :                 unsigned short v;
     336           2 :                 memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
     337           2 :                 if (m_bByteSwapIntegers)
     338             :                 {
     339           0 :                     CPL_SWAP16PTR(&v);
     340             :                 }
     341           2 :                 poFeature->SetField(i, v);
     342           2 :                 break;
     343             :             }
     344           2 :             case FIELD_UNSIGNED_INT:
     345             :             {
     346             :                 unsigned int v;
     347           2 :                 memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
     348           2 :                 if (m_bByteSwapIntegers)
     349             :                 {
     350           0 :                     CPL_SWAP32PTR(&v);
     351             :                 }
     352           2 :                 poFeature->SetField(i, static_cast<GIntBig>(v));
     353           2 :                 break;
     354             :             }
     355           2 :             case FIELD_SHORT:
     356             :             {
     357             :                 short v;
     358           2 :                 memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
     359           2 :                 if (m_bByteSwapIntegers)
     360             :                 {
     361           0 :                     CPL_SWAP16PTR(&v);
     362             :                 }
     363           2 :                 poFeature->SetField(i, v);
     364           2 :                 break;
     365             :             }
     366           2 :             case FIELD_INT:
     367             :             {
     368             :                 int v;
     369           2 :                 memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
     370           2 :                 if (m_bByteSwapIntegers)
     371             :                 {
     372           0 :                     CPL_SWAP32PTR(&v);
     373             :                 }
     374           2 :                 poFeature->SetField(i, v);
     375           2 :                 break;
     376             :             }
     377           2 :             case FIELD_FLOAT:
     378             :             {
     379             :                 float v;
     380           2 :                 memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
     381           2 :                 if (m_eBREALByteOrder == RawRasterBand::ByteOrder::ORDER_VAX)
     382             :                 {
     383           0 :                     CPLVaxToIEEEFloat(&v);
     384             :                 }
     385           2 :                 else if (m_eBREALByteOrder !=
     386             : #ifdef CPL_LSB
     387             :                          RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
     388             : #else
     389             :                          RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN
     390             : #endif
     391             :                 )
     392             :                 {
     393           0 :                     CPL_SWAP32PTR(&v);
     394             :                 }
     395           2 :                 poFeature->SetField(i, v);
     396           2 :                 break;
     397             :             }
     398           2 :             case FIELD_DOUBLE:
     399             :             {
     400             :                 double v;
     401           2 :                 memcpy(&v, &m_abyRecord[nOffset], sizeof(v));
     402           2 :                 if (m_eBREALByteOrder == RawRasterBand::ByteOrder::ORDER_VAX)
     403             :                 {
     404           0 :                     CPLVaxToIEEEDouble(&v);
     405             :                 }
     406           2 :                 else if (m_eBREALByteOrder !=
     407             : #ifdef CPL_LSB
     408             :                          RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
     409             : #else
     410             :                          RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN
     411             : #endif
     412             :                 )
     413             :                 {
     414           0 :                     CPL_SWAP64PTR(&v);
     415             :                 }
     416           2 :                 poFeature->SetField(i, v);
     417           2 :                 break;
     418             :             }
     419           0 :             default:
     420           0 :                 CPLAssert(false);
     421             :         }
     422             :     }
     423           2 :     poFeature->SetFID(m_iRecord);
     424           2 :     m_iRecord++;
     425           2 :     return poFeature;
     426             : }
     427             : 
     428             : /************************************************************************/
     429             : /*                           GetNextFeature()                           */
     430             : /************************************************************************/
     431             : 
     432           3 : OGRFeature *OGRVICARBinaryPrefixesLayer::GetNextFeature()
     433             : {
     434             :     while (true)
     435             :     {
     436           3 :         auto poFeature = GetNextRawFeature();
     437           3 :         if (poFeature == nullptr)
     438           1 :             return nullptr;
     439             : 
     440           4 :         if ((m_poFilterGeom == nullptr ||
     441           4 :              FilterGeometry(poFeature->GetGeometryRef())) &&
     442           2 :             (m_poAttrQuery == nullptr || m_poAttrQuery->Evaluate(poFeature)))
     443             :         {
     444           2 :             return poFeature;
     445             :         }
     446             :         else
     447           0 :             delete poFeature;
     448           0 :     }
     449             : }
     450             : 
     451             : /************************************************************************/
     452             : /*                         VICARRawRasterBand                           */
     453             : /************************************************************************/
     454             : 
     455             : class VICARRawRasterBand final : public RawRasterBand
     456             : {
     457             :   protected:
     458             :     friend class VICARDataset;
     459             : 
     460             :   public:
     461             :     VICARRawRasterBand(VICARDataset *poDSIn, int nBandIn, VSILFILE *fpRawIn,
     462             :                        vsi_l_offset nImgOffsetIn, int nPixelOffsetIn,
     463             :                        int nLineOffsetIn, GDALDataType eDataTypeIn,
     464             :                        ByteOrder eByteOrderIn);
     465             : 
     466             :     CPLErr IReadBlock(int, int, void *) override;
     467             :     CPLErr IWriteBlock(int, int, void *) override;
     468             : 
     469             :     CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
     470             :                      GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace,
     471             :                      GDALRasterIOExtraArg *psExtraArg) override;
     472             : };
     473             : 
     474             : /************************************************************************/
     475             : /*                        VICARRawRasterBand()                          */
     476             : /************************************************************************/
     477             : 
     478         216 : VICARRawRasterBand::VICARRawRasterBand(VICARDataset *poDSIn, int nBandIn,
     479             :                                        VSILFILE *fpRawIn,
     480             :                                        vsi_l_offset nImgOffsetIn,
     481             :                                        int nPixelOffsetIn, int nLineOffsetIn,
     482             :                                        GDALDataType eDataTypeIn,
     483         216 :                                        ByteOrder eByteOrderIn)
     484             :     : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
     485             :                     nLineOffsetIn, eDataTypeIn, eByteOrderIn,
     486         216 :                     RawRasterBand::OwnFP::NO)
     487             : {
     488         216 : }
     489             : 
     490             : /************************************************************************/
     491             : /*                             IReadBlock()                             */
     492             : /************************************************************************/
     493             : 
     494        2001 : CPLErr VICARRawRasterBand::IReadBlock(int nXBlock, int nYBlock, void *pImage)
     495             : 
     496             : {
     497        2001 :     VICARDataset *poGDS = cpl::down_cast<VICARDataset *>(poDS);
     498        2001 :     if (!poGDS->m_bIsLabelWritten)
     499           0 :         poGDS->WriteLabel();
     500        2001 :     return RawRasterBand::IReadBlock(nXBlock, nYBlock, pImage);
     501             : }
     502             : 
     503             : /************************************************************************/
     504             : /*                            IWriteBlock()                             */
     505             : /************************************************************************/
     506             : 
     507        2000 : CPLErr VICARRawRasterBand::IWriteBlock(int nXBlock, int nYBlock, void *pImage)
     508             : 
     509             : {
     510        2000 :     VICARDataset *poGDS = cpl::down_cast<VICARDataset *>(poDS);
     511        2000 :     if (!poGDS->m_bIsLabelWritten)
     512           0 :         poGDS->WriteLabel();
     513        2000 :     return RawRasterBand::IWriteBlock(nXBlock, nYBlock, pImage);
     514             : }
     515             : 
     516             : /************************************************************************/
     517             : /*                             IRasterIO()                              */
     518             : /************************************************************************/
     519             : 
     520         496 : CPLErr VICARRawRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     521             :                                      int nXSize, int nYSize, void *pData,
     522             :                                      int nBufXSize, int nBufYSize,
     523             :                                      GDALDataType eBufType,
     524             :                                      GSpacing nPixelSpace, GSpacing nLineSpace,
     525             :                                      GDALRasterIOExtraArg *psExtraArg)
     526             : 
     527             : {
     528         496 :     VICARDataset *poGDS = cpl::down_cast<VICARDataset *>(poDS);
     529         496 :     if (!poGDS->m_bIsLabelWritten)
     530          29 :         poGDS->WriteLabel();
     531         496 :     return RawRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     532             :                                     pData, nBufXSize, nBufYSize, eBufType,
     533         496 :                                     nPixelSpace, nLineSpace, psExtraArg);
     534             : }
     535             : 
     536             : /************************************************************************/
     537             : /*                        VICARBASICRasterBand                          */
     538             : /************************************************************************/
     539             : 
     540             : class VICARBASICRasterBand final : public GDALPamRasterBand
     541             : {
     542             :   public:
     543             :     VICARBASICRasterBand(VICARDataset *poDSIn, int nBandIn, GDALDataType eType);
     544             : 
     545             :     CPLErr IReadBlock(int, int, void *) override;
     546             :     CPLErr IWriteBlock(int, int, void *) override;
     547             : };
     548             : 
     549             : /************************************************************************/
     550             : /*                        VICARBASICRasterBand()                        */
     551             : /************************************************************************/
     552             : 
     553          21 : VICARBASICRasterBand::VICARBASICRasterBand(VICARDataset *poDSIn, int nBandIn,
     554          21 :                                            GDALDataType eType)
     555             : {
     556          21 :     poDS = poDSIn;
     557          21 :     nBand = nBandIn;
     558          21 :     nBlockXSize = poDSIn->GetRasterXSize();
     559          21 :     nBlockYSize = 1;
     560          21 :     eDataType = eType;
     561          21 : }
     562             : 
     563             : namespace
     564             : {
     565             : class DecodeEncodeException final : public std::exception
     566             : {
     567             :   public:
     568           0 :     DecodeEncodeException() = default;
     569             : };
     570             : }  // namespace
     571             : 
     572             : //////////////////////////////////////////////////////////////////////////
     573             : /// Below functions are adapted from Public Domain VICAR project
     574             : /// from
     575             : /// https://github.com/nasa/VICAR/blob/master/vos/rtl/source/basic_compression.c
     576             : //////////////////////////////////////////////////////////////////////////
     577             : 
     578             : /* masking array used in the algorithm to take out bits in memory */
     579             : const unsigned int cod1mask[25] = {
     580             :     0x0,      0x1,      0x3,      0x7,     0xf,     0x1f,    0x3f,
     581             :     0x7f,     0xff,     0x1ff,    0x3ff,   0x7ff,   0xfff,   0x1fff,
     582             :     0x3fff,   0x7fff,   0xffff,   0x1ffff, 0x3ffff, 0x7ffff, 0xfffff,
     583             :     0x1fffff, 0x3fffff, 0x7fffff, 0xffffff};
     584             : 
     585             : /*****************************************************/
     586             : /* This function is a helper function for the BASIC  */
     587             : /* compression algorithm to get a specified number   */
     588             : /* of bits from buffer, convert it to a number and   */
     589             : /* return it.                                        */
     590             : /*****************************************************/
     591       11439 : static unsigned char grab1(int nbit, const unsigned char *buffer,
     592             :                            size_t buffer_size, size_t &buffer_pos,
     593             :                            int &bit1ptr) /* bit position in the current byte of
     594             :                                             encrypted or decrypted buffer*/
     595             : {
     596             :     unsigned char val;
     597       11439 :     int shift = 8 - nbit - (bit1ptr);
     598             : 
     599       11439 :     if (buffer_pos >= buffer_size)
     600             :     {
     601           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Out of decoding buffer");
     602           0 :         throw DecodeEncodeException();
     603             :     }
     604             : 
     605       11439 :     if (shift > 0)
     606             :     {
     607        5625 :         val = (buffer[buffer_pos] >> shift) & cod1mask[nbit];
     608        5625 :         bit1ptr += nbit;
     609             : 
     610        5625 :         return val;
     611             :     }
     612        5814 :     if (shift < 0)
     613             :     {
     614        3642 :         unsigned v1 = buffer[buffer_pos] & cod1mask[nbit + shift];
     615        3642 :         buffer_pos++;
     616             : 
     617        3642 :         if (buffer_pos >= buffer_size)
     618             :         {
     619           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Out of decoding buffer");
     620           0 :             throw DecodeEncodeException();
     621             :         }
     622             : 
     623        3642 :         unsigned v2 = (buffer[buffer_pos] >> (8 + shift)) & cod1mask[-shift];
     624             : 
     625        3642 :         val = static_cast<unsigned char>((v1 << (-shift)) + v2);
     626             : 
     627        3642 :         bit1ptr = -shift;
     628             : 
     629        3642 :         return val;
     630             :     }
     631        2172 :     val = buffer[buffer_pos] & cod1mask[nbit];
     632        2172 :     buffer_pos++;
     633        2172 :     bit1ptr = 0;
     634             : 
     635        2172 :     return val;
     636             : }
     637             : 
     638             : /*****************************************************/
     639             : /* This function is the decoding algorithm for BASIC */
     640             : /* compression.  The encoded buffer is passed into   */
     641             : /* code and the decoded buffer is passed out in buf. */
     642             : /*****************************************************/
     643         330 : static void basic_decode(const unsigned char *code, size_t code_size,
     644             :                          unsigned char *buf, int ns, int wid)
     645             : {
     646         330 :     int runInt = -3;
     647             :     unsigned char runChar;
     648         330 :     unsigned int nval = 999999;
     649             :     static const int cmprtrns1[7] = {-3, -2, -1, 0, 1, 2, 3};
     650         330 :     size_t buffer_pos = 0;
     651         330 :     int bit1ptr = 0;
     652         330 :     unsigned int old = 0;
     653         330 :     const int ptop = ns * wid;
     654             : 
     655         720 :     for (int iw = 0; iw < wid; iw++)
     656             :     {
     657      305190 :         for (int ip = iw; ip < ptop; ip += wid)
     658             :         {
     659      304800 :             if (runInt > (-3))
     660             :             {
     661      301053 :                 buf[ip] = static_cast<unsigned char>(nval);
     662      301053 :                 runInt--;
     663      301053 :                 continue;
     664             :             }
     665        3747 :             unsigned char val = grab1(3, code, code_size, buffer_pos, bit1ptr);
     666             : 
     667        3747 :             if (val < 7)
     668             :             {
     669         459 :                 nval = CPLUnsanitizedAdd<unsigned>(old, cmprtrns1[val]);
     670         459 :                 buf[ip] = static_cast<unsigned char>(nval);
     671         459 :                 old = nval;
     672         459 :                 continue;
     673             :             }
     674        3288 :             val = grab1(1, code, code_size, buffer_pos, bit1ptr);
     675             : 
     676        3288 :             if (val)
     677             :             {
     678         228 :                 runChar = grab1(4, code, code_size, buffer_pos, bit1ptr);
     679         228 :                 if (runChar == 15)
     680             :                 {
     681         210 :                     runChar = grab1(8, code, code_size, buffer_pos, bit1ptr);
     682             : 
     683         210 :                     if (runChar == 255)
     684             :                     {
     685             :                         unsigned char part0 =
     686         150 :                             grab1(8, code, code_size, buffer_pos, bit1ptr);
     687             :                         unsigned char part1 =
     688         150 :                             grab1(8, code, code_size, buffer_pos, bit1ptr);
     689             :                         unsigned char part2 =
     690         150 :                             grab1(8, code, code_size, buffer_pos, bit1ptr);
     691         150 :                         runInt = part0 | (part1 << 8) | (part2 << 16);
     692             :                     }
     693             :                     else
     694          60 :                         runInt = runChar + 15;
     695             :                 }
     696             :                 else
     697          18 :                     runInt = runChar;
     698             : 
     699         228 :                 val = grab1(3, code, code_size, buffer_pos, bit1ptr);
     700         228 :                 if (val < 7)
     701           0 :                     nval = CPLUnsanitizedAdd<unsigned>(old, cmprtrns1[val]);
     702             :                 else
     703         228 :                     nval = grab1(8, code, code_size, buffer_pos, bit1ptr);
     704         228 :                 buf[ip] = static_cast<unsigned char>(nval);
     705         228 :                 old = nval;
     706             :             }
     707             :             else
     708             :             {
     709        3060 :                 val = grab1(8, code, code_size, buffer_pos, bit1ptr);
     710        3060 :                 buf[ip] = val;
     711        3060 :                 old = val;
     712             :             }
     713             :         }
     714             :     }
     715         330 : }
     716             : 
     717             : /*****************************************************/
     718             : /* This function is a helper function for the BASIC  */
     719             : /* encoding operation.  It puts the value in val into*/
     720             : /* memory location pointed by pcode1+reg1 into nbit  */
     721             : /* number of bits.                                   */
     722             : /*****************************************************/
     723        2717 : static void emit1(unsigned char val, int nbit, unsigned char *reg1,
     724             :                   int &bit1ptr, unsigned char *coded_buffer,
     725             :                   size_t &coded_buffer_pos, size_t coded_buffer_size)
     726             : {
     727             :     int shift;
     728             : 
     729        2717 :     shift = 8 - nbit - bit1ptr;
     730        2717 :     if (shift > 0)
     731             :     {
     732         779 :         *reg1 = static_cast<unsigned char>(*reg1 | (val << shift));
     733         779 :         bit1ptr += nbit;
     734         779 :         return;
     735             :     }
     736        1938 :     if (shift < 0)
     737             :     {
     738        1265 :         if (coded_buffer_pos >= coded_buffer_size)
     739             :         {
     740           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Out of encoding buffer");
     741           0 :             throw DecodeEncodeException();
     742             :         }
     743        1265 :         coded_buffer[coded_buffer_pos] =
     744        1265 :             static_cast<unsigned char>(*reg1 | (val >> (-shift)));
     745        1265 :         coded_buffer_pos++;
     746        1265 :         *reg1 = static_cast<unsigned char>(val << (8 + shift));
     747        1265 :         bit1ptr = -shift;
     748        1265 :         return;
     749             :     }
     750         673 :     if (coded_buffer_pos >= coded_buffer_size)
     751             :     {
     752           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Out of encoding buffer");
     753           0 :         throw DecodeEncodeException();
     754             :     }
     755         673 :     coded_buffer[coded_buffer_pos] = static_cast<unsigned char>(*reg1 | val);
     756         673 :     coded_buffer_pos++;
     757             : 
     758         673 :     *reg1 = 0;
     759         673 :     bit1ptr = 0;
     760             : }
     761             : 
     762             : /*****************************************************/
     763             : /* This function is meat of the BASIC encoding       */
     764             : /* algorithm.  This function is called repeatedly by */
     765             : /* the basic_encode function to compress the data    */
     766             : /* according to its run length (run), last 2         */
     767             : /* different values (vold and old), and the current  */
     768             : /* value (val), into the memory location pointed by  */
     769             : /* pcode1+reg1.                                      */
     770             : /*****************************************************/
     771        1096 : static void basic_encrypt(int *run, int *old, int *vold, int val,
     772             :                           unsigned char *reg1, int &bit1ptr,
     773             :                           unsigned char *coded_buffer, size_t &coded_buffer_pos,
     774             :                           size_t coded_buffer_size)
     775             : {
     776        1096 :     if (*run < 4)
     777             :     {
     778        1020 :         if (abs(*old - *vold) < 4)
     779           0 :             emit1(static_cast<unsigned char>(*old - *vold + 3), 3, reg1,
     780             :                   bit1ptr, coded_buffer, coded_buffer_pos, coded_buffer_size);
     781             :         else
     782             :         {
     783        1020 :             emit1(static_cast<unsigned char>(14), 4, reg1, bit1ptr,
     784             :                   coded_buffer, coded_buffer_pos, coded_buffer_size);
     785        1020 :             emit1(static_cast<unsigned char>(*old), 8, reg1, bit1ptr,
     786             :                   coded_buffer, coded_buffer_pos, coded_buffer_size);
     787             :         }
     788             : 
     789        1173 :         while (*run > 1)
     790             :         {
     791         153 :             emit1(static_cast<unsigned char>(3), 3, reg1, bit1ptr, coded_buffer,
     792             :                   coded_buffer_pos, coded_buffer_size);
     793         153 :             (*run)--;
     794             :         }
     795             : 
     796        1020 :         *vold = *old;
     797        1020 :         *old = val;
     798             :     }
     799             :     else
     800             :     {
     801          76 :         emit1(static_cast<unsigned char>(15), 4, reg1, bit1ptr, coded_buffer,
     802             :               coded_buffer_pos, coded_buffer_size);
     803          76 :         if (*run < 19)
     804             :         {
     805           6 :             emit1(static_cast<unsigned char>(*run - 4), 4, reg1, bit1ptr,
     806             :                   coded_buffer, coded_buffer_pos, coded_buffer_size);
     807             :         }
     808             :         else
     809             :         {
     810          70 :             emit1(static_cast<unsigned char>(15), 4, reg1, bit1ptr,
     811             :                   coded_buffer, coded_buffer_pos, coded_buffer_size);
     812          70 :             if (*run < 274)
     813             :             {
     814          20 :                 emit1(static_cast<char>(*run - 19), 8, reg1, bit1ptr,
     815             :                       coded_buffer, coded_buffer_pos, coded_buffer_size);
     816             :             }
     817             :             else
     818             :             {
     819          50 :                 emit1(static_cast<unsigned char>(255), 8, reg1, bit1ptr,
     820             :                       coded_buffer, coded_buffer_pos, coded_buffer_size);
     821             : 
     822          50 :                 unsigned char part0 =
     823          50 :                     static_cast<unsigned char>((*run - 4) & 0xff);
     824          50 :                 unsigned char part1 =
     825          50 :                     static_cast<unsigned char>(((*run - 4) >> 8) & 0xff);
     826          50 :                 unsigned char part2 =
     827          50 :                     static_cast<unsigned char>(((*run - 4) >> 16) & 0xff);
     828          50 :                 emit1(part0, 8, reg1, bit1ptr, coded_buffer, coded_buffer_pos,
     829             :                       coded_buffer_size);
     830          50 :                 emit1(part1, 8, reg1, bit1ptr, coded_buffer, coded_buffer_pos,
     831             :                       coded_buffer_size);
     832          50 :                 emit1(part2, 8, reg1, bit1ptr, coded_buffer, coded_buffer_pos,
     833             :                       coded_buffer_size);
     834             :             }
     835             :         }
     836          76 :         if (abs(*old - *vold) < 4)
     837             :         {
     838           0 :             emit1(static_cast<unsigned char>(*old - *vold + 3), 3, reg1,
     839             :                   bit1ptr, coded_buffer, coded_buffer_pos, coded_buffer_size);
     840             :         }
     841             :         else
     842             :         {
     843          76 :             emit1(static_cast<unsigned char>(7), 3, reg1, bit1ptr, coded_buffer,
     844             :                   coded_buffer_pos, coded_buffer_size);
     845          76 :             emit1(static_cast<unsigned char>(*old), 8, reg1, bit1ptr,
     846             :                   coded_buffer, coded_buffer_pos, coded_buffer_size);
     847             :         }
     848          76 :         *vold = *old;
     849          76 :         *old = val;
     850          76 :         *run = 1;
     851             :     }
     852        1096 : }
     853             : 
     854             : /*****************************************************/
     855             : /* This function loops through the data given by     */
     856             : /* unencodedBuf, keeping track of run length.  When  */
     857             : /* the value of the data changes, it passes the run  */
     858             : /* length, last 2 differing values, the current      */
     859             : /* value, and the pointer in pcode1 buffer to encode */
     860             : /* the data into, to basic_encrypt function.         */
     861             : /*****************************************************/
     862         110 : static void basic_encode(const unsigned char *unencodedBuf,
     863             :                          unsigned char *coded_buffer, size_t coded_buffer_size,
     864             :                          int ns, int wid, size_t *totBytes)
     865             : {
     866         110 :     int val = 0;
     867         110 :     int bit1ptr = 0;
     868         110 :     const int ptop = ns * wid;
     869         110 :     unsigned char reg1 = 0;
     870         110 :     int run = 0;
     871         110 :     int old = unencodedBuf[0];
     872         110 :     int vold = 999999;
     873             : 
     874         110 :     size_t coded_buffer_pos = 0;
     875             : 
     876         240 :     for (int iw = 0; iw < wid; iw++)
     877             :     {
     878      101730 :         for (int ip = iw; ip < ptop; ip += wid)
     879             :         {
     880      101600 :             val = unencodedBuf[ip];
     881             : 
     882      101600 :             if (val == old)
     883      100614 :                 run++;
     884             :             else
     885         986 :                 basic_encrypt(&run, &old, &vold, val, &reg1, bit1ptr,
     886             :                               coded_buffer, coded_buffer_pos,
     887             :                               coded_buffer_size);
     888             :         }
     889             :     }
     890             : 
     891             :     /* purge of last code */
     892         110 :     basic_encrypt(&run, &old, &vold, val, &reg1, bit1ptr, coded_buffer,
     893             :                   coded_buffer_pos, coded_buffer_size);
     894             : 
     895         110 :     if (coded_buffer_pos >= coded_buffer_size)
     896             :     {
     897           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Out of encoding buffer");
     898           0 :         throw DecodeEncodeException();
     899             :     }
     900         110 :     coded_buffer[coded_buffer_pos] = reg1;
     901             : 
     902         110 :     *totBytes = coded_buffer_pos;
     903         110 :     if (bit1ptr > 0)
     904         102 :         (*totBytes)++;
     905         110 : }
     906             : 
     907             : //////////////////////////////////////////////////////////////////////////
     908             : /// End of VICAR code
     909             : //////////////////////////////////////////////////////////////////////////
     910             : 
     911             : /************************************************************************/
     912             : /*                             IReadBlock()                             */
     913             : /************************************************************************/
     914             : 
     915         330 : CPLErr VICARBASICRasterBand::IReadBlock(int /*nXBlock*/, int nYBlock,
     916             :                                         void *pImage)
     917             : 
     918             : {
     919         330 :     VICARDataset *poGDS = cpl::down_cast<VICARDataset *>(poDS);
     920             : 
     921         330 :     const int nRecord = (nBand - 1) * nRasterYSize + nYBlock;
     922         330 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     923             : 
     924         330 :     if (poGDS->eAccess == GA_Update &&
     925           0 :         poGDS->m_anRecordOffsets[nRecord + 1] == 0)
     926             :     {
     927           0 :         memset(pImage, 0, static_cast<size_t>(nDTSize) * nRasterXSize);
     928           0 :         return CE_None;
     929             :     }
     930             : 
     931             :     // Find at which offset the compressed record is.
     932             :     // For BASIC compression, each compressed run is preceded by a uint32 value
     933             :     // givin its size, including the size of this uint32 value
     934             :     // For BASIC2 compression, the uint32 sizes of all records are put
     935             :     // immediately after the label.
     936         660 :     for (; poGDS->m_nLastRecordOffset <= nRecord; poGDS->m_nLastRecordOffset++)
     937             :     {
     938         330 :         CPLAssert(poGDS->m_anRecordOffsets[poGDS->m_nLastRecordOffset + 1] ==
     939             :                   0);
     940             : 
     941             :         int nRet;
     942         330 :         if (poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC)
     943             :         {
     944             :             nRet =
     945          80 :                 VSIFSeekL(poGDS->fpImage,
     946          80 :                           poGDS->m_anRecordOffsets[poGDS->m_nLastRecordOffset] -
     947             :                               sizeof(GUInt32),
     948             :                           SEEK_SET);
     949             :         }
     950             :         else
     951             :         {
     952         250 :             nRet = VSIFSeekL(poGDS->fpImage,
     953         250 :                              poGDS->m_nImageOffsetWithoutNBB +
     954         250 :                                  static_cast<vsi_l_offset>(sizeof(GUInt32)) *
     955         250 :                                      poGDS->m_nLastRecordOffset,
     956             :                              SEEK_SET);
     957             :         }
     958             :         GUInt32 nSize;
     959         660 :         if (nRet != 0 ||
     960         330 :             VSIFReadL(&nSize, sizeof(nSize), 1, poGDS->fpImage) != 1)
     961             :         {
     962           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot read record %d size",
     963             :                      poGDS->m_nLastRecordOffset);
     964           0 :             return CE_Failure;
     965             :         }
     966         330 :         CPL_LSBPTR32(&nSize);
     967         740 :         if ((poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC &&
     968          80 :              nSize <= sizeof(GUInt32)) ||
     969         330 :             (poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC2 &&
     970         910 :              nSize == 0) ||
     971         330 :             poGDS->m_anRecordOffsets[poGDS->m_nLastRecordOffset] >
     972         330 :                 std::numeric_limits<uint64_t>::max() - nSize)
     973             :         {
     974           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Wrong size at record %d",
     975             :                      poGDS->m_nLastRecordOffset);
     976           0 :             return CE_Failure;
     977             :         }
     978             : 
     979         330 :         poGDS->m_anRecordOffsets[poGDS->m_nLastRecordOffset + 1] =
     980         330 :             poGDS->m_anRecordOffsets[poGDS->m_nLastRecordOffset] + nSize;
     981             :     }
     982             : 
     983             :     unsigned int nSize;
     984         330 :     if (poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC)
     985             :     {
     986         160 :         nSize = static_cast<unsigned>(poGDS->m_anRecordOffsets[nRecord + 1] -
     987          80 :                                       poGDS->m_anRecordOffsets[nRecord] -
     988             :                                       sizeof(GUInt32));
     989             :     }
     990             :     else
     991             :     {
     992         500 :         nSize = static_cast<unsigned>(poGDS->m_anRecordOffsets[nRecord + 1] -
     993         250 :                                       poGDS->m_anRecordOffsets[nRecord]);
     994             :     }
     995         330 :     if (nSize > 100 * 1000 * 1000 ||
     996           0 :         (nSize > 1000 &&
     997           0 :          (nSize - 11) / 4 > static_cast<unsigned>(nRasterXSize) * nDTSize))
     998             :     {
     999           0 :         return CE_Failure;
    1000             :     }
    1001         330 :     if (poGDS->m_abyCodedBuffer.size() < nSize)
    1002             :     {
    1003             :         try
    1004             :         {
    1005          39 :             poGDS->m_abyCodedBuffer.resize(nSize);
    1006             :         }
    1007           0 :         catch (const std::exception &e)
    1008             :         {
    1009           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1010           0 :             return CE_Failure;
    1011             :         }
    1012             :     }
    1013         330 :     if (VSIFSeekL(poGDS->fpImage, poGDS->m_anRecordOffsets[nRecord],
    1014         660 :                   SEEK_SET) != 0 ||
    1015         330 :         VSIFReadL(&poGDS->m_abyCodedBuffer[0], nSize, 1, poGDS->fpImage) != 1)
    1016             :     {
    1017           0 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot read record %d", nRecord);
    1018           0 :         return CE_Failure;
    1019             :     }
    1020             : 
    1021             :     try
    1022             :     {
    1023         330 :         basic_decode(poGDS->m_abyCodedBuffer.data(), nSize,
    1024             :                      static_cast<unsigned char *>(pImage), nRasterXSize,
    1025             :                      nDTSize);
    1026             :     }
    1027           0 :     catch (const DecodeEncodeException &)
    1028             :     {
    1029           0 :         return CE_Failure;
    1030             :     }
    1031             : #ifdef CPL_MSB
    1032             :     if (nDTSize > 1)
    1033             :     {
    1034             :         GDALSwapWords(pImage, nDTSize, nRasterXSize, nDTSize);
    1035             :     }
    1036             : #endif
    1037         330 :     return CE_None;
    1038             : }
    1039             : 
    1040             : /************************************************************************/
    1041             : /*                            IWriteBlock()                             */
    1042             : /************************************************************************/
    1043             : 
    1044         111 : CPLErr VICARBASICRasterBand::IWriteBlock(int /*nXBlock*/, int nYBlock,
    1045             :                                          void *pImage)
    1046             : 
    1047             : {
    1048         111 :     VICARDataset *poGDS = cpl::down_cast<VICARDataset *>(poDS);
    1049         111 :     if (poGDS->eAccess == GA_ReadOnly)
    1050           0 :         return CE_Failure;
    1051         111 :     if (!poGDS->m_bIsLabelWritten)
    1052             :     {
    1053           5 :         poGDS->WriteLabel();
    1054           5 :         poGDS->m_nLabelSize = VSIFTellL(poGDS->fpImage);
    1055           5 :         poGDS->m_anRecordOffsets[0] = poGDS->m_nLabelSize;
    1056           5 :         if (poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC)
    1057             :         {
    1058           2 :             poGDS->m_anRecordOffsets[0] += sizeof(GUInt32);
    1059             :         }
    1060             :         else
    1061             :         {
    1062           3 :             poGDS->m_anRecordOffsets[0] +=
    1063           3 :                 static_cast<vsi_l_offset>(sizeof(GUInt32)) * nRasterYSize;
    1064             :         }
    1065             :     }
    1066         111 :     if (nYBlock != poGDS->m_nLastRecordOffset)
    1067             :     {
    1068           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1069             :                  "Lines must be written in sequential order");
    1070           1 :         return CE_Failure;
    1071             :     }
    1072             : 
    1073         110 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
    1074         110 :     const size_t nMaxEncodedSize =
    1075         110 :         static_cast<size_t>(nRasterXSize) * nDTSize +
    1076         110 :         static_cast<size_t>(nRasterXSize) * nDTSize / 2 + 11;
    1077         110 :     if (poGDS->m_abyCodedBuffer.size() < nMaxEncodedSize)
    1078             :     {
    1079             :         try
    1080             :         {
    1081           4 :             poGDS->m_abyCodedBuffer.resize(nMaxEncodedSize);
    1082             :         }
    1083           0 :         catch (const std::exception &e)
    1084             :         {
    1085           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    1086           0 :             return CE_Failure;
    1087             :         }
    1088             :     }
    1089             : 
    1090             : #ifdef CPL_MSB
    1091             :     if (nDTSize > 1)
    1092             :     {
    1093             :         GDALSwapWords(pImage, nDTSize, nRasterXSize, nDTSize);
    1094             :     }
    1095             : #endif
    1096             : 
    1097         110 :     size_t nCodedSize = 0;
    1098             :     try
    1099             :     {
    1100         220 :         basic_encode(
    1101         110 :             static_cast<unsigned char *>(pImage), &poGDS->m_abyCodedBuffer[0],
    1102             :             poGDS->m_abyCodedBuffer.size(), nRasterXSize, nDTSize, &nCodedSize);
    1103             :     }
    1104           0 :     catch (const DecodeEncodeException &)
    1105             :     {
    1106           0 :         return CE_Failure;
    1107             :     }
    1108             : 
    1109             : #ifdef CPL_MSB
    1110             :     if (nDTSize > 1)
    1111             :     {
    1112             :         GDALSwapWords(pImage, nDTSize, nRasterXSize, nDTSize);
    1113             :     }
    1114             : #endif
    1115             : 
    1116         110 :     if (poGDS->m_eCompress == VICARDataset::COMPRESS_BASIC)
    1117             :     {
    1118          20 :         VSIFSeekL(poGDS->fpImage,
    1119          20 :                   poGDS->m_anRecordOffsets[nYBlock] - sizeof(GUInt32),
    1120             :                   SEEK_SET);
    1121          20 :         GUInt32 nSizeToWrite =
    1122          20 :             static_cast<GUInt32>(nCodedSize + sizeof(GUInt32));
    1123          20 :         CPL_LSBPTR32(&nSizeToWrite);
    1124          20 :         VSIFWriteL(&nSizeToWrite, sizeof(GUInt32), 1, poGDS->fpImage);
    1125          20 :         VSIFWriteL(poGDS->m_abyCodedBuffer.data(), nCodedSize, 1,
    1126             :                    poGDS->fpImage);
    1127          20 :         poGDS->m_anRecordOffsets[nYBlock + 1] =
    1128          20 :             poGDS->m_anRecordOffsets[nYBlock] + nCodedSize + sizeof(GUInt32);
    1129             :     }
    1130             :     else
    1131             :     {
    1132          90 :         VSIFSeekL(poGDS->fpImage,
    1133          90 :                   poGDS->m_nLabelSize +
    1134          90 :                       static_cast<vsi_l_offset>(nYBlock) * sizeof(GUInt32),
    1135             :                   SEEK_SET);
    1136          90 :         GUInt32 nSizeToWrite = static_cast<GUInt32>(nCodedSize);
    1137          90 :         CPL_LSBPTR32(&nSizeToWrite);
    1138          90 :         VSIFWriteL(&nSizeToWrite, sizeof(GUInt32), 1, poGDS->fpImage);
    1139          90 :         VSIFSeekL(poGDS->fpImage, poGDS->m_anRecordOffsets[nYBlock], SEEK_SET);
    1140          90 :         VSIFWriteL(poGDS->m_abyCodedBuffer.data(), nCodedSize, 1,
    1141             :                    poGDS->fpImage);
    1142          90 :         poGDS->m_anRecordOffsets[nYBlock + 1] =
    1143          90 :             poGDS->m_anRecordOffsets[nYBlock] + nCodedSize;
    1144             :     }
    1145             : 
    1146         110 :     poGDS->m_nLastRecordOffset++;
    1147             : 
    1148         110 :     return CE_None;
    1149             : }
    1150             : 
    1151             : /************************************************************************/
    1152             : /*                            VICARDataset()                            */
    1153             : /************************************************************************/
    1154             : 
    1155         166 : VICARDataset::VICARDataset()
    1156             : 
    1157             : {
    1158         166 :     m_oJSonLabel.Deinit();
    1159         166 :     m_oSrcJSonLabel.Deinit();
    1160         166 : }
    1161             : 
    1162             : /************************************************************************/
    1163             : /*                           ~VICARDataset()                            */
    1164             : /************************************************************************/
    1165             : 
    1166         332 : VICARDataset::~VICARDataset()
    1167             : 
    1168             : {
    1169         166 :     VICARDataset::Close();
    1170         332 : }
    1171             : 
    1172             : /************************************************************************/
    1173             : /*                              Close()                                 */
    1174             : /************************************************************************/
    1175             : 
    1176         322 : CPLErr VICARDataset::Close()
    1177             : {
    1178         322 :     CPLErr eErr = CE_None;
    1179         322 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
    1180             :     {
    1181         166 :         if (!m_bIsLabelWritten)
    1182          19 :             WriteLabel();
    1183             : 
    1184         166 :         if (VICARDataset::FlushCache(true) != CE_None)
    1185           0 :             eErr = CE_Failure;
    1186             : 
    1187         166 :         PatchLabel();
    1188         166 :         if (fpImage)
    1189         166 :             VSIFCloseL(fpImage);
    1190             : 
    1191         166 :         if (GDALPamDataset::Close() != CE_None)
    1192           0 :             eErr = CE_Failure;
    1193             :     }
    1194         322 :     return eErr;
    1195             : }
    1196             : 
    1197             : /************************************************************************/
    1198             : /*                           GetSpatialRef()                            */
    1199             : /************************************************************************/
    1200             : 
    1201          26 : const OGRSpatialReference *VICARDataset::GetSpatialRef() const
    1202             : 
    1203             : {
    1204          26 :     if (!m_oSRS.IsEmpty())
    1205          16 :         return &m_oSRS;
    1206             : 
    1207          10 :     return GDALPamDataset::GetSpatialRef();
    1208             : }
    1209             : 
    1210             : /************************************************************************/
    1211             : /*                           SetSpatialRef()                            */
    1212             : /************************************************************************/
    1213             : 
    1214          39 : CPLErr VICARDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    1215             : {
    1216          39 :     if (eAccess == GA_ReadOnly)
    1217           0 :         return GDALPamDataset::SetSpatialRef(poSRS);
    1218          39 :     if (poSRS)
    1219          39 :         m_oSRS = *poSRS;
    1220             :     else
    1221           0 :         m_oSRS.Clear();
    1222          39 :     InvalidateLabel();
    1223          39 :     return CE_None;
    1224             : }
    1225             : 
    1226             : /************************************************************************/
    1227             : /*                          GetGeoTransform()                           */
    1228             : /************************************************************************/
    1229             : 
    1230          39 : CPLErr VICARDataset::GetGeoTransform(GDALGeoTransform &gt) const
    1231             : {
    1232          39 :     if (m_bGotTransform)
    1233             :     {
    1234          33 :         gt = m_gt;
    1235          33 :         return CE_None;
    1236             :     }
    1237             : 
    1238           6 :     return GDALPamDataset::GetGeoTransform(gt);
    1239             : }
    1240             : 
    1241             : /************************************************************************/
    1242             : /*                          SetGeoTransform()                           */
    1243             : /************************************************************************/
    1244             : 
    1245          43 : CPLErr VICARDataset::SetGeoTransform(const GDALGeoTransform &gt)
    1246             : 
    1247             : {
    1248          43 :     if (eAccess == GA_ReadOnly)
    1249           0 :         return GDALPamDataset::SetGeoTransform(gt);
    1250          43 :     if (gt[1] <= 0.0 || gt[1] != -gt[5] || gt[2] != 0.0 || gt[4] != 0.0)
    1251             :     {
    1252           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    1253             :                  "Only north-up geotransform with square pixels supported");
    1254           1 :         return CE_Failure;
    1255             :     }
    1256          42 :     m_bGotTransform = true;
    1257          42 :     m_gt = gt;
    1258          42 :     InvalidateLabel();
    1259          42 :     return CE_None;
    1260             : }
    1261             : 
    1262             : /************************************************************************/
    1263             : /*                        GetRawBinaryLayout()                          */
    1264             : /************************************************************************/
    1265             : 
    1266           2 : bool VICARDataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
    1267             : {
    1268           2 :     if (!RawDataset::GetRawBinaryLayout(sLayout))
    1269           0 :         return false;
    1270           2 :     sLayout.osRawFilename = GetDescription();
    1271           2 :     return true;
    1272             : }
    1273             : 
    1274             : /************************************************************************/
    1275             : /*                      GetMetadataDomainList()                         */
    1276             : /************************************************************************/
    1277             : 
    1278           1 : char **VICARDataset::GetMetadataDomainList()
    1279             : {
    1280           1 :     return BuildMetadataDomainList(nullptr, FALSE, "", "json:VICAR", nullptr);
    1281             : }
    1282             : 
    1283             : /************************************************************************/
    1284             : /*                             GetMetadata()                            */
    1285             : /************************************************************************/
    1286             : 
    1287          35 : char **VICARDataset::GetMetadata(const char *pszDomain)
    1288             : {
    1289          35 :     if (pszDomain != nullptr && EQUAL(pszDomain, "json:VICAR"))
    1290             :     {
    1291          22 :         if (m_aosVICARMD.empty())
    1292             :         {
    1293          19 :             if (eAccess == GA_Update && !m_oJSonLabel.IsValid())
    1294             :             {
    1295           0 :                 BuildLabel();
    1296             :             }
    1297          19 :             CPLAssert(m_oJSonLabel.IsValid());
    1298             :             const CPLString osJson =
    1299          38 :                 m_oJSonLabel.Format(CPLJSONObject::PrettyFormat::Pretty);
    1300          19 :             m_aosVICARMD.InsertString(0, osJson.c_str());
    1301             :         }
    1302          22 :         return m_aosVICARMD.List();
    1303             :     }
    1304          13 :     return GDALPamDataset::GetMetadata(pszDomain);
    1305             : }
    1306             : 
    1307             : /************************************************************************/
    1308             : /*                           InvalidateLabel()                          */
    1309             : /************************************************************************/
    1310             : 
    1311          94 : void VICARDataset::InvalidateLabel()
    1312             : {
    1313          94 :     m_oJSonLabel.Deinit();
    1314          94 :     m_aosVICARMD.Clear();
    1315          94 : }
    1316             : 
    1317             : /************************************************************************/
    1318             : /*                             SetMetadata()                            */
    1319             : /************************************************************************/
    1320             : 
    1321          13 : CPLErr VICARDataset::SetMetadata(char **papszMD, const char *pszDomain)
    1322             : {
    1323          13 :     if (m_bUseSrcLabel && eAccess == GA_Update && pszDomain != nullptr &&
    1324          13 :         EQUAL(pszDomain, "json:VICAR"))
    1325             :     {
    1326          13 :         m_oSrcJSonLabel.Deinit();
    1327          13 :         InvalidateLabel();
    1328          13 :         if (papszMD != nullptr && papszMD[0] != nullptr)
    1329             :         {
    1330          13 :             CPLJSONDocument oJSONDocument;
    1331          13 :             const GByte *pabyData = reinterpret_cast<const GByte *>(papszMD[0]);
    1332          13 :             if (!oJSONDocument.LoadMemory(pabyData))
    1333             :             {
    1334           0 :                 return CE_Failure;
    1335             :             }
    1336             : 
    1337          13 :             m_oSrcJSonLabel = oJSONDocument.GetRoot();
    1338          13 :             if (!m_oSrcJSonLabel.IsValid())
    1339             :             {
    1340           0 :                 return CE_Failure;
    1341             :             }
    1342             :         }
    1343          13 :         return CE_None;
    1344             :     }
    1345           0 :     return GDALPamDataset::SetMetadata(papszMD, pszDomain);
    1346             : }
    1347             : 
    1348             : /************************************************************************/
    1349             : /*                         SerializeString()                            */
    1350             : /************************************************************************/
    1351             : 
    1352         851 : static std::string SerializeString(const std::string &s)
    1353             : {
    1354        1702 :     return '\'' + CPLString(s).replaceAll('\'', "''").replaceAll('\n', "\\n") +
    1355        1702 :            '\'';
    1356             : }
    1357             : 
    1358             : /************************************************************************/
    1359             : /*                        WriteLabelItemValue()                         */
    1360             : /************************************************************************/
    1361             : 
    1362        1931 : static void WriteLabelItemValue(std::string &osLabel, const CPLJSONObject &obj)
    1363             : {
    1364        1931 :     const auto eType(obj.GetType());
    1365        1931 :     if (eType == CPLJSONObject::Type::Boolean)
    1366             :     {
    1367           2 :         osLabel += CPLSPrintf("%d", obj.ToBool() ? 1 : 0);
    1368             :     }
    1369        1929 :     else if (eType == CPLJSONObject::Type::Integer)
    1370             :     {
    1371         797 :         osLabel += CPLSPrintf("%d", obj.ToInteger());
    1372             :     }
    1373        1132 :     else if (eType == CPLJSONObject::Type::Long)
    1374             :     {
    1375             :         std::string osVal(
    1376           2 :             CPLSPrintf("%.17g", static_cast<double>(obj.ToLong())));
    1377           1 :         if (osVal.find('.') == std::string::npos)
    1378           1 :             osVal += ".0";
    1379           1 :         osLabel += osVal;
    1380             :     }
    1381        1131 :     else if (eType == CPLJSONObject::Type::Double)
    1382             :     {
    1383         324 :         double dfVal = obj.ToDouble();
    1384         324 :         if (dfVal >= static_cast<double>(std::numeric_limits<GIntBig>::min()) &&
    1385         648 :             dfVal <= static_cast<double>(std::numeric_limits<GIntBig>::max()) &&
    1386         324 :             static_cast<double>(static_cast<GIntBig>(dfVal)) == dfVal)
    1387             :         {
    1388         212 :             std::string osVal(CPLSPrintf("%.17g", dfVal));
    1389         106 :             if (osVal.find('.') == std::string::npos)
    1390         106 :                 osVal += ".0";
    1391         106 :             osLabel += osVal;
    1392             :         }
    1393             :         else
    1394             :         {
    1395         218 :             osLabel += CPLSPrintf("%.15g", dfVal);
    1396             :         }
    1397             :     }
    1398         807 :     else if (eType == CPLJSONObject::Type::String)
    1399             :     {
    1400         796 :         osLabel += SerializeString(obj.ToString());
    1401             :     }
    1402          11 :     else if (eType == CPLJSONObject::Type::Array)
    1403             :     {
    1404          18 :         const auto oArray = obj.ToArray();
    1405           9 :         osLabel += '(';
    1406          27 :         for (int i = 0; i < oArray.Size(); i++)
    1407             :         {
    1408          18 :             if (i > 0)
    1409           9 :                 osLabel += ',';
    1410          18 :             WriteLabelItemValue(osLabel, oArray[i]);
    1411             :         }
    1412           9 :         osLabel += ')';
    1413             :     }
    1414           2 :     else if (eType == CPLJSONObject::Type::Null)
    1415             :     {
    1416           1 :         osLabel += "'NULL'";
    1417             :     }
    1418             :     else
    1419             :     {
    1420             :         osLabel +=
    1421           1 :             SerializeString(obj.Format(CPLJSONObject::PrettyFormat::Plain));
    1422             :     }
    1423        1931 : }
    1424             : 
    1425             : /************************************************************************/
    1426             : /*                      SanitizeItemName()                              */
    1427             : /************************************************************************/
    1428             : 
    1429        1913 : static std::string SanitizeItemName(const std::string &osItemName)
    1430             : {
    1431        3826 :     std::string osRet(osItemName);
    1432        1913 :     if (osRet.size() > 32)
    1433           0 :         osRet.resize(32);
    1434        1913 :     if (osRet.empty())
    1435           0 :         return "UNNAMED";
    1436        1913 :     if (osRet[0] < 'A' || osRet[0] > 'Z')
    1437           0 :         osRet[0] = 'X';  // item name must start with a letter
    1438       14570 :     for (size_t i = 1; i < osRet.size(); i++)
    1439             :     {
    1440       12657 :         char ch = osRet[i];
    1441       12657 :         if (ch >= 'a' && ch <= 'z')
    1442           0 :             osRet[i] = ch - 'a' + 'A';
    1443       12657 :         else if (!((ch >= 'A' && ch <= 'Z') || (ch >= '0' && ch <= '9') ||
    1444             :                    ch == '_'))
    1445             :         {
    1446           0 :             osRet[i] = '_';
    1447             :         }
    1448             :     }
    1449        1913 :     if (osRet != osItemName)
    1450             :     {
    1451           0 :         CPLError(CE_Warning, CPLE_AppDefined,
    1452             :                  "Label item name %s has been sanitized to %s",
    1453             :                  osItemName.c_str(), osRet.c_str());
    1454             :     }
    1455        1913 :     return osRet;
    1456             : }
    1457             : 
    1458             : /************************************************************************/
    1459             : /*                        WriteLabelItem()                              */
    1460             : /************************************************************************/
    1461             : 
    1462        1913 : static void WriteLabelItem(std::string &osLabel, const CPLJSONObject &obj,
    1463             :                            const std::string &osItemName = std::string())
    1464             : {
    1465        1913 :     osLabel += ' ';
    1466             :     osLabel +=
    1467        1913 :         SanitizeItemName(osItemName.empty() ? obj.GetName() : osItemName);
    1468        1913 :     osLabel += '=';
    1469        1913 :     WriteLabelItemValue(osLabel, obj);
    1470        1913 : }
    1471             : 
    1472             : /************************************************************************/
    1473             : /*                           WriteLabel()                               */
    1474             : /************************************************************************/
    1475             : 
    1476          53 : void VICARDataset::WriteLabel()
    1477             : {
    1478          53 :     m_bIsLabelWritten = true;
    1479             : 
    1480          53 :     if (!m_oJSonLabel.IsValid())
    1481          53 :         BuildLabel();
    1482             : 
    1483         106 :     std::string osLabel;
    1484         106 :     auto children = m_oJSonLabel.GetChildren();
    1485        1504 :     for (const auto &child : children)
    1486             :     {
    1487        1451 :         const auto osName(child.GetName());
    1488        1451 :         if (osName == "LBLSIZE" || osName == "PROPERTY" || osName == "TASK")
    1489         110 :             continue;
    1490        2682 :         std::string osNameSubst;
    1491        1341 :         if (osName == "DAT_TIM" || osName == "USER")
    1492             :         {
    1493           2 :             osNameSubst = osName + '_';
    1494             :         }
    1495        1341 :         WriteLabelItem(osLabel, child, osNameSubst);
    1496             :     }
    1497             : 
    1498         159 :     auto property = m_oJSonLabel.GetObj("PROPERTY");
    1499          53 :     if (property.IsValid() && property.GetType() == CPLJSONObject::Type::Object)
    1500             :     {
    1501          46 :         children = property.GetChildren();
    1502          89 :         for (const auto &child : children)
    1503             :         {
    1504          43 :             if (child.GetType() == CPLJSONObject::Type::Object)
    1505             :             {
    1506          43 :                 osLabel += " PROPERTY=" + SerializeString(child.GetName());
    1507          86 :                 auto childrenProperty = child.GetChildren();
    1508         551 :                 for (const auto &childProperty : childrenProperty)
    1509             :                 {
    1510        1016 :                     const auto osName(child.GetName());
    1511        1016 :                     std::string osNameSubst;
    1512        1524 :                     if (osName == "LBLSIZE" || osName == "PROPERTY" ||
    1513        2032 :                         osName == "TASK" || osName == "DAT_TIM" ||
    1514         508 :                         osName == "USER")
    1515             :                     {
    1516           0 :                         osNameSubst = osName + '_';
    1517             :                     }
    1518         508 :                     WriteLabelItem(osLabel, childProperty, osNameSubst);
    1519             :                 }
    1520             :             }
    1521             :         }
    1522             :     }
    1523             : 
    1524         159 :     auto task = m_oJSonLabel.GetObj("TASK");
    1525          53 :     if (task.IsValid() && task.GetType() == CPLJSONObject::Type::Object)
    1526             :     {
    1527          11 :         children = task.GetChildren();
    1528          22 :         for (const auto &child : children)
    1529             :         {
    1530          11 :             if (child.GetType() == CPLJSONObject::Type::Object)
    1531             :             {
    1532          11 :                 osLabel += " TASK=" + SerializeString(child.GetName());
    1533          33 :                 auto oUser = child.GetObj("USER");
    1534          11 :                 if (oUser.IsValid())
    1535          10 :                     WriteLabelItem(osLabel, oUser);
    1536          33 :                 auto oDatTim = child.GetObj("DAT_TIM");
    1537          11 :                 if (oDatTim.IsValid())
    1538          11 :                     WriteLabelItem(osLabel, oDatTim);
    1539          22 :                 auto childrenProperty = child.GetChildren();
    1540          54 :                 for (const auto &childProperty : childrenProperty)
    1541             :                 {
    1542          43 :                     const auto osName(child.GetName());
    1543          43 :                     if (osName == "USER" || osName == "DAT_TIM")
    1544           0 :                         continue;
    1545          86 :                     std::string osNameSubst;
    1546          86 :                     if (osName == "LBLSIZE" || osName == "PROPERTY" ||
    1547          43 :                         osName == "TASK")
    1548             :                     {
    1549          10 :                         osNameSubst = osName + '_';
    1550             :                     }
    1551          43 :                     WriteLabelItem(osLabel, childProperty, osNameSubst);
    1552             :                 }
    1553             :             }
    1554             :         }
    1555             :     }
    1556             : 
    1557             :     // Figure out label size, round it to the next multiple of RECSIZE
    1558          53 :     constexpr size_t MAX_LOG10_LBLSIZE = 10;
    1559          53 :     size_t nLabelSize = strlen("LBLSIZE=") + MAX_LOG10_LBLSIZE + osLabel.size();
    1560          53 :     nLabelSize = DIV_ROUND_UP(nLabelSize, m_nRecordSize) * m_nRecordSize;
    1561             :     std::string osLabelSize(
    1562         159 :         CPLSPrintf("LBLSIZE=%d", static_cast<int>(nLabelSize)));
    1563         421 :     while (osLabelSize.size() < strlen("LBLSIZE=") + MAX_LOG10_LBLSIZE)
    1564         368 :         osLabelSize += ' ';
    1565          53 :     osLabel = osLabelSize + osLabel;
    1566          53 :     CPLAssert(osLabel.size() <= nLabelSize);
    1567             : 
    1568             :     // Write label
    1569          53 :     VSIFSeekL(fpImage, 0, SEEK_SET);
    1570          53 :     VSIFWriteL(osLabel.data(), 1, osLabel.size(), fpImage);
    1571          53 :     const size_t nZeroPadding = nLabelSize - osLabel.size();
    1572          53 :     if (nZeroPadding)
    1573             :     {
    1574          50 :         VSIFWriteL(std::string(nZeroPadding, '\0').data(), 1, nZeroPadding,
    1575             :                    fpImage);
    1576             :     }
    1577             : 
    1578          53 :     if (m_bInitToNodata && m_eCompress == COMPRESS_NONE)
    1579             :     {
    1580             :         const int nDTSize =
    1581          20 :             GDALGetDataTypeSizeBytes(GetRasterBand(1)->GetRasterDataType());
    1582          20 :         VSIFTruncateL(fpImage, VSIFTellL(fpImage) +
    1583          20 :                                    static_cast<vsi_l_offset>(nRasterXSize) *
    1584          20 :                                        nRasterYSize * nBands * nDTSize);
    1585             :     }
    1586             : 
    1587             :     // Patch band offsets to take into account label
    1588         140 :     for (int i = 0; i < nBands; i++)
    1589             :     {
    1590          87 :         auto poBand = dynamic_cast<VICARRawRasterBand *>(GetRasterBand(i + 1));
    1591          87 :         if (poBand)
    1592          82 :             poBand->nImgOffset += nLabelSize;
    1593             :     }
    1594          53 : }
    1595             : 
    1596             : /************************************************************************/
    1597             : /*                           PatchLabel()                               */
    1598             : /************************************************************************/
    1599             : 
    1600         166 : void VICARDataset::PatchLabel()
    1601             : {
    1602         166 :     if (eAccess == GA_ReadOnly || m_eCompress == COMPRESS_NONE)
    1603         161 :         return;
    1604             : 
    1605           5 :     VSIFSeekL(fpImage, 0, SEEK_END);
    1606           5 :     const vsi_l_offset nFileSize = VSIFTellL(fpImage);
    1607           5 :     VSIFSeekL(fpImage, 0, SEEK_SET);
    1608          10 :     std::string osBuffer;
    1609           5 :     osBuffer.resize(1024);
    1610           5 :     size_t nRead = VSIFReadL(&osBuffer[0], 1, 1024, fpImage);
    1611             : 
    1612             :     {
    1613           5 :         CPLString osEOCI1;
    1614           5 :         osEOCI1.Printf("%u", static_cast<unsigned>(nFileSize));
    1615          36 :         while (osEOCI1.size() < 10)
    1616          31 :             osEOCI1 += ' ';
    1617           5 :         size_t nPos = osBuffer.find("EOCI1=");
    1618           5 :         CPLAssert(nPos <= nRead - (strlen("EOCI1=") + 10));
    1619           5 :         memcpy(&osBuffer[nPos + strlen("EOCI1=")], osEOCI1.data(), 10);
    1620             :     }
    1621             : 
    1622             :     {
    1623           5 :         CPLString osEOCI2;
    1624           5 :         osEOCI2.Printf("%u", static_cast<unsigned>(nFileSize >> 32));
    1625          50 :         while (osEOCI2.size() < 10)
    1626          45 :             osEOCI2 += ' ';
    1627           5 :         size_t nPos = osBuffer.find("EOCI2=");
    1628           5 :         CPLAssert(nPos <= nRead - (strlen("EOCI2=") + 10));
    1629           5 :         memcpy(&osBuffer[nPos + strlen("EOCI2=")], osEOCI2.data(), 10);
    1630             :     }
    1631           5 :     VSIFSeekL(fpImage, 0, SEEK_SET);
    1632           5 :     VSIFWriteL(&osBuffer[0], 1, nRead, fpImage);
    1633             : }
    1634             : 
    1635             : /************************************************************************/
    1636             : /*                           BuildLabel()                               */
    1637             : /************************************************************************/
    1638             : 
    1639          53 : void VICARDataset::BuildLabel()
    1640             : {
    1641         106 :     CPLJSONObject oLabel = m_oSrcJSonLabel;
    1642          53 :     if (!oLabel.IsValid())
    1643             :     {
    1644          38 :         oLabel = CPLJSONObject();
    1645             :     }
    1646             : 
    1647          53 :     oLabel.Set("LBLSIZE", 0);  // to be overridden later
    1648             : 
    1649          53 :     if (!oLabel.GetObj("TYPE").IsValid())
    1650          41 :         oLabel.Set("TYPE", "IMAGE");
    1651             : 
    1652          53 :     const auto eType = GetRasterBand(1)->GetRasterDataType();
    1653          53 :     const char *pszFormat = "";
    1654          53 :     CPL_IGNORE_RET_VAL(pszFormat);  // Make CSA happy
    1655          53 :     switch (eType)
    1656             :     {
    1657          33 :         case GDT_Byte:
    1658          33 :             pszFormat = "BYTE";
    1659          33 :             break;
    1660           5 :         case GDT_Int16:
    1661           5 :             pszFormat = "HALF";
    1662           5 :             break;
    1663           3 :         case GDT_Int32:
    1664           3 :             pszFormat = "FULL";
    1665           3 :             break;
    1666           4 :         case GDT_Float32:
    1667           4 :             pszFormat = "REAL";
    1668           4 :             break;
    1669           4 :         case GDT_Float64:
    1670           4 :             pszFormat = "DOUB";
    1671           4 :             break;
    1672           4 :         case GDT_CFloat32:
    1673           4 :             pszFormat = "COMP";
    1674           4 :             break;
    1675           0 :         default:
    1676           0 :             CPLAssert(false);
    1677             :             break;
    1678             :     }
    1679          53 :     oLabel.Set("FORMAT", pszFormat);
    1680             : 
    1681          53 :     oLabel.Set("BUFSIZ", m_nRecordSize);  // arbitrary value
    1682          53 :     oLabel.Set("DIM", 3);
    1683          53 :     oLabel.Set("EOL", 0);
    1684          53 :     oLabel.Set("RECSIZE", m_nRecordSize);
    1685          53 :     oLabel.Set("ORG", "BSQ");
    1686          53 :     oLabel.Set("NL", nRasterYSize);
    1687          53 :     oLabel.Set("NS", nRasterXSize);
    1688          53 :     oLabel.Set("NB", nBands);
    1689          53 :     oLabel.Set("N1", nRasterXSize);
    1690          53 :     oLabel.Set("N2", nRasterYSize);
    1691          53 :     oLabel.Set("N3", nBands);
    1692          53 :     oLabel.Set("N4", 0);
    1693          53 :     oLabel.Set("NBB", 0);
    1694          53 :     oLabel.Set("NLB", 0);
    1695          53 :     oLabel.Set("HOST", "X86-64-LINX");
    1696          53 :     oLabel.Set("INTFMT", "LOW");
    1697          53 :     oLabel.Set("REALFMT", "RIEEE");
    1698          53 :     oLabel.Set("BHOST", "X86-64-LINX");
    1699          53 :     oLabel.Set("BINTFMT", "LOW");
    1700          53 :     if (!oLabel.GetObj("BLTYPE").IsValid())
    1701          38 :         oLabel.Set("BLTYPE", "");
    1702         104 :     oLabel.Set("COMPRESS", m_eCompress == COMPRESS_BASIC    ? "BASIC"
    1703          51 :                            : m_eCompress == COMPRESS_BASIC2 ? "BASIC2"
    1704             :                                                             : "NONE");
    1705          53 :     if (m_eCompress == COMPRESS_NONE)
    1706             :     {
    1707          48 :         oLabel.Set("EOCI1", 0);
    1708          48 :         oLabel.Set("EOCI2", 0);
    1709             :     }
    1710             :     else
    1711             :     {
    1712             :         // To be later patched. Those fake values must take 10 bytes
    1713             :         // (8 + 2 single quotes) so that they can be later replaced by a
    1714             :         // integer of maximum value 4294967295 (10 digits)
    1715           5 :         oLabel.Set("EOCI1", "XXXXXXXX");
    1716           5 :         oLabel.Set("EOCI2", "XXXXXXXX");
    1717             :     }
    1718             : 
    1719          53 :     if (m_bUseSrcMap)
    1720             :     {
    1721           0 :         auto oMap = oLabel.GetObj("PROPERTY/MAP");
    1722           0 :         if (oMap.IsValid() && oMap.GetType() == CPLJSONObject::Type::Object)
    1723             :         {
    1724           0 :             if (!m_osTargetName.empty())
    1725           0 :                 oMap.Set("TARGET_NAME", m_osTargetName);
    1726           0 :             if (!m_osLatitudeType.empty())
    1727           0 :                 oMap.Set("COORDINATE_SYSTEM_NAME", m_osLatitudeType);
    1728           0 :             if (!m_osLongitudeDirection.empty())
    1729           0 :                 oMap.Set("POSITIVE_LONGITUDE_DIRECTION",
    1730           0 :                          m_osLongitudeDirection);
    1731             :         }
    1732             :     }
    1733          53 :     else if (m_bGeoRefFormatIsMIPL)
    1734             :     {
    1735         153 :         auto oProperty = oLabel.GetObj("PROPERTY");
    1736          51 :         if (oProperty.IsValid())
    1737             :         {
    1738           8 :             oProperty.Delete("MAP");
    1739           8 :             oProperty.Delete("GEOTIFF");
    1740             :         }
    1741          51 :         if (!m_oSRS.IsEmpty())
    1742             :         {
    1743          37 :             BuildLabelPropertyMap(oLabel);
    1744             :         }
    1745             :     }
    1746             :     else
    1747             :     {
    1748           6 :         auto oProperty = oLabel.GetObj("PROPERTY");
    1749           2 :         if (oProperty.IsValid())
    1750             :         {
    1751           1 :             oProperty.Delete("MAP");
    1752           1 :             oProperty.Delete("GEOTIFF");
    1753             :         }
    1754           2 :         if (!m_oSRS.IsEmpty())
    1755             :         {
    1756             : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
    1757           2 :             BuildLabelPropertyGeoTIFF(oLabel);
    1758             : #endif
    1759             :         }
    1760             :     }
    1761             : 
    1762          53 :     m_oJSonLabel = std::move(oLabel);
    1763          53 : }
    1764             : 
    1765             : /************************************************************************/
    1766             : /*                        BuildLabelPropertyMap()                       */
    1767             : /************************************************************************/
    1768             : 
    1769          37 : void VICARDataset::BuildLabelPropertyMap(CPLJSONObject &oLabel)
    1770             : {
    1771          37 :     if (m_oSRS.IsProjected() || m_oSRS.IsGeographic())
    1772             :     {
    1773         111 :         auto oProperty = GetOrCreateJSONObject(oLabel, "PROPERTY");
    1774         111 :         auto oMap = GetOrCreateJSONObject(oProperty, "MAP");
    1775             : 
    1776          37 :         const char *pszDatum = m_oSRS.GetAttrValue("DATUM");
    1777          74 :         CPLString osTargetName(m_osTargetName);
    1778          37 :         if (osTargetName.empty())
    1779             :         {
    1780          37 :             if (pszDatum && STARTS_WITH(pszDatum, "D_"))
    1781             :             {
    1782           2 :                 osTargetName = pszDatum + 2;
    1783             :             }
    1784          35 :             else if (pszDatum)
    1785             :             {
    1786          35 :                 osTargetName = pszDatum;
    1787             :             }
    1788             :         }
    1789          37 :         if (!osTargetName.empty())
    1790          37 :             oMap.Add("TARGET_NAME", osTargetName);
    1791             : 
    1792          37 :         oMap.Add("A_AXIS_RADIUS", m_oSRS.GetSemiMajor() / 1000.0);
    1793          37 :         oMap.Add("B_AXIS_RADIUS", m_oSRS.GetSemiMajor() / 1000.0);
    1794          37 :         oMap.Add("C_AXIS_RADIUS", m_oSRS.GetSemiMinor() / 1000.0);
    1795             : 
    1796          37 :         if (!m_osLatitudeType.empty())
    1797           0 :             oMap.Add("COORDINATE_SYSTEM_NAME", m_osLatitudeType);
    1798             :         else
    1799          37 :             oMap.Add("COORDINATE_SYSTEM_NAME", "PLANETOCENTRIC");
    1800             : 
    1801          37 :         if (!m_osLongitudeDirection.empty())
    1802           0 :             oMap.Add("POSITIVE_LONGITUDE_DIRECTION", m_osLongitudeDirection);
    1803             :         else
    1804          37 :             oMap.Add("POSITIVE_LONGITUDE_DIRECTION", "EAST");
    1805             : 
    1806          37 :         const char *pszProjection = m_oSRS.GetAttrValue("PROJECTION");
    1807          37 :         if (pszProjection == nullptr)
    1808             :         {
    1809          35 :             oMap.Add("MAP_PROJECTION_TYPE", "SIMPLE_CYLINDRICAL");
    1810          35 :             oMap.Add("CENTER_LONGITUDE", 0.0);
    1811          35 :             oMap.Add("CENTER_LATITUDE", 0.0);
    1812             :         }
    1813           2 :         else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
    1814             :         {
    1815           0 :             oMap.Add("MAP_PROJECTION_TYPE", "EQUIRECTANGULAR");
    1816           0 :             if (m_oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
    1817             :             {
    1818           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    1819             :                          "Ignoring %s. Only 0 value supported",
    1820             :                          SRS_PP_LATITUDE_OF_ORIGIN);
    1821             :             }
    1822           0 :             oMap.Add("CENTER_LONGITUDE",
    1823             :                      m_oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0));
    1824             :             const double dfCenterLat =
    1825           0 :                 m_oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
    1826           0 :             oMap.Add("CENTER_LATITUDE", dfCenterLat);
    1827             :         }
    1828           2 :         else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
    1829             :         {
    1830           2 :             oMap.Add("MAP_PROJECTION_TYPE", "SINUSOIDAL");
    1831           2 :             oMap.Add("CENTER_LONGITUDE",
    1832             :                      m_oSRS.GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0));
    1833           2 :             oMap.Add("CENTER_LATITUDE", 0.0);
    1834             :         }
    1835             :         else
    1836             :         {
    1837           0 :             CPLError(CE_Warning, CPLE_NotSupported,
    1838             :                      "Projection %s not supported", pszProjection);
    1839             :         }
    1840             : 
    1841          37 :         if (oMap["MAP_PROJECTION_TYPE"].IsValid())
    1842             :         {
    1843          37 :             if (m_oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) != 0.0)
    1844             :             {
    1845           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    1846             :                          "Ignoring %s. Only 0 value supported",
    1847             :                          SRS_PP_FALSE_EASTING);
    1848             :             }
    1849          37 :             if (m_oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) != 0.0)
    1850             :             {
    1851           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1852             :                          "Ignoring %s. Only 0 value supported",
    1853             :                          SRS_PP_FALSE_NORTHING);
    1854             :             }
    1855             : 
    1856          37 :             if (m_bGotTransform)
    1857             :             {
    1858             :                 const double dfDegToMeter =
    1859          37 :                     m_oSRS.GetSemiMajor() * M_PI / 180.0;
    1860          37 :                 if (m_oSRS.IsProjected())
    1861             :                 {
    1862           2 :                     const double dfLinearUnits = m_oSRS.GetLinearUnits();
    1863           2 :                     const double dfScale = m_gt[1] * dfLinearUnits;
    1864           2 :                     oMap.Add("SAMPLE_PROJECTION_OFFSET",
    1865           2 :                              -m_gt[0] * dfLinearUnits / dfScale - 0.5);
    1866           2 :                     oMap.Add("LINE_PROJECTION_OFFSET",
    1867           2 :                              m_gt[3] * dfLinearUnits / dfScale - 0.5);
    1868           2 :                     oMap.Add("MAP_SCALE", dfScale / 1000.0);
    1869             :                 }
    1870          35 :                 else if (m_oSRS.IsGeographic())
    1871             :                 {
    1872          35 :                     const double dfScale = m_gt[1] * dfDegToMeter;
    1873          35 :                     oMap.Add("SAMPLE_PROJECTION_OFFSET",
    1874          35 :                              -m_gt[0] * dfDegToMeter / dfScale - 0.5);
    1875          35 :                     oMap.Add("LINE_PROJECTION_OFFSET",
    1876          35 :                              m_gt[3] * dfDegToMeter / dfScale - 0.5);
    1877          35 :                     oMap.Add("MAP_SCALE", dfScale / 1000.0);
    1878             :                 }
    1879             :             }
    1880             :         }
    1881             :     }
    1882             :     else
    1883             :     {
    1884           0 :         CPLError(CE_Warning, CPLE_NotSupported, "SRS not supported");
    1885             :     }
    1886          37 : }
    1887             : 
    1888             : /************************************************************************/
    1889             : /*                    BuildLabelPropertyGeoTIFF()                       */
    1890             : /************************************************************************/
    1891             : 
    1892             : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
    1893           2 : void VICARDataset::BuildLabelPropertyGeoTIFF(CPLJSONObject &oLabel)
    1894             : {
    1895           4 :     auto oProperty = GetOrCreateJSONObject(oLabel, "PROPERTY");
    1896           4 :     auto oGeoTIFF = GetOrCreateJSONObject(oProperty, "GEOTIFF");
    1897             : 
    1898             :     // Ported from Vicar Open Source: Afids expects to be able to read
    1899             :     // NITF_NROWS and NITF_NCOLS
    1900             : 
    1901           2 :     oGeoTIFF.Add("NITF_NROWS", nRasterYSize);
    1902           2 :     oGeoTIFF.Add("NITF_NCOLS", nRasterXSize);
    1903             : 
    1904             :     // Create a in-memory GeoTIFF file
    1905             : 
    1906             :     const std::string osTmpFilename(
    1907           2 :         VSIMemGenerateHiddenFilename("vicar_tmp.tif"));
    1908             :     GDALDriver *poGTiffDriver =
    1909           2 :         GDALDriver::FromHandle(GDALGetDriverByName("GTiff"));
    1910           2 :     if (poGTiffDriver == nullptr)
    1911             :     {
    1912           0 :         CPLError(CE_Failure, CPLE_AppDefined, "GTiff driver not available");
    1913           0 :         return;
    1914             :     }
    1915           2 :     const char *const apszOptions[] = {"GEOTIFF_VERSION=1.0", nullptr};
    1916             :     auto poDS = std::unique_ptr<GDALDataset>(poGTiffDriver->Create(
    1917           2 :         osTmpFilename.c_str(), 1, 1, 1, GDT_Byte, apszOptions));
    1918           2 :     if (!poDS)
    1919           0 :         return;
    1920           2 :     poDS->SetSpatialRef(&m_oSRS);
    1921           2 :     if (m_bGotTransform)
    1922           2 :         poDS->SetGeoTransform(m_gt);
    1923           4 :     poDS->SetMetadataItem(GDALMD_AREA_OR_POINT,
    1924           2 :                           GetMetadataItem(GDALMD_AREA_OR_POINT));
    1925           2 :     poDS.reset();
    1926             : 
    1927             :     // Open it with libtiff/libgeotiff
    1928           2 :     VSILFILE *fpL = VSIFOpenL(osTmpFilename.c_str(), "r");
    1929           2 :     if (fpL == nullptr)
    1930             :     {
    1931           0 :         VSIUnlink(osTmpFilename.c_str());
    1932           0 :         return;
    1933             :     }
    1934             : 
    1935           2 :     TIFF *hTIFF = VSI_TIFFOpen(osTmpFilename.c_str(), "r", fpL);
    1936           2 :     CPLAssert(hTIFF);
    1937             : 
    1938           2 :     GTIF *hGTIF = GTIFNew(hTIFF);
    1939           2 :     CPLAssert(hGTIF);
    1940             : 
    1941             :     // Get geotiff keys and write them as VICAR metadata
    1942          34 :     for (const auto &gkey : GTiffShortKeys)
    1943             :     {
    1944          32 :         unsigned short val = 0;
    1945          32 :         if (GDALGTIFKeyGetSHORT(hGTIF, gkey, &val, 0, 1))
    1946             :         {
    1947          20 :             oGeoTIFF.Add(
    1948          20 :                 CPLString(GTIFKeyName(gkey)).toupper(),
    1949             :                 CPLSPrintf("%d(%s)", val, GTIFValueNameEx(hGTIF, gkey, val)));
    1950             :         }
    1951             :     }
    1952             : 
    1953          62 :     for (const auto &gkey : GTiffDoubleKeys)
    1954             :     {
    1955          60 :         double val = 0;
    1956          60 :         if (GDALGTIFKeyGetDOUBLE(hGTIF, gkey, &val, 0, 1))
    1957             :         {
    1958          12 :             oGeoTIFF.Add(CPLString(GTIFKeyName(gkey)).toupper(),
    1959             :                          CPLSPrintf("%.17g", val));
    1960             :         }
    1961             :     }
    1962             : 
    1963          10 :     for (const auto &gkey : GTiffAsciiKeys)
    1964             :     {
    1965             :         char szAscii[1024];
    1966           8 :         if (GDALGTIFKeyGetASCII(hGTIF, gkey, szAscii,
    1967           8 :                                 static_cast<int>(sizeof(szAscii))))
    1968             :         {
    1969           4 :             oGeoTIFF.Add(CPLString(GTIFKeyName(gkey)).toupper(), szAscii);
    1970             :         }
    1971             :     }
    1972             : 
    1973           2 :     GTIFFree(hGTIF);
    1974             : 
    1975             :     // Get geotiff tags and write them as VICAR metadata
    1976             :     const std::map<int, const char *> oMapTagCodeToName = {
    1977             :         {TIFFTAG_GEOPIXELSCALE, "MODELPIXELSCALETAG"},
    1978             :         {TIFFTAG_GEOTIEPOINTS, "MODELTIEPOINTTAG"},
    1979           4 :         {TIFFTAG_GEOTRANSMATRIX, "MODELTRANSFORMATIONTAG"}};
    1980             : 
    1981           8 :     for (const auto &kv : oMapTagCodeToName)
    1982             :     {
    1983           6 :         uint16_t nCount = 0;
    1984           6 :         double *padfValues = nullptr;
    1985           6 :         if (TIFFGetField(hTIFF, kv.first, &nCount, &padfValues))
    1986             :         {
    1987           4 :             std::string osVal("(");
    1988          22 :             for (uint16_t i = 0; i < nCount; ++i)
    1989             :             {
    1990          18 :                 if (i > 0)
    1991          14 :                     osVal += ',';
    1992          18 :                 osVal += CPLSPrintf("%.17g", padfValues[i]);
    1993             :             }
    1994           4 :             osVal += ')';
    1995           4 :             oGeoTIFF.Add(kv.second, osVal);
    1996             :         }
    1997             :     }
    1998             : 
    1999           2 :     XTIFFClose(hTIFF);
    2000           2 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
    2001           2 :     VSIUnlink(osTmpFilename.c_str());
    2002             : }
    2003             : #endif
    2004             : 
    2005             : /************************************************************************/
    2006             : /*                       ReadProjectionFromMapGroup()                   */
    2007             : /************************************************************************/
    2008             : 
    2009          27 : void VICARDataset::ReadProjectionFromMapGroup()
    2010             : {
    2011          27 :     double dfXDim = 1.0;
    2012          27 :     double dfYDim = 1.0;
    2013             : 
    2014          27 :     const char *value = GetKeyword("MAP.MAP_SCALE");
    2015          27 :     if (strlen(value) > 0)
    2016             :     {
    2017          27 :         dfXDim = CPLAtof(value) * 1000.0;
    2018          27 :         dfYDim = CPLAtof(value) * -1 * 1000.0;
    2019             :     }
    2020             : 
    2021             :     const double dfSampleOffset_Shift =
    2022          27 :         CPLAtof(CPLGetConfigOption("PDS_SampleProjOffset_Shift", "0.5"));
    2023             : 
    2024             :     const double dfLineOffset_Shift =
    2025          27 :         CPLAtof(CPLGetConfigOption("PDS_LineProjOffset_Shift", "0.5"));
    2026             : 
    2027             :     const double dfSampleOffset_Mult =
    2028          27 :         CPLAtof(CPLGetConfigOption("PDS_SampleProjOffset_Mult", "-1.0"));
    2029             : 
    2030             :     const double dfLineOffset_Mult =
    2031          27 :         CPLAtof(CPLGetConfigOption("PDS_LineProjOffset_Mult", "1.0"));
    2032             : 
    2033             :     /***********   Grab LINE_PROJECTION_OFFSET ************/
    2034          27 :     double dfULYMap = 0.5;
    2035             : 
    2036          27 :     value = GetKeyword("MAP.LINE_PROJECTION_OFFSET");
    2037          27 :     if (strlen(value) > 0)
    2038             :     {
    2039          27 :         const double yulcenter = CPLAtof(value);
    2040          27 :         dfULYMap =
    2041          27 :             ((yulcenter + dfLineOffset_Shift) * -dfYDim * dfLineOffset_Mult);
    2042             :     }
    2043             :     /***********   Grab SAMPLE_PROJECTION_OFFSET ************/
    2044          27 :     double dfULXMap = 0.5;
    2045             : 
    2046          27 :     value = GetKeyword("MAP.SAMPLE_PROJECTION_OFFSET");
    2047          27 :     if (strlen(value) > 0)
    2048             :     {
    2049          27 :         const double xulcenter = CPLAtof(value);
    2050          27 :         dfULXMap =
    2051          27 :             ((xulcenter + dfSampleOffset_Shift) * dfXDim * dfSampleOffset_Mult);
    2052             :     }
    2053             : 
    2054             :     /* ==================================================================== */
    2055             :     /*      Get the coordinate system.                                      */
    2056             :     /* ==================================================================== */
    2057          27 :     bool bProjectionSet = true;
    2058             : 
    2059             :     /***********  Grab TARGET_NAME  ************/
    2060             :     /**** This is the planets name i.e. MARS ***/
    2061          54 :     CPLString target_name = GetKeyword("MAP.TARGET_NAME");
    2062             : 
    2063             :     /**********   Grab MAP_PROJECTION_TYPE *****/
    2064          54 :     const CPLString map_proj_name = GetKeyword("MAP.MAP_PROJECTION_TYPE");
    2065             : 
    2066             :     /******  Grab semi_major & convert to KM ******/
    2067          27 :     const double semi_major = CPLAtof(GetKeyword("MAP.A_AXIS_RADIUS")) * 1000.0;
    2068             : 
    2069             :     /******  Grab semi-minor & convert to KM ******/
    2070          27 :     const double semi_minor = CPLAtof(GetKeyword("MAP.C_AXIS_RADIUS")) * 1000.0;
    2071             : 
    2072             :     /***********   Grab CENTER_LAT ************/
    2073          27 :     const double center_lat = CPLAtof(GetKeyword("MAP.CENTER_LATITUDE"));
    2074             : 
    2075             :     /***********   Grab CENTER_LON ************/
    2076          27 :     const double center_lon = CPLAtof(GetKeyword("MAP.CENTER_LONGITUDE"));
    2077             : 
    2078             :     /**********   Grab 1st std parallel *******/
    2079             :     const double first_std_parallel =
    2080          27 :         CPLAtof(GetKeyword("MAP.FIRST_STANDARD_PARALLEL"));
    2081             : 
    2082             :     /**********   Grab 2nd std parallel *******/
    2083             :     const double second_std_parallel =
    2084          27 :         CPLAtof(GetKeyword("MAP.SECOND_STANDARD_PARALLEL"));
    2085             : 
    2086             :     /*** grab  PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/
    2087             :     // Need to further study how ocentric/ographic will effect the gdal library.
    2088             :     // So far we will use this fact to define a sphere or ellipse for some
    2089             :     // projections Frank - may need to talk this over
    2090          27 :     bool bIsGeographic = true;
    2091          27 :     value = GetKeyword("MAP.COORDINATE_SYSTEM_NAME");
    2092          27 :     if (EQUAL(value, "PLANETOCENTRIC"))
    2093          27 :         bIsGeographic = false;
    2094             : 
    2095             :     /**   Set oSRS projection and parameters --- all PDS supported types added
    2096             :     if apparently supported in oSRS "AITOFF",  ** Not supported in GDAL??
    2097             :           "ALBERS",
    2098             :           "BONNE",
    2099             :           "BRIESEMEISTER",   ** Not supported in GDAL??
    2100             :           "CYLINDRICAL EQUAL AREA",
    2101             :           "EQUIDISTANT",
    2102             :           "EQUIRECTANGULAR",
    2103             :           "GNOMONIC",
    2104             :           "HAMMER",    ** Not supported in GDAL??
    2105             :           "HENDU",     ** Not supported in GDAL??
    2106             :           "LAMBERT AZIMUTHAL EQUAL AREA",
    2107             :           "LAMBERT CONFORMAL",
    2108             :           "MERCATOR",
    2109             :           "MOLLWEIDE",
    2110             :           "OBLIQUE CYLINDRICAL",
    2111             :           "ORTHOGRAPHIC",
    2112             :           "SIMPLE CYLINDRICAL",
    2113             :           "SINUSOIDAL",
    2114             :           "STEREOGRAPHIC",
    2115             :           "TRANSVERSE MERCATOR",
    2116             :           "VAN DER GRINTEN",     ** Not supported in GDAL??
    2117             :           "WERNER"     ** Not supported in GDAL??
    2118             :     **/
    2119          27 :     CPLDebug("PDS", "using projection %s\n\n", map_proj_name.c_str());
    2120             : 
    2121          54 :     OGRSpatialReference oSRS;
    2122             : 
    2123          27 :     if ((EQUAL(map_proj_name, "EQUIRECTANGULAR")) ||
    2124          39 :         (EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
    2125          12 :         (EQUAL(map_proj_name, "EQUIDISTANT")))
    2126             :     {
    2127          15 :         oSRS.SetEquirectangular2(0.0, center_lon, center_lat, 0, 0);
    2128             :     }
    2129          12 :     else if (EQUAL(map_proj_name, "ORTHOGRAPHIC"))
    2130             :     {
    2131           0 :         oSRS.SetOrthographic(center_lat, center_lon, 0, 0);
    2132             :     }
    2133          12 :     else if (EQUAL(map_proj_name, "SINUSOIDAL"))
    2134             :     {
    2135          12 :         oSRS.SetSinusoidal(center_lon, 0, 0);
    2136             :     }
    2137           0 :     else if (EQUAL(map_proj_name, "MERCATOR"))
    2138             :     {
    2139           0 :         oSRS.SetMercator(center_lat, center_lon, 1, 0, 0);
    2140             :     }
    2141           0 :     else if (EQUAL(map_proj_name, "STEREOGRAPHIC"))
    2142             :     {
    2143           0 :         if ((fabs(center_lat) - 90) < 0.0000001)
    2144             :         {
    2145           0 :             oSRS.SetPS(center_lat, center_lon, 1, 0, 0);
    2146             :         }
    2147             :         else
    2148             :         {
    2149           0 :             oSRS.SetStereographic(center_lat, center_lon, 1, 0, 0);
    2150             :         }
    2151             :     }
    2152           0 :     else if (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC"))
    2153             :     {
    2154           0 :         oSRS.SetPS(center_lat, center_lon, 1, 0, 0);
    2155             :     }
    2156           0 :     else if (EQUAL(map_proj_name, "TRANSVERSE_MERCATOR"))
    2157             :     {
    2158           0 :         oSRS.SetTM(center_lat, center_lon, 1, 0, 0);
    2159             :     }
    2160           0 :     else if (EQUAL(map_proj_name, "LAMBERT_CONFORMAL_CONIC"))
    2161             :     {
    2162           0 :         oSRS.SetLCC(first_std_parallel, second_std_parallel, center_lat,
    2163             :                     center_lon, 0, 0);
    2164             :     }
    2165           0 :     else if (EQUAL(map_proj_name, "LAMBERT_AZIMUTHAL_EQUAL_AREA"))
    2166             :     {
    2167           0 :         oSRS.SetLAEA(center_lat, center_lon, 0, 0);
    2168             :     }
    2169           0 :     else if (EQUAL(map_proj_name, "CYLINDRICAL_EQUAL_AREA"))
    2170             :     {
    2171           0 :         oSRS.SetCEA(first_std_parallel, center_lon, 0, 0);
    2172             :     }
    2173           0 :     else if (EQUAL(map_proj_name, "MOLLWEIDE"))
    2174             :     {
    2175           0 :         oSRS.SetMollweide(center_lon, 0, 0);
    2176             :     }
    2177           0 :     else if (EQUAL(map_proj_name, "ALBERS"))
    2178             :     {
    2179           0 :         oSRS.SetACEA(first_std_parallel, second_std_parallel, center_lat,
    2180             :                      center_lon, 0, 0);
    2181             :     }
    2182           0 :     else if (EQUAL(map_proj_name, "BONNE"))
    2183             :     {
    2184           0 :         oSRS.SetBonne(first_std_parallel, center_lon, 0, 0);
    2185             :     }
    2186           0 :     else if (EQUAL(map_proj_name, "GNOMONIC"))
    2187             :     {
    2188           0 :         oSRS.SetGnomonic(center_lat, center_lon, 0, 0);
    2189             : #ifdef FIXME
    2190             :     }
    2191             :     else if (EQUAL(map_proj_name, "OBLIQUE_CYLINDRICAL"))
    2192             :     {
    2193             :         // hope Swiss Oblique Cylindrical is the same
    2194             :         oSRS.SetSOC(center_lat, center_lon, 0, 0);
    2195             : #endif
    2196             :     }
    2197             :     else
    2198             :     {
    2199           0 :         CPLDebug("VICAR",
    2200             :                  "Dataset projection %s is not supported. Continuing...",
    2201             :                  map_proj_name.c_str());
    2202           0 :         bProjectionSet = false;
    2203             :     }
    2204             : 
    2205          27 :     if (bProjectionSet)
    2206             :     {
    2207             :         // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
    2208          81 :         const CPLString proj_target_name = map_proj_name + " " + target_name;
    2209          27 :         oSRS.SetProjCS(proj_target_name);  // set ProjCS keyword
    2210             : 
    2211             :         // The geographic/geocentric name will be the same basic name as the
    2212             :         // body name 'GCS' = Geographic/Geocentric Coordinate System
    2213          54 :         const CPLString geog_name = "GCS_" + target_name;
    2214             : 
    2215             :         // The datum and sphere names will be the same basic name aas the planet
    2216          54 :         const CPLString datum_name = "D_" + target_name;
    2217             : 
    2218          54 :         CPLString sphere_name = std::move(target_name);
    2219             : 
    2220             :         // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
    2221          27 :         double iflattening = 0.0;
    2222          27 :         if ((semi_major - semi_minor) < 0.0000001)
    2223          12 :             iflattening = 0;
    2224             :         else
    2225          15 :             iflattening = semi_major / (semi_major - semi_minor);
    2226             : 
    2227             :         // Set the body size but take into consideration which proj is being
    2228             :         // used to help w/ compatibility Notice that most PDS projections are
    2229             :         // spherical based on the fact that ISIS/PICS are spherical Set the body
    2230             :         // size but take into consideration which proj is being used to help w/
    2231             :         // proj4 compatibility The use of a Sphere, polar radius or ellipse here
    2232             :         // is based on how ISIS does it internally
    2233          27 :         if (((EQUAL(map_proj_name, "STEREOGRAPHIC") &&
    2234          54 :               (fabs(center_lat) == 90))) ||
    2235          27 :             (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC")))
    2236             :         {
    2237           0 :             if (bIsGeographic)
    2238             :             {
    2239             :                 // Geograpraphic, so set an ellipse
    2240           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
    2241             :                                iflattening, "Reference_Meridian", 0.0);
    2242             :             }
    2243             :             else
    2244             :             {
    2245             :                 // Geocentric, so force a sphere using the semi-minor axis. I
    2246             :                 // hope...
    2247           0 :                 sphere_name += "_polarRadius";
    2248           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_minor,
    2249             :                                0.0, "Reference_Meridian", 0.0);
    2250             :             }
    2251             :         }
    2252          27 :         else if ((EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
    2253          12 :                  (EQUAL(map_proj_name, "EQUIDISTANT")) ||
    2254          12 :                  (EQUAL(map_proj_name, "ORTHOGRAPHIC")) ||
    2255          51 :                  (EQUAL(map_proj_name, "STEREOGRAPHIC")) ||
    2256          12 :                  (EQUAL(map_proj_name, "SINUSOIDAL")))
    2257             :         {
    2258             :             // isis uses the spherical equation for these projections so force a
    2259             :             // sphere
    2260          27 :             oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
    2261             :                            "Reference_Meridian", 0.0);
    2262             :         }
    2263           0 :         else if (EQUAL(map_proj_name, "EQUIRECTANGULAR"))
    2264             :         {
    2265             :             // isis uses local radius as a sphere, which is pre-calculated in
    2266             :             // the PDS label as the semi-major
    2267           0 :             sphere_name += "_localRadius";
    2268           0 :             oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
    2269             :                            "Reference_Meridian", 0.0);
    2270             :         }
    2271             :         else
    2272             :         {
    2273             :             // All other projections: Mercator, Transverse Mercator, Lambert
    2274             :             // Conformal, etc. Geographic, so set an ellipse
    2275           0 :             if (bIsGeographic)
    2276             :             {
    2277           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
    2278             :                                iflattening, "Reference_Meridian", 0.0);
    2279             :             }
    2280             :             else
    2281             :             {
    2282             :                 // Geocentric, so force a sphere. I hope...
    2283           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
    2284             :                                0.0, "Reference_Meridian", 0.0);
    2285             :             }
    2286             :         }
    2287             : 
    2288          27 :         m_oSRS = std::move(oSRS);
    2289          27 :         m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2290             :     }
    2291          27 :     if (bProjectionSet)
    2292             :     {
    2293          27 :         m_bGotTransform = true;
    2294          27 :         m_gt[0] = dfULXMap;
    2295          27 :         m_gt[1] = dfXDim;
    2296          27 :         m_gt[2] = 0.0;
    2297          27 :         m_gt[3] = dfULYMap;
    2298          27 :         m_gt[4] = 0.0;
    2299          27 :         m_gt[5] = dfYDim;
    2300             :     }
    2301          27 : }
    2302             : 
    2303             : /************************************************************************/
    2304             : /*                    ReadProjectionFromGeoTIFFGroup()                  */
    2305             : /************************************************************************/
    2306             : 
    2307             : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
    2308          12 : void VICARDataset::ReadProjectionFromGeoTIFFGroup()
    2309             : {
    2310          12 :     m_bGeoRefFormatIsMIPL = true;
    2311             : 
    2312             :     // We will build a in-memory temporary GeoTIFF file from the VICAR GEOTIFF
    2313             :     // metadata items.
    2314             : 
    2315             :     const std::string osTmpFilename(
    2316          12 :         VSIMemGenerateHiddenFilename("vicar_tmp.tif"));
    2317             : 
    2318             :     /* -------------------------------------------------------------------- */
    2319             :     /*      Initialization of libtiff and libgeotiff.                       */
    2320             :     /* -------------------------------------------------------------------- */
    2321          12 :     GTiffOneTimeInit();
    2322          12 :     LibgeotiffOneTimeInit();
    2323             : 
    2324             :     /* -------------------------------------------------------------------- */
    2325             :     /*      Initialize access to the memory geotiff structure.              */
    2326             :     /* -------------------------------------------------------------------- */
    2327          12 :     VSILFILE *fpL = VSIFOpenL(osTmpFilename.c_str(), "w");
    2328          12 :     if (fpL == nullptr)
    2329           0 :         return;
    2330             : 
    2331          12 :     TIFF *hTIFF = VSI_TIFFOpen(osTmpFilename.c_str(), "w", fpL);
    2332             : 
    2333          12 :     if (hTIFF == nullptr)
    2334             :     {
    2335           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2336             :                  "TIFF/GeoTIFF structure is corrupt.");
    2337           0 :         CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
    2338           0 :         return;
    2339             :     }
    2340             : 
    2341             :     /* -------------------------------------------------------------------- */
    2342             :     /*      Write some minimal set of image parameters.                     */
    2343             :     /* -------------------------------------------------------------------- */
    2344          12 :     TIFFSetField(hTIFF, TIFFTAG_IMAGEWIDTH, 1);
    2345          12 :     TIFFSetField(hTIFF, TIFFTAG_IMAGELENGTH, 1);
    2346          12 :     TIFFSetField(hTIFF, TIFFTAG_BITSPERSAMPLE, 8);
    2347          12 :     TIFFSetField(hTIFF, TIFFTAG_SAMPLESPERPIXEL, 1);
    2348          12 :     TIFFSetField(hTIFF, TIFFTAG_ROWSPERSTRIP, 1);
    2349          12 :     TIFFSetField(hTIFF, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG);
    2350          12 :     TIFFSetField(hTIFF, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK);
    2351             : 
    2352             :     /* -------------------------------------------------------------------- */
    2353             :     /*      Write geotiff keys from VICAR metadata                          */
    2354             :     /* -------------------------------------------------------------------- */
    2355          12 :     GTIF *hGTIF = GTIFNew(hTIFF);
    2356          12 :     CPLAssert(hGTIF);
    2357             : 
    2358          60 :     for (const auto &gkey : GTiffAsciiKeys)
    2359             :     {
    2360          48 :         const char *pszValue = GetKeyword(
    2361          96 :             ("GEOTIFF." + CPLString(GTIFKeyName(gkey)).toupper()).c_str(),
    2362             :             nullptr);
    2363          48 :         if (pszValue)
    2364             :         {
    2365           8 :             GTIFKeySet(hGTIF, gkey, TYPE_ASCII,
    2366           8 :                        static_cast<int>(strlen(pszValue)), pszValue);
    2367             :         }
    2368             :     }
    2369             : 
    2370         372 :     for (const auto &gkey : GTiffDoubleKeys)
    2371             :     {
    2372         360 :         const char *pszValue = GetKeyword(
    2373         720 :             ("GEOTIFF." + CPLString(GTIFKeyName(gkey)).toupper()).c_str(),
    2374             :             nullptr);
    2375         360 :         if (pszValue)
    2376             :         {
    2377          24 :             GTIFKeySet(hGTIF, gkey, TYPE_DOUBLE, 1, CPLAtof(pszValue));
    2378             :         }
    2379             :     }
    2380             : 
    2381         204 :     for (const auto &gkey : GTiffShortKeys)
    2382             :     {
    2383         192 :         const char *pszValue = GetKeyword(
    2384         384 :             ("GEOTIFF." + CPLString(GTIFKeyName(gkey)).toupper()).c_str(),
    2385             :             nullptr);
    2386         192 :         if (pszValue)
    2387             :         {
    2388          40 :             GTIFKeySet(hGTIF, gkey, TYPE_SHORT, 1, atoi(pszValue));
    2389             :         }
    2390             :     }
    2391             : 
    2392          12 :     GTIFWriteKeys(hGTIF);
    2393          12 :     GTIFFree(hGTIF);
    2394             : 
    2395             :     /* -------------------------------------------------------------------- */
    2396             :     /*      Write geotiff tags from VICAR metadata                          */
    2397             :     /* -------------------------------------------------------------------- */
    2398             : 
    2399             :     const std::map<const char *, int> oMapTagNameToCode = {
    2400             :         {"MODELPIXELSCALETAG", TIFFTAG_GEOPIXELSCALE},
    2401             :         {"MODELTIEPOINTTAG", TIFFTAG_GEOTIEPOINTS},
    2402             :         {"MODELTRANSFORMATIONTAG", TIFFTAG_GEOTRANSMATRIX},
    2403          24 :     };
    2404             : 
    2405          48 :     for (const auto &kv : oMapTagNameToCode)
    2406             :     {
    2407             :         const char *pszValue =
    2408          36 :             GetKeyword((std::string("GEOTIFF.") + kv.first).c_str(), nullptr);
    2409          36 :         if (pszValue)
    2410             :         {
    2411             :             // Remove leading ( and trailing ), and replace comma by space
    2412             :             // to separate on it.
    2413             :             const CPLStringList aosTokens(
    2414          24 :                 CSLTokenizeString2(CPLString(pszValue)
    2415          48 :                                        .replaceAll('(', "")
    2416          48 :                                        .replaceAll(')', "")
    2417          24 :                                        .replaceAll(',', ' ')
    2418             :                                        .c_str(),
    2419          48 :                                    " ", 0));
    2420          24 :             if (!aosTokens.empty())
    2421             :             {
    2422          48 :                 std::vector<double> adfValues;
    2423         132 :                 for (int i = 0; i < aosTokens.size(); ++i)
    2424         108 :                     adfValues.push_back(CPLAtof(aosTokens[i]));
    2425          24 :                 TIFFSetField(hTIFF, kv.second, aosTokens.size(), &adfValues[0]);
    2426             :             }
    2427             :         }
    2428             :     }
    2429             : 
    2430             :     /* -------------------------------------------------------------------- */
    2431             :     /*      Finalize the geotiff file.                                      */
    2432             :     /* -------------------------------------------------------------------- */
    2433             : 
    2434          12 :     char bySmallImage = 0;
    2435             : 
    2436          12 :     TIFFWriteEncodedStrip(hTIFF, 0, &bySmallImage, 1);
    2437          12 :     TIFFWriteDirectory(hTIFF);
    2438             : 
    2439          12 :     XTIFFClose(hTIFF);
    2440          12 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fpL));
    2441             : 
    2442             :     /* -------------------------------------------------------------------- */
    2443             :     /*      Get georeferencing from file.                                   */
    2444             :     /* -------------------------------------------------------------------- */
    2445             :     auto poGTiffDS =
    2446          24 :         std::unique_ptr<GDALDataset>(GDALDataset::Open(osTmpFilename.c_str()));
    2447          12 :     if (poGTiffDS)
    2448             :     {
    2449          12 :         auto poSRS = poGTiffDS->GetSpatialRef();
    2450          12 :         if (poSRS)
    2451           4 :             m_oSRS = *poSRS;
    2452             : 
    2453          12 :         if (poGTiffDS->GetGeoTransform(m_gt) == CE_None)
    2454             :         {
    2455          12 :             m_bGotTransform = true;
    2456             :         }
    2457             : 
    2458             :         const char *pszAreaOrPoint =
    2459          12 :             poGTiffDS->GetMetadataItem(GDALMD_AREA_OR_POINT);
    2460          12 :         if (pszAreaOrPoint)
    2461           4 :             GDALDataset::SetMetadataItem(GDALMD_AREA_OR_POINT, pszAreaOrPoint);
    2462             :     }
    2463             : 
    2464          12 :     VSIUnlink(osTmpFilename.c_str());
    2465             : }
    2466             : #endif
    2467             : 
    2468             : /************************************************************************/
    2469             : /*                                Open()                                */
    2470             : /************************************************************************/
    2471             : 
    2472         115 : GDALDataset *VICARDataset::Open(GDALOpenInfo *poOpenInfo)
    2473             : {
    2474             :     /* -------------------------------------------------------------------- */
    2475             :     /*      Does this look like a VICAR dataset?                            */
    2476             :     /* -------------------------------------------------------------------- */
    2477         115 :     const vsi_l_offset nLabelOffset = VICARGetLabelOffset(poOpenInfo);
    2478         115 :     if (nLabelOffset == static_cast<vsi_l_offset>(-1))
    2479           0 :         return nullptr;
    2480         115 :     if (nLabelOffset > 0)
    2481             :     {
    2482           4 :         CPLString osSubFilename;
    2483             :         osSubFilename.Printf("/vsisubfile/" CPL_FRMT_GUIB ",%s",
    2484             :                              static_cast<GUIntBig>(nLabelOffset),
    2485           2 :                              poOpenInfo->pszFilename);
    2486           4 :         GDALOpenInfo oOpenInfo(osSubFilename.c_str(), poOpenInfo->eAccess);
    2487           2 :         return Open(&oOpenInfo);
    2488             :     }
    2489             : 
    2490         226 :     auto poDS = std::make_unique<VICARDataset>();
    2491         113 :     poDS->fpImage = poOpenInfo->fpL;
    2492         113 :     poOpenInfo->fpL = nullptr;
    2493         113 :     if (!poDS->oKeywords.Ingest(poDS->fpImage, poOpenInfo->pabyHeader))
    2494             :     {
    2495           0 :         return nullptr;
    2496             :     }
    2497             : 
    2498             :     /************ CHECK INSTRUMENT/DATA *****************/
    2499             : 
    2500         113 :     bool bIsDTM = false;
    2501         113 :     const char *value = poDS->GetKeyword("DTM.DTM_OFFSET");
    2502         113 :     if (!EQUAL(value, ""))
    2503             :     {
    2504           0 :         bIsDTM = true;
    2505             :     }
    2506             : 
    2507         113 :     bool bInstKnown = false;
    2508             :     // Check for HRSC
    2509         113 :     if (EQUAL(poDS->GetKeyword("BLTYPE"), "M94_HRSC"))
    2510           8 :         bInstKnown = true;
    2511             :     // Check for Framing Camera on Dawn
    2512         105 :     else if (EQUAL(poDS->GetKeyword("INSTRUMENT_ID"), "FC2"))
    2513           0 :         bInstKnown = true;
    2514             : 
    2515             :     /************ Grab dimensions *****************/
    2516             : 
    2517         113 :     const int nCols = atoi(poDS->GetKeyword("NS"));
    2518         113 :     const int nRows = atoi(poDS->GetKeyword("NL"));
    2519         113 :     const int nBands = atoi(poDS->GetKeyword("NB"));
    2520             : 
    2521         226 :     if (!GDALCheckDatasetDimensions(nCols, nRows) ||
    2522         113 :         !GDALCheckBandCount(nBands, false))
    2523             :     {
    2524           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2525             :                  "File %s appears to be a VICAR file, but failed to find some "
    2526             :                  "required keywords.",
    2527             :                  poOpenInfo->pszFilename);
    2528           0 :         return nullptr;
    2529             :     }
    2530             : 
    2531             :     const GDALDataType eDataType =
    2532         113 :         GetDataTypeFromFormat(poDS->GetKeyword("FORMAT"));
    2533         113 :     if (eDataType == GDT_Unknown)
    2534             :     {
    2535           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    2536             :                  "Could not find known VICAR label entries!\n");
    2537           0 :         return nullptr;
    2538             :     }
    2539         113 :     double dfNoData = 0.0;
    2540         113 :     if (eDataType == GDT_Byte)
    2541             :     {
    2542          51 :         dfNoData = VICAR_NULL1;
    2543             :     }
    2544          62 :     else if (eDataType == GDT_Int16)
    2545             :     {
    2546          15 :         dfNoData = VICAR_NULL2;
    2547             :     }
    2548          47 :     else if (eDataType == GDT_Float32)
    2549             :     {
    2550          20 :         dfNoData = VICAR_NULL3;
    2551             :     }
    2552             : 
    2553             :     /***** CHECK ENDIANNESS **************/
    2554             : 
    2555         113 :     RawRasterBand::ByteOrder eByteOrder =
    2556             :         RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
    2557         113 :     if (GDALDataTypeIsInteger(eDataType))
    2558             :     {
    2559          71 :         value = poDS->GetKeyword("INTFMT", "LOW");
    2560          71 :         if (EQUAL(value, "LOW"))
    2561             :         {
    2562          68 :             eByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
    2563             :         }
    2564           3 :         else if (EQUAL(value, "HIGH"))
    2565             :         {
    2566           3 :             eByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
    2567             :         }
    2568             :         else
    2569             :         {
    2570           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2571             :                      "INTFMT=%s layout not supported.", value);
    2572           0 :             return nullptr;
    2573             :         }
    2574             :     }
    2575             :     else
    2576             :     {
    2577          42 :         value = poDS->GetKeyword("REALFMT", "VAX");
    2578          42 :         if (EQUAL(value, "RIEEE"))
    2579             :         {
    2580          29 :             eByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
    2581             :         }
    2582          13 :         else if (EQUAL(value, "IEEE"))
    2583             :         {
    2584           4 :             eByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
    2585             :         }
    2586           9 :         else if (EQUAL(value, "VAX"))
    2587             :         {
    2588           9 :             eByteOrder = RawRasterBand::ByteOrder::ORDER_VAX;
    2589             :         }
    2590             :         else
    2591             :         {
    2592           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2593             :                      "REALFMT=%s layout not supported.", value);
    2594           0 :             return nullptr;
    2595             :         }
    2596             :     }
    2597             : 
    2598             :     /* -------------------------------------------------------------------- */
    2599             :     /*      Capture some information from the file that is of interest.     */
    2600             :     /* -------------------------------------------------------------------- */
    2601         113 :     poDS->nRasterXSize = nCols;
    2602         113 :     poDS->nRasterYSize = nRows;
    2603             : 
    2604         113 :     if (poDS->GetKeyword("MAP.MAP_PROJECTION_TYPE")[0] != '\0')
    2605             :     {
    2606          27 :         poDS->ReadProjectionFromMapGroup();
    2607             :     }
    2608             : #if defined(HAVE_TIFF) && defined(HAVE_GEOTIFF)
    2609         168 :     else if (poDS->GetKeyword("GEOTIFF.GTMODELTYPEGEOKEY")[0] != '\0' ||
    2610          82 :              poDS->GetKeyword("GEOTIFF.MODELTIEPOINTTAG")[0] != '\0')
    2611             :     {
    2612          12 :         poDS->ReadProjectionFromGeoTIFFGroup();
    2613             :     }
    2614             : #endif
    2615             : 
    2616         113 :     if (!poDS->m_bGotTransform)
    2617         148 :         poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
    2618         148 :             poOpenInfo->pszFilename, "wld", poDS->m_gt.data()));
    2619             : 
    2620         113 :     poDS->eAccess = poOpenInfo->eAccess;
    2621         113 :     poDS->m_oJSonLabel = poDS->oKeywords.GetJsonObject();
    2622             : 
    2623             :     /* -------------------------------------------------------------------- */
    2624             :     /*      Compute the line offsets.                                        */
    2625             :     /* -------------------------------------------------------------------- */
    2626             : 
    2627             :     uint64_t nPixelOffset;
    2628             :     uint64_t nLineOffset;
    2629             :     uint64_t nBandOffset;
    2630             :     uint64_t nImageOffsetWithoutNBB;
    2631             :     uint64_t nNBB;
    2632             :     uint64_t nImageSize;
    2633         113 :     if (!GetSpacings(poDS->oKeywords, nPixelOffset, nLineOffset, nBandOffset,
    2634         226 :                      nImageOffsetWithoutNBB, nNBB, nImageSize) ||
    2635         113 :         nImageOffsetWithoutNBB > std::numeric_limits<uint64_t>::max() -
    2636         113 :                                      (nNBB + nBandOffset * (nBands - 1)))
    2637             :     {
    2638           0 :         CPLDebug("VICAR", "Invalid spacings found");
    2639           0 :         return nullptr;
    2640             :     }
    2641             : 
    2642         113 :     poDS->m_nRecordSize = atoi(poDS->GetKeyword("RECSIZE", ""));
    2643             : 
    2644         113 :     if (nNBB != 0)
    2645             :     {
    2646           2 :         const char *pszBLType = poDS->GetKeyword("BLTYPE", nullptr);
    2647             : #ifdef USE_ONLY_EMBEDDED_RESOURCE_FILES
    2648             :         const char *pszVicarConf = nullptr;
    2649             : #else
    2650           2 :         const char *pszVicarConf = CPLFindFile("gdal", "vicar.json");
    2651             : #endif
    2652           4 :         CPLJSONDocument oDoc;
    2653           2 :         if (!pszVicarConf || EQUAL(pszVicarConf, "vicar.json"))
    2654             :         {
    2655             : #ifdef EMBED_RESOURCE_FILES
    2656             :             oDoc.LoadMemory(VICARGetEmbeddedConf());
    2657             :             pszVicarConf = "__embedded__";
    2658             : #endif
    2659             :         }
    2660             : 
    2661           2 :         if (pszBLType && pszVicarConf && poDS->m_nRecordSize > 0)
    2662             :         {
    2663             : 
    2664           2 :             RawRasterBand::ByteOrder eBINTByteOrder =
    2665             :                 RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
    2666           2 :             value = poDS->GetKeyword("BINTFMT", "LOW");
    2667           2 :             if (EQUAL(value, "LOW"))
    2668             :             {
    2669           2 :                 eBINTByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
    2670             :             }
    2671           0 :             else if (EQUAL(value, "HIGH"))
    2672             :             {
    2673           0 :                 eBINTByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
    2674             :             }
    2675             :             else
    2676             :             {
    2677           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2678             :                          "BINTFMT=%s layout not supported.", value);
    2679             :             }
    2680             : 
    2681           2 :             RawRasterBand::ByteOrder eBREALByteOrder =
    2682             :                 RawRasterBand::ByteOrder::ORDER_VAX;
    2683           2 :             value = poDS->GetKeyword("BREALFMT", "VAX");
    2684           2 :             if (EQUAL(value, "RIEEE"))
    2685             :             {
    2686           2 :                 eBREALByteOrder = RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN;
    2687             :             }
    2688           0 :             else if (EQUAL(value, "IEEE"))
    2689             :             {
    2690           0 :                 eBREALByteOrder = RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN;
    2691             :             }
    2692           0 :             else if (EQUAL(value, "VAX"))
    2693             :             {
    2694           0 :                 eBREALByteOrder = RawRasterBand::ByteOrder::ORDER_VAX;
    2695             :             }
    2696             :             else
    2697             :             {
    2698           0 :                 CPLError(CE_Failure, CPLE_NotSupported,
    2699             :                          "BREALFMT=%s layout not supported.", value);
    2700             :             }
    2701             : 
    2702           2 :             if (EQUAL(pszVicarConf, "__embedded__") || oDoc.Load(pszVicarConf))
    2703             :             {
    2704           4 :                 const auto oRoot = oDoc.GetRoot();
    2705           2 :                 if (oRoot.GetType() == CPLJSONObject::Type::Object)
    2706             :                 {
    2707           6 :                     auto oDef = oRoot.GetObj(pszBLType);
    2708           8 :                     if (oDef.IsValid() &&
    2709           4 :                         oDef.GetType() == CPLJSONObject::Type::Object &&
    2710           4 :                         static_cast<GUInt64>(oDef.GetInteger("size")) == nNBB)
    2711             :                     {
    2712             :                         auto poLayer =
    2713             :                             std::unique_ptr<OGRVICARBinaryPrefixesLayer>(
    2714             :                                 new OGRVICARBinaryPrefixesLayer(
    2715           2 :                                     poDS->fpImage,
    2716           4 :                                     static_cast<int>(nImageSize /
    2717           2 :                                                      poDS->m_nRecordSize),
    2718             :                                     oDef, nImageOffsetWithoutNBB,
    2719           2 :                                     poDS->m_nRecordSize, eBINTByteOrder,
    2720           4 :                                     eBREALByteOrder));
    2721           2 :                         if (!poLayer->HasError())
    2722             :                         {
    2723           2 :                             poDS->m_poLayer = std::move(poLayer);
    2724             :                         }
    2725             :                     }
    2726             :                 }
    2727             :             }
    2728             :         }
    2729             :     }
    2730             : 
    2731         113 :     poDS->m_nImageOffsetWithoutNBB =
    2732             :         static_cast<vsi_l_offset>(nImageOffsetWithoutNBB);
    2733             : 
    2734         226 :     CPLString osCompress = poDS->GetKeyword("COMPRESS", "NONE");
    2735         113 :     if (EQUAL(osCompress, "BASIC") || EQUAL(osCompress, "BASIC2"))
    2736             :     {
    2737          16 :         if (poOpenInfo->eAccess == GA_Update)
    2738             :         {
    2739           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2740             :                      "Update of compressed VICAR file not supported");
    2741           0 :             return nullptr;
    2742             :         }
    2743          16 :         poDS->SetMetadataItem("COMPRESS", osCompress, "IMAGE_STRUCTURE");
    2744          16 :         poDS->m_eCompress =
    2745          16 :             EQUAL(osCompress, "BASIC") ? COMPRESS_BASIC : COMPRESS_BASIC2;
    2746          16 :         if (poDS->nRasterYSize > 100 * 1000 * 1000 / nBands)
    2747             :         {
    2748           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2749             :                      "Too many records for compressed dataset");
    2750           0 :             return nullptr;
    2751             :         }
    2752          16 :         if (!GDALDataTypeIsInteger(eDataType))
    2753             :         {
    2754           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    2755             :                      "Data type incompatible of compression");
    2756           0 :             return nullptr;
    2757             :         }
    2758             :         // To avoid potential issues in basic_decode()
    2759          16 :         const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
    2760          16 :         if (nDTSize == 0 || poDS->nRasterXSize > INT_MAX / nDTSize)
    2761             :         {
    2762           0 :             CPLError(CE_Failure, CPLE_NotSupported, "Too large scanline");
    2763           0 :             return nullptr;
    2764             :         }
    2765          16 :         const int nRecords = poDS->nRasterYSize * nBands;
    2766             :         try
    2767             :         {
    2768             :             // + 1 to store implicitly the size of the last record
    2769          16 :             poDS->m_anRecordOffsets.resize(nRecords + 1);
    2770             :         }
    2771           0 :         catch (const std::exception &e)
    2772             :         {
    2773           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    2774           0 :             return nullptr;
    2775             :         }
    2776          16 :         if (poDS->m_eCompress == COMPRESS_BASIC)
    2777             :         {
    2778           5 :             poDS->m_anRecordOffsets[0] =
    2779           5 :                 poDS->m_nImageOffsetWithoutNBB + sizeof(GUInt32);
    2780             :         }
    2781             :         else
    2782             :         {
    2783          11 :             poDS->m_anRecordOffsets[0] =
    2784          11 :                 poDS->m_nImageOffsetWithoutNBB + sizeof(GUInt32) * nRecords;
    2785             :         }
    2786             :     }
    2787          97 :     else if (!EQUAL(osCompress, "NONE"))
    2788             :     {
    2789           0 :         CPLError(CE_Failure, CPLE_NotSupported, "COMPRESS=%s not supported",
    2790             :                  osCompress.c_str());
    2791           0 :         return nullptr;
    2792             :     }
    2793             : 
    2794             :     /* -------------------------------------------------------------------- */
    2795             :     /*      Create band information objects.                                */
    2796             :     /* -------------------------------------------------------------------- */
    2797         263 :     for (int i = 0; i < nBands; i++)
    2798             :     {
    2799           0 :         std::unique_ptr<GDALRasterBand> poBand;
    2800             : 
    2801         295 :         if (poDS->m_eCompress == COMPRESS_BASIC ||
    2802         145 :             poDS->m_eCompress == COMPRESS_BASIC2)
    2803             :         {
    2804          32 :             poBand = std::make_unique<VICARBASICRasterBand>(poDS.get(), i + 1,
    2805          16 :                                                             eDataType);
    2806             :         }
    2807             :         else
    2808             :         {
    2809             :             auto poRawBand = std::make_unique<VICARRawRasterBand>(
    2810         134 :                 poDS.get(), i + 1, poDS->fpImage,
    2811           0 :                 static_cast<vsi_l_offset>(nImageOffsetWithoutNBB + nNBB +
    2812         134 :                                           nBandOffset * i),
    2813         134 :                 static_cast<int>(nPixelOffset), static_cast<int>(nLineOffset),
    2814         134 :                 eDataType, eByteOrder);
    2815         134 :             if (!poRawBand->IsValid())
    2816             :             {
    2817           0 :                 return nullptr;
    2818             :             }
    2819         134 :             poBand = std::move(poRawBand);
    2820             :         }
    2821             : 
    2822             :         // only set NoData if instrument is supported
    2823         150 :         if (bInstKnown)
    2824           8 :             poBand->SetNoDataValue(dfNoData);
    2825         150 :         if (bIsDTM)
    2826             :         {
    2827           0 :             poBand->SetScale(static_cast<double>(
    2828           0 :                 CPLAtof(poDS->GetKeyword("DTM.DTM_SCALING_FACTOR"))));
    2829           0 :             poBand->SetOffset(static_cast<double>(
    2830           0 :                 CPLAtof(poDS->GetKeyword("DTM.DTM_OFFSET"))));
    2831             :             const char *pszMin =
    2832           0 :                 poDS->GetKeyword("DTM.DTM_MINIMUM_DN", nullptr);
    2833             :             const char *pszMax =
    2834           0 :                 poDS->GetKeyword("DTM.DTM_MAXIMUM_DN", nullptr);
    2835           0 :             if (pszMin != nullptr && pszMax != nullptr)
    2836           0 :                 poBand->SetStatistics(CPLAtofM(pszMin), CPLAtofM(pszMax), 0, 0);
    2837             :             const char *pszNoData =
    2838           0 :                 poDS->GetKeyword("DTM.DTM_MISSING_DN", nullptr);
    2839           0 :             if (pszNoData != nullptr)
    2840           0 :                 poBand->SetNoDataValue(CPLAtofM(pszNoData));
    2841             :         }
    2842         150 :         else if (EQUAL(poDS->GetKeyword("BLTYPE"), "M94_HRSC"))
    2843             :         {
    2844           8 :             double scale = CPLAtof(
    2845             :                 poDS->GetKeyword("DLRTO8.REFLECTANCE_SCALING_FACTOR", "-1."));
    2846           8 :             if (scale < 0.)
    2847             :             {
    2848           0 :                 scale = CPLAtof(
    2849             :                     poDS->GetKeyword("HRCAL.REFLECTANCE_SCALING_FACTOR", "1."));
    2850             :             }
    2851           8 :             poBand->SetScale(scale);
    2852             :             double offset =
    2853           8 :                 CPLAtof(poDS->GetKeyword("DLRTO8.REFLECTANCE_OFFSET", "-1."));
    2854           8 :             if (offset < 0.)
    2855             :             {
    2856             :                 offset =
    2857           0 :                     CPLAtof(poDS->GetKeyword("HRCAL.REFLECTANCE_OFFSET", "0."));
    2858             :             }
    2859           8 :             poBand->SetOffset(offset);
    2860             :         }
    2861         150 :         const char *pszMin = poDS->GetKeyword("STATISTICS.MINIMUM", nullptr);
    2862         150 :         const char *pszMax = poDS->GetKeyword("STATISTICS.MAXIMUM", nullptr);
    2863         150 :         const char *pszMean = poDS->GetKeyword("STATISTICS.MEAN", nullptr);
    2864             :         const char *pszStdDev =
    2865         150 :             poDS->GetKeyword("STATISTICS.STANDARD_DEVIATION", nullptr);
    2866         150 :         if (pszMin != nullptr && pszMax != nullptr && pszMean != nullptr &&
    2867             :             pszStdDev != nullptr)
    2868           0 :             poBand->SetStatistics(CPLAtofM(pszMin), CPLAtofM(pszMax),
    2869           0 :                                   CPLAtofM(pszMean), CPLAtofM(pszStdDev));
    2870             : 
    2871         150 :         poDS->SetBand(i + 1, std::move(poBand));
    2872             :     }
    2873             : 
    2874             :     /* -------------------------------------------------------------------- */
    2875             :     /*      Instrument-specific keywords as metadata.                       */
    2876             :     /* -------------------------------------------------------------------- */
    2877             : 
    2878             :     /******************   HRSC    ******************************/
    2879             : 
    2880         113 :     if (EQUAL(poDS->GetKeyword("BLTYPE"), "M94_HRSC"))
    2881             :     {
    2882          16 :         poDS->SetMetadataItem(
    2883             :             "SPACECRAFT_NAME",
    2884           8 :             poDS->GetKeyword("M94_INSTRUMENT.INSTRUMENT_HOST_NAME"));
    2885           8 :         poDS->SetMetadataItem("PRODUCT_TYPE", poDS->GetKeyword("TYPE"));
    2886             : 
    2887           8 :         if (EQUAL(poDS->GetKeyword("M94_INSTRUMENT.DETECTOR_ID"),
    2888             :                   "MEX_HRSC_SRC"))
    2889             :         {
    2890             :             static const char *const apszKeywords[] = {
    2891             :                 "M94_ORBIT.IMAGE_TIME",
    2892             :                 "FILE.EVENT_TYPE",
    2893             :                 "FILE.PROCESSING_LEVEL_ID",
    2894             :                 "M94_INSTRUMENT.DETECTOR_ID",
    2895             :                 "M94_CAMERAS.EXPOSURE_DURATION",
    2896             :                 "HRCONVER.INSTRUMENT_TEMPERATURE",
    2897             :                 nullptr};
    2898           0 :             for (int i = 0; apszKeywords[i] != nullptr; i++)
    2899             :             {
    2900           0 :                 const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
    2901           0 :                 if (pszKeywordValue != nullptr)
    2902           0 :                     poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
    2903             :             }
    2904             :         }
    2905             :         else
    2906             :         {
    2907             :             static const char *const apszKeywords[] = {
    2908             :                 "M94_ORBIT.START_TIME",
    2909             :                 "M94_ORBIT.STOP_TIME",
    2910             :                 "M94_INSTRUMENT.DETECTOR_ID",
    2911             :                 "M94_CAMERAS.MACROPIXEL_SIZE",
    2912             :                 "FILE.EVENT_TYPE",
    2913             :                 "M94_INSTRUMENT.MISSION_PHASE_NAME",
    2914             :                 "HRORTHO.SPICE_FILE_NAME",
    2915             :                 "HRCONVER.MISSING_FRAMES",
    2916             :                 "HRCONVER.OVERFLOW_FRAMES",
    2917             :                 "HRCONVER.ERROR_FRAMES",
    2918             :                 "HRFOOT.BEST_GROUND_SAMPLING_DISTANCE",
    2919             :                 "DLRTO8.RADIANCE_SCALING_FACTOR",
    2920             :                 "DLRTO8.RADIANCE_OFFSET",
    2921             :                 "DLRTO8.REFLECTANCE_SCALING_FACTOR",
    2922             :                 "DLRTO8.REFLECTANCE_OFFSET",
    2923             :                 "HRCAL.RADIANCE_SCALING_FACTOR",
    2924             :                 "HRCAL.RADIANCE_OFFSET",
    2925             :                 "HRCAL.REFLECTANCE_SCALING_FACTOR",
    2926             :                 "HRCAL.REFLECTANCE_OFFSET",
    2927             :                 "HRORTHO.DTM_NAME",
    2928             :                 "HRORTHO.EXTORI_FILE_NAME",
    2929             :                 "HRORTHO.GEOMETRIC_CALIB_FILE_NAME",
    2930             :                 nullptr};
    2931         184 :             for (int i = 0; apszKeywords[i] != nullptr; i++)
    2932             :             {
    2933             :                 const char *pszKeywordValue =
    2934         176 :                     poDS->GetKeyword(apszKeywords[i], nullptr);
    2935         176 :                 if (pszKeywordValue != nullptr)
    2936         144 :                     poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
    2937             :             }
    2938             :         }
    2939             :     }
    2940         113 :     if (bIsDTM && EQUAL(poDS->GetKeyword("MAP.TARGET_NAME"), "MARS"))
    2941             :     {
    2942           0 :         poDS->SetMetadataItem("SPACECRAFT_NAME", "MARS_EXPRESS");
    2943           0 :         poDS->SetMetadataItem("PRODUCT_TYPE", "DTM");
    2944             :         static const char *const apszKeywords[] = {
    2945             :             "DTM.DTM_MISSING_DN",     "DTM.DTM_OFFSET",
    2946             :             "DTM.DTM_SCALING_FACTOR", "DTM.DTM_A_AXIS_RADIUS",
    2947             :             "DTM.DTM_B_AXIS_RADIUS",  "DTM.DTM_C_AXIS_RADIUS",
    2948             :             "DTM.DTM_DESC",           "DTM.DTM_MINIMUM_DN",
    2949             :             "DTM.DTM_MAXIMUM_DN",     nullptr};
    2950           0 :         for (int i = 0; apszKeywords[i] != nullptr; i++)
    2951             :         {
    2952           0 :             const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
    2953           0 :             if (pszKeywordValue != nullptr)
    2954           0 :                 poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
    2955             :         }
    2956             :     }
    2957             : 
    2958             :     /******************   DAWN   ******************************/
    2959         113 :     else if (EQUAL(poDS->GetKeyword("INSTRUMENT_ID"), "FC2"))
    2960             :     {
    2961           0 :         poDS->SetMetadataItem("SPACECRAFT_NAME", "DAWN");
    2962             :         static const char *const apszKeywords[] = {
    2963             :             "ORBIT_NUMBER",
    2964             :             "FILTER_NUMBER",
    2965             :             "FRONT_DOOR_STATUS",
    2966             :             "FIRST_LINE",
    2967             :             "FIRST_LINE_SAMPLE",
    2968             :             "PRODUCER_INSTITUTION_NAME",
    2969             :             "SOURCE_FILE_NAME",
    2970             :             "PROCESSING_LEVEL_ID",
    2971             :             "TARGET_NAME",
    2972             :             "LIMB_IN_IMAGE",
    2973             :             "POLE_IN_IMAGE",
    2974             :             "REFLECTANCE_SCALING_FACTOR",
    2975             :             "SPICE_FILE_NAME",
    2976             :             "SPACECRAFT_CENTRIC_LATITUDE",
    2977             :             "SPACECRAFT_EASTERN_LONGITUDE",
    2978             :             "FOOTPRINT_POSITIVE_LONGITUDE",
    2979             :             nullptr};
    2980           0 :         for (int i = 0; apszKeywords[i] != nullptr; i++)
    2981             :         {
    2982           0 :             const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
    2983           0 :             if (pszKeywordValue != nullptr)
    2984           0 :                 poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
    2985             :         }
    2986             :     }
    2987         113 :     else if (bIsDTM && (EQUAL(poDS->GetKeyword("TARGET_NAME"), "VESTA") ||
    2988           0 :                         EQUAL(poDS->GetKeyword("TARGET_NAME"), "CERES")))
    2989             :     {
    2990           0 :         poDS->SetMetadataItem("SPACECRAFT_NAME", "DAWN");
    2991           0 :         poDS->SetMetadataItem("PRODUCT_TYPE", "DTM");
    2992             :         static const char *const apszKeywords[] = {
    2993             :             "DTM_MISSING_DN",
    2994             :             "DTM_OFFSET",
    2995             :             "DTM_SCALING_FACTOR",
    2996             :             "DTM_A_AXIS_RADIUS",
    2997             :             "DTM_B_AXIS_RADIUS",
    2998             :             "DTM_C_AXIS_RADIUS",
    2999             :             "DTM_MINIMUM_DN",
    3000             :             "DTM_MAXIMUM_DN",
    3001             :             "MAP_PROJECTION_TYPE",
    3002             :             "COORDINATE_SYSTEM_NAME",
    3003             :             "POSITIVE_LONGITUDE_DIRECTION",
    3004             :             "MAP_SCALE",
    3005             :             "CENTER_LONGITUDE",
    3006             :             "LINE_PROJECTION_OFFSET",
    3007             :             "SAMPLE_PROJECTION_OFFSET",
    3008             :             nullptr};
    3009           0 :         for (int i = 0; apszKeywords[i] != nullptr; i++)
    3010             :         {
    3011           0 :             const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
    3012           0 :             if (pszKeywordValue != nullptr)
    3013           0 :                 poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
    3014             :         }
    3015             :     }
    3016             : 
    3017             :     /* -------------------------------------------------------------------- */
    3018             :     /*      Initialize any PAM information.                                 */
    3019             :     /* -------------------------------------------------------------------- */
    3020         113 :     poDS->TryLoadXML();
    3021             : 
    3022             :     /* -------------------------------------------------------------------- */
    3023             :     /*      Check for overviews.                                            */
    3024             :     /* -------------------------------------------------------------------- */
    3025         113 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
    3026             : 
    3027         113 :     return poDS.release();
    3028             : }
    3029             : 
    3030             : /************************************************************************/
    3031             : /*                             GetKeyword()                             */
    3032             : /************************************************************************/
    3033             : 
    3034        3561 : const char *VICARDataset::GetKeyword(const char *pszPath,
    3035             :                                      const char *pszDefault)
    3036             : 
    3037             : {
    3038        3561 :     return oKeywords.GetKeyword(pszPath, pszDefault);
    3039             : }
    3040             : 
    3041             : /************************************************************************/
    3042             : /*                        GetDataTypeFromFormat()                       */
    3043             : /************************************************************************/
    3044             : 
    3045         276 : GDALDataType VICARDataset::GetDataTypeFromFormat(const char *pszFormat)
    3046             : {
    3047         276 :     if (EQUAL(pszFormat, "BYTE"))
    3048         110 :         return GDT_Byte;
    3049             : 
    3050         166 :     if (EQUAL(pszFormat, "HALF") || EQUAL(pszFormat, "WORD"))
    3051          39 :         return GDT_Int16;
    3052             : 
    3053         127 :     if (EQUAL(pszFormat, "FULL") || EQUAL(pszFormat, "LONG"))
    3054          13 :         return GDT_Int32;
    3055             : 
    3056         114 :     if (EQUAL(pszFormat, "REAL"))
    3057          56 :         return GDT_Float32;
    3058             : 
    3059          58 :     if (EQUAL(pszFormat, "DOUB"))
    3060          29 :         return GDT_Float64;
    3061             : 
    3062          29 :     if (EQUAL(pszFormat, "COMP") || EQUAL(pszFormat, "COMPLEX"))
    3063          29 :         return GDT_CFloat32;
    3064             : 
    3065           0 :     return GDT_Unknown;
    3066             : }
    3067             : 
    3068             : /************************************************************************/
    3069             : /*                             GetSpacings()                            */
    3070             : /************************************************************************/
    3071             : 
    3072         163 : bool VICARDataset::GetSpacings(const VICARKeywordHandler &keywords,
    3073             :                                uint64_t &nPixelOffset, uint64_t &nLineOffset,
    3074             :                                uint64_t &nBandOffset,
    3075             :                                uint64_t &nImageOffsetWithoutNBB, uint64_t &nNBB,
    3076             :                                uint64_t &nImageSize)
    3077             : {
    3078             :     const GDALDataType eDataType =
    3079         163 :         GetDataTypeFromFormat(keywords.GetKeyword("FORMAT", ""));
    3080         163 :     if (eDataType == GDT_Unknown)
    3081           0 :         return false;
    3082         163 :     const uint64_t nItemSize = GDALGetDataTypeSizeBytes(eDataType);
    3083         163 :     const char *value = keywords.GetKeyword("ORG", "BSQ");
    3084             :     // number of bytes of binary prefix before each record
    3085         163 :     nNBB = atoi(keywords.GetKeyword("NBB", ""));
    3086         163 :     const uint64_t nCols64 = atoi(keywords.GetKeyword("NS", ""));
    3087         163 :     const uint64_t nRows64 = atoi(keywords.GetKeyword("NL", ""));
    3088         163 :     const uint64_t nBands64 = atoi(keywords.GetKeyword("NB", ""));
    3089             :     try
    3090             :     {
    3091         163 :         if (EQUAL(value, "BIP"))
    3092             :         {
    3093           6 :             nPixelOffset = (CPLSM(nItemSize) * CPLSM(nBands64)).v();
    3094           6 :             nBandOffset = nItemSize;
    3095           6 :             nLineOffset =
    3096           6 :                 (CPLSM(nNBB) + CPLSM(nPixelOffset) * CPLSM(nCols64)).v();
    3097           6 :             nImageSize = (CPLSM(nLineOffset) * CPLSM(nRows64)).v();
    3098             :         }
    3099         157 :         else if (EQUAL(value, "BIL"))
    3100             :         {
    3101           6 :             nPixelOffset = nItemSize;
    3102           6 :             nBandOffset = (CPLSM(nItemSize) * CPLSM(nCols64)).v();
    3103           6 :             nLineOffset =
    3104           6 :                 (CPLSM(nNBB) + CPLSM(nBandOffset) * CPLSM(nBands64)).v();
    3105           6 :             nImageSize = (CPLSM(nLineOffset) * CPLSM(nRows64)).v();
    3106             :         }
    3107         151 :         else if (EQUAL(value, "BSQ"))
    3108             :         {
    3109         151 :             nPixelOffset = nItemSize;
    3110         151 :             nLineOffset =
    3111         151 :                 (CPLSM(nNBB) + CPLSM(nPixelOffset) * CPLSM(nCols64)).v();
    3112         151 :             nBandOffset = (CPLSM(nLineOffset) * CPLSM(nRows64)).v();
    3113         151 :             nImageSize = (CPLSM(nBandOffset) * CPLSM(nBands64)).v();
    3114             :         }
    3115             :         else
    3116             :         {
    3117           0 :             CPLError(CE_Failure, CPLE_NotSupported,
    3118             :                      "ORG=%s layout not supported.", value);
    3119           0 :             return false;
    3120             :         }
    3121             :     }
    3122           0 :     catch (const CPLSafeIntOverflow &)
    3123             :     {
    3124           0 :         return false;
    3125             :     }
    3126             : 
    3127         163 :     const uint64_t nLabelSize = atoi(keywords.GetKeyword("LBLSIZE", ""));
    3128         163 :     const uint64_t nRecordSize = atoi(keywords.GetKeyword("RECSIZE", ""));
    3129         163 :     const uint64_t nNLB = atoi(keywords.GetKeyword("NLB", ""));
    3130             :     try
    3131             :     {
    3132         163 :         nImageOffsetWithoutNBB =
    3133         163 :             (CPLSM(nLabelSize) + CPLSM(nRecordSize) * CPLSM(nNLB) + CPLSM(nNBB))
    3134         163 :                 .v();
    3135         163 :         nImageOffsetWithoutNBB -= nNBB;
    3136             :     }
    3137           0 :     catch (const CPLSafeIntOverflow &)
    3138             :     {
    3139           0 :         return false;
    3140             :     }
    3141         163 :     return true;
    3142             : }
    3143             : 
    3144             : /************************************************************************/
    3145             : /*                           Create()                                   */
    3146             : /************************************************************************/
    3147             : 
    3148          62 : GDALDataset *VICARDataset::Create(const char *pszFilename, int nXSize,
    3149             :                                   int nYSize, int nBandsIn, GDALDataType eType,
    3150             :                                   char **papszOptions)
    3151             : {
    3152          62 :     return CreateInternal(pszFilename, nXSize, nYSize, nBandsIn, eType,
    3153          62 :                           papszOptions);
    3154             : }
    3155             : 
    3156         102 : VICARDataset *VICARDataset::CreateInternal(const char *pszFilename, int nXSize,
    3157             :                                            int nYSize, int nBandsIn,
    3158             :                                            GDALDataType eType,
    3159             :                                            char **papszOptions)
    3160             : {
    3161         102 :     if (eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Int32 &&
    3162          46 :         eType != GDT_Float32 && eType != GDT_Float64 && eType != GDT_CFloat32)
    3163             :     {
    3164          38 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type");
    3165          38 :         return nullptr;
    3166             :     }
    3167             : 
    3168          64 :     const int nPixelOffset = GDALGetDataTypeSizeBytes(eType);
    3169          64 :     if (nXSize == 0 || nYSize == 0 || nPixelOffset > INT_MAX / nXSize)
    3170             :     {
    3171           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    3172             :                  "Unsupported raster dimensions");
    3173           0 :         return nullptr;
    3174             :     }
    3175          64 :     const int nLineOffset = nXSize * nPixelOffset;
    3176             : 
    3177          64 :     if (nBandsIn == 0 || nBandsIn > 32767)
    3178             :     {
    3179           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
    3180           1 :         return nullptr;
    3181             :     }
    3182             : 
    3183             :     const char *pszCompress =
    3184          63 :         CSLFetchNameValueDef(papszOptions, "COMPRESS", "NONE");
    3185          63 :     CompressMethod eCompress = COMPRESS_NONE;
    3186          63 :     if (EQUAL(pszCompress, "NONE"))
    3187             :     {
    3188          53 :         eCompress = COMPRESS_NONE;
    3189             :     }
    3190          10 :     else if (EQUAL(pszCompress, "BASIC"))
    3191             :     {
    3192           6 :         eCompress = COMPRESS_BASIC;
    3193             :     }
    3194           4 :     else if (EQUAL(pszCompress, "BASIC2"))
    3195             :     {
    3196           3 :         eCompress = COMPRESS_BASIC2;
    3197             :     }
    3198             :     else
    3199             :     {
    3200           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported COMPRESS value");
    3201           1 :         return nullptr;
    3202             :     }
    3203          71 :     if (eCompress != COMPRESS_NONE &&
    3204           9 :         (!GDALDataTypeIsInteger(eType) || nBandsIn != 1))
    3205             :     {
    3206           2 :         CPLError(
    3207             :             CE_Failure, CPLE_NotSupported,
    3208             :             "BASIC/BASIC2 compression only supports one-band integer datasets");
    3209           2 :         return nullptr;
    3210             :     }
    3211             : 
    3212         120 :     std::vector<vsi_l_offset> anRecordOffsets;
    3213          60 :     if (eCompress != COMPRESS_NONE)
    3214             :     {
    3215           7 :         const GUInt64 nMaxEncodedSize =
    3216           7 :             static_cast<GUInt64>(nXSize) * nPixelOffset +
    3217           7 :             static_cast<GUInt64>(nXSize) * nPixelOffset / 2 + 11;
    3218             :         // To avoid potential later int overflows
    3219           7 :         if (nMaxEncodedSize > static_cast<GUInt64>(INT_MAX))
    3220             :         {
    3221           1 :             CPLError(CE_Failure, CPLE_NotSupported, "Too large scanline");
    3222           1 :             return nullptr;
    3223             :         }
    3224           6 :         if (nYSize > 100 * 1000 * 1000)
    3225             :         {
    3226           1 :             CPLError(CE_Failure, CPLE_NotSupported,
    3227             :                      "Too many records for compressed dataset");
    3228           1 :             return nullptr;
    3229             :         }
    3230             :         try
    3231             :         {
    3232             :             // + 1 to store implicitly the size of the last record
    3233           5 :             anRecordOffsets.resize(nYSize + 1);
    3234             :         }
    3235           0 :         catch (const std::exception &e)
    3236             :         {
    3237           0 :             CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    3238           0 :             return nullptr;
    3239             :         }
    3240             :     }
    3241             : 
    3242         116 :     CPLJSONObject oSrcJSonLabel;
    3243          58 :     oSrcJSonLabel.Deinit();
    3244             : 
    3245          58 :     const char *pszLabel = CSLFetchNameValue(papszOptions, "LABEL");
    3246          58 :     if (pszLabel)
    3247             :     {
    3248           4 :         CPLJSONDocument oJSONDocument;
    3249           4 :         if (pszLabel[0] == '{')
    3250             :         {
    3251           2 :             const GByte *pabyData = reinterpret_cast<const GByte *>(pszLabel);
    3252           2 :             if (!oJSONDocument.LoadMemory(pabyData))
    3253             :             {
    3254           1 :                 return nullptr;
    3255             :             }
    3256             :         }
    3257             :         else
    3258             :         {
    3259           2 :             if (!oJSONDocument.Load(pszLabel))
    3260             :             {
    3261           1 :                 return nullptr;
    3262             :             }
    3263             :         }
    3264             : 
    3265           2 :         oSrcJSonLabel = oJSONDocument.GetRoot();
    3266           2 :         if (!oSrcJSonLabel.IsValid())
    3267             :         {
    3268           0 :             return nullptr;
    3269             :         }
    3270             :     }
    3271             : 
    3272          56 :     VSILFILE *fp = VSIFOpenExL(pszFilename, "wb+", true);
    3273          56 :     if (fp == nullptr)
    3274             :     {
    3275           3 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s", pszFilename,
    3276             :                  VSIGetLastErrorMsg());
    3277           3 :         return nullptr;
    3278             :     }
    3279             : 
    3280          53 :     VICARDataset *poDS = new VICARDataset();
    3281          53 :     poDS->fpImage = fp;
    3282          53 :     poDS->nRasterXSize = nXSize;
    3283          53 :     poDS->nRasterYSize = nYSize;
    3284          53 :     poDS->m_nRecordSize = nLineOffset;
    3285          53 :     poDS->m_bIsLabelWritten = false;
    3286          53 :     poDS->m_bGeoRefFormatIsMIPL = EQUAL(
    3287             :         CSLFetchNameValueDef(papszOptions, "GEOREF_FORMAT", "MIPL"), "MIPL");
    3288          53 :     poDS->m_bUseSrcLabel = CPLFetchBool(papszOptions, "USE_SRC_LABEL", true);
    3289          53 :     poDS->m_bUseSrcMap = CPLFetchBool(papszOptions, "USE_SRC_MAP", false);
    3290             :     poDS->m_osLatitudeType =
    3291          53 :         CSLFetchNameValueDef(papszOptions, "COORDINATE_SYSTEM_NAME", "");
    3292             :     poDS->m_osLongitudeDirection =
    3293          53 :         CSLFetchNameValueDef(papszOptions, "POSITIVE_LONGITUDE_DIRECTION", "");
    3294             :     poDS->m_osTargetName =
    3295          53 :         CSLFetchNameValueDef(papszOptions, "TARGET_NAME", "");
    3296          53 :     poDS->m_bInitToNodata = true;
    3297          53 :     poDS->m_oSrcJSonLabel = std::move(oSrcJSonLabel);
    3298          53 :     poDS->m_eCompress = eCompress;
    3299          53 :     poDS->m_anRecordOffsets = std::move(anRecordOffsets);
    3300          53 :     poDS->eAccess = GA_Update;
    3301             : 
    3302             :     /* -------------------------------------------------------------------- */
    3303             :     /*      Create band information objects.                                */
    3304             :     /* -------------------------------------------------------------------- */
    3305          53 :     const vsi_l_offset nBandOffset =
    3306          53 :         static_cast<vsi_l_offset>(nLineOffset) * nYSize;
    3307         140 :     for (int i = 0; i < nBandsIn; i++)
    3308             :     {
    3309             :         GDALRasterBand *poBand;
    3310          87 :         if (eCompress != COMPRESS_NONE)
    3311             :         {
    3312           5 :             poBand = new VICARBASICRasterBand(poDS, i + 1, eType);
    3313             :         }
    3314             :         else
    3315             :         {
    3316          82 :             poBand = new VICARRawRasterBand(
    3317             :                 poDS, i + 1, poDS->fpImage,
    3318          82 :                 i * nBandOffset,  // will be set later to final value since we
    3319             :                                   // need to include the label size
    3320             :                 nPixelOffset, nLineOffset, eType,
    3321          82 :                 RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN);
    3322             :         }
    3323          87 :         poDS->SetBand(i + 1, poBand);
    3324             :     }
    3325             : 
    3326          53 :     return poDS;
    3327             : }
    3328             : 
    3329             : /************************************************************************/
    3330             : /*                            CreateCopy()                              */
    3331             : /************************************************************************/
    3332             : 
    3333          41 : GDALDataset *VICARDataset::CreateCopy(const char *pszFilename,
    3334             :                                       GDALDataset *poSrcDS, int /*bStrict*/,
    3335             :                                       char **papszOptions,
    3336             :                                       GDALProgressFunc pfnProgress,
    3337             :                                       void *pProgressData)
    3338             : {
    3339          41 :     if (poSrcDS->GetRasterCount() == 0)
    3340             :     {
    3341           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
    3342           1 :         return nullptr;
    3343             :     }
    3344             : 
    3345          40 :     const int nXSize = poSrcDS->GetRasterXSize();
    3346          40 :     const int nYSize = poSrcDS->GetRasterYSize();
    3347          40 :     const int nBands = poSrcDS->GetRasterCount();
    3348          40 :     GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    3349             :     auto poDS = std::unique_ptr<VICARDataset>(CreateInternal(
    3350          80 :         pszFilename, nXSize, nYSize, nBands, eType, papszOptions));
    3351          40 :     if (poDS == nullptr)
    3352           8 :         return nullptr;
    3353             : 
    3354          32 :     GDALGeoTransform gt;
    3355          32 :     if (poSrcDS->GetGeoTransform(gt) == CE_None && gt != GDALGeoTransform())
    3356             :     {
    3357          26 :         poDS->SetGeoTransform(gt);
    3358             :     }
    3359             : 
    3360          32 :     auto poSrcSRS = poSrcDS->GetSpatialRef();
    3361          32 :     if (poSrcSRS)
    3362             :     {
    3363          22 :         poDS->SetSpatialRef(poSrcSRS);
    3364             :     }
    3365             : 
    3366          32 :     if (poDS->m_bUseSrcLabel && !poDS->m_oSrcJSonLabel.IsValid())
    3367             :     {
    3368          32 :         char **papszMD_VICAR = poSrcDS->GetMetadata("json:VICAR");
    3369          32 :         if (papszMD_VICAR != nullptr)
    3370             :         {
    3371          12 :             poDS->SetMetadata(papszMD_VICAR, "json:VICAR");
    3372             :         }
    3373             :     }
    3374             : 
    3375          32 :     poDS->m_bInitToNodata = false;
    3376          32 :     CPLErr eErr = GDALDatasetCopyWholeRaster(poSrcDS, poDS.get(), nullptr,
    3377             :                                              pfnProgress, pProgressData);
    3378          32 :     poDS->FlushCache(false);
    3379          32 :     if (eErr != CE_None)
    3380             :     {
    3381          10 :         return nullptr;
    3382             :     }
    3383             : 
    3384          22 :     return poDS.release();
    3385             : }
    3386             : 
    3387             : /************************************************************************/
    3388             : /*                         GDALRegister_VICAR()                         */
    3389             : /************************************************************************/
    3390             : 
    3391        2024 : void GDALRegister_VICAR()
    3392             : 
    3393             : {
    3394        2024 :     if (GDALGetDriverByName(VICAR_DRIVER_NAME) != nullptr)
    3395         283 :         return;
    3396             : 
    3397        1741 :     GDALDriver *poDriver = new GDALDriver();
    3398        1741 :     VICARDriverSetCommonMetadata(poDriver);
    3399             : 
    3400        1741 :     poDriver->pfnOpen = VICARDataset::Open;
    3401        1741 :     poDriver->pfnCreate = VICARDataset::Create;
    3402        1741 :     poDriver->pfnCreateCopy = VICARDataset::CreateCopy;
    3403             : 
    3404        1741 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3405             : }

Generated by: LCOV version 1.14