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

Generated by: LCOV version 1.14