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

Generated by: LCOV version 1.14