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

Generated by: LCOV version 1.14