LCOV - code coverage report
Current view: top level - frmts/pds - isis3dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1704 1876 90.8 %
Date: 2026-08-22 15:37:05 Functions: 82 83 98.8 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ISIS Version 3 Driver
       4             :  * Purpose:  Implementation of ISIS3Dataset
       5             :  * Author:   Trent Hare (thare@usgs.gov)
       6             :  *           Frank Warmerdam (warmerdam@pobox.com)
       7             :  *           Even Rouault (even.rouault at spatialys.com)
       8             :  *
       9             :  * NOTE: Original code authored by Trent and placed in the public domain as
      10             :  * per US government policy.  I have (within my rights) appropriated it and
      11             :  * placed it under the following license.  This is not intended to diminish
      12             :  * Trents contribution.
      13             :  ******************************************************************************
      14             :  * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
      15             :  * Copyright (c) 2009-2010, Even Rouault <even.rouault at spatialys.com>
      16             :  * Copyright (c) 2017 Hobu Inc
      17             :  * Copyright (c) 2017, Dmitry Baryshnikov <polimax@mail.ru>
      18             :  * Copyright (c) 2017, NextGIS <info@nextgis.com>
      19             :  *
      20             :  * SPDX-License-Identifier: MIT
      21             :  ****************************************************************************/
      22             : 
      23             : #include "cpl_json.h"
      24             : #include "cpl_string.h"
      25             : #include "cpl_time.h"
      26             : #include "cpl_vsi_error.h"
      27             : #include "cpl_vsi_virtual.h"
      28             : #include "gdal_frmts.h"
      29             : #include "gdal_proxy.h"
      30             : #include "nasakeywordhandler.h"
      31             : #include "ogrgeojsonreader.h"
      32             : #include "ogr_spatialref.h"
      33             : #include "rawdataset.h"
      34             : #include "vrtdataset.h"
      35             : #include "cpl_safemaths.hpp"
      36             : #include "pdsdrivercore.h"
      37             : #include "json_utils.h"
      38             : 
      39             : // For gethostname()
      40             : #ifdef _WIN32
      41             : #include <winsock2.h>
      42             : #else
      43             : #include <unistd.h>
      44             : #endif
      45             : 
      46             : #include <algorithm>
      47             : #include <cinttypes>
      48             : #include <map>
      49             : #include <utility>  // pair
      50             : #include <vector>
      51             : #include <iostream>
      52             : 
      53             : // Constants coming from ISIS3 source code
      54             : // in isis/src/base/objs/SpecialPixel/SpecialPixel.h
      55             : 
      56             : // There are several types of special pixels
      57             : //    *   Isis::Null Pixel has no data available
      58             : //    *   Isis::Lis Pixel was saturated on the instrument
      59             : //    *   Isis::His Pixel was saturated on the instrument
      60             : //    *   Isis::Lrs Pixel was saturated during a computation
      61             : //    *   Isis::Hrs Pixel was saturated during a computation
      62             : 
      63             : // 1-byte special pixel values
      64             : const unsigned char ISIS3_NULL1 = 0;
      65             : const unsigned char LOW_REPR_SAT1 = 0;
      66             : const unsigned char LOW_INSTR_SAT1 = 0;
      67             : const unsigned char HIGH_INSTR_SAT1 = 255;
      68             : const unsigned char HIGH_REPR_SAT1 = 255;
      69             : 
      70             : // 2-byte unsigned special pixel values
      71             : const unsigned short ISIS3_NULLU2 = 0;
      72             : const unsigned short LOW_REPR_SATU2 = 1;
      73             : const unsigned short LOW_INSTR_SATU2 = 2;
      74             : const unsigned short HIGH_INSTR_SATU2 = 65534;
      75             : const unsigned short HIGH_REPR_SATU2 = 65535;
      76             : 
      77             : // 2-byte signed special pixel values
      78             : const short ISIS3_NULL2 = -32768;
      79             : const short LOW_REPR_SAT2 = -32767;
      80             : const short LOW_INSTR_SAT2 = -32766;
      81             : const short HIGH_INSTR_SAT2 = -32765;
      82             : const short HIGH_REPR_SAT2 = -32764;
      83             : 
      84             : // Define 4-byte special pixel values for IEEE floating point
      85             : const float ISIS3_NULL4 = -3.4028226550889045e+38f;      // 0xFF7FFFFB;
      86             : const float LOW_REPR_SAT4 = -3.4028228579130005e+38f;    // 0xFF7FFFFC;
      87             : const float LOW_INSTR_SAT4 = -3.4028230607370965e+38f;   // 0xFF7FFFFD;
      88             : const float HIGH_INSTR_SAT4 = -3.4028232635611926e+38f;  // 0xFF7FFFFE;
      89             : const float HIGH_REPR_SAT4 = -3.4028234663852886e+38f;   // 0xFF7FFFFF;
      90             : 
      91             : const double ISIS3_NULL8 = -1.797693134862315e+308;       // 0xFFEFFFFFFFFFFFFB
      92             : const double LOW_REPR_SAT8 = -1.7976931348623151e+308;    // 0xFFEFFFFFFFFFFFFC
      93             : const double LOW_INSTR_SAT8 = -1.7976931348623153e+308;   // 0xFFEFFFFFFFFFFFFD
      94             : const double HIGH_INSTR_SAT8 = -1.7976931348623155e+308;  // 0xFFEFFFFFFFFFFFFE
      95             : const double HIGH_REPR_SAT8 = -1.7976931348623157e+308;   // 0xFFEFFFFFFFFFFFFF
      96             : 
      97             : // Must be large enough to hold an integer
      98             : static const char *const pszSTARTBYTE_PLACEHOLDER = "!*^STARTBYTE^*!";
      99             : // Must be large enough to hold an integer
     100             : static const char *const pszLABEL_BYTES_PLACEHOLDER = "!*^LABEL_BYTES^*!";
     101             : // Must be large enough to hold an integer
     102             : static const char *const pszHISTORY_STARTBYTE_PLACEHOLDER =
     103             :     "!*^HISTORY_STARTBYTE^*!";
     104             : 
     105             : /************************************************************************/
     106             : /* ==================================================================== */
     107             : /*                             ISISDataset                              */
     108             : /* ==================================================================== */
     109             : /************************************************************************/
     110             : 
     111             : class ISIS3Dataset final : public RawDataset
     112             : {
     113             :     friend class ISIS3RawRasterBand;
     114             :     friend class ISISTiledBand;
     115             :     friend class ISIS3WrapperRasterBand;
     116             : 
     117             :     class NonPixelSection
     118             :     {
     119             :       public:
     120             :         CPLString osSrcFilename{};
     121             :         CPLString osDstFilename{};  // empty for same file
     122             :         vsi_l_offset nSrcOffset{};
     123             :         vsi_l_offset nSize{};
     124             :         CPLString osPlaceHolder{};  // empty if not same file
     125             :     };
     126             : 
     127             :     VSILFILE *m_fpLabel{};               // label file (only used for writing)
     128             :     VSILFILE *m_fpImage{};               // image data file. May be == fpLabel
     129             :     GDALDataset *m_poExternalDS{};       // external dataset (GeoTIFF)
     130             :     bool m_bGeoTIFFAsRegularExternal{};  // creation only
     131             :     bool m_bGeoTIFFInitDone{true};       // creation only
     132             : 
     133             :     CPLString m_osExternalFilename{};
     134             :     bool m_bIsLabelWritten{true};  // creation only
     135             : 
     136             :     bool m_bIsTiled{};
     137             :     bool m_bInitToNodata{};  // creation only
     138             : 
     139             :     NASAKeywordHandler m_oKeywords{};
     140             : 
     141             :     bool m_bGotTransform{};
     142             :     GDALGeoTransform m_gt{};
     143             : 
     144             :     bool m_bHasSrcNoData{};  // creation only
     145             :     double m_dfSrcNoData{};  // creation only
     146             : 
     147             :     OGRSpatialReference m_oSRS{};
     148             : 
     149             :     // creation only variables
     150             :     CPLString m_osComment{};
     151             :     CPLString m_osLatitudeType{};
     152             :     CPLString m_osLongitudeDirection{};
     153             :     CPLString m_osTargetName{};
     154             :     bool m_bForce360{};
     155             :     bool m_bWriteBoundingDegrees{true};
     156             :     CPLString m_osBoundingDegrees{};
     157             : 
     158             :     CPLJSONObject m_oJSonLabel{};
     159             :     CPLString m_osHistory{};                              // creation only
     160             :     bool m_bUseSrcLabel{true};                            // creation only
     161             :     bool m_bUseSrcMapping{};                              // creation only
     162             :     bool m_bUseSrcHistory{true};                          // creation only
     163             :     bool m_bAddGDALHistory{true};                         // creation only
     164             :     CPLString m_osGDALHistory{};                          // creation only
     165             :     std::vector<NonPixelSection> m_aoNonPixelSections{};  // creation only
     166             :     CPLJSONObject m_oSrcJSonLabel{};                      // creation only
     167             :     CPLStringList m_aosISIS3MD{};
     168             :     CPLStringList m_aosAdditionalFiles{};
     169             :     CPLString m_osFromFilename{};  // creation only
     170             : 
     171             :     RawBinaryLayout m_sLayout{};
     172             : 
     173             :     bool m_bResolveOfflineContent = true;
     174             :     int m_nMaxOfflineContentSize = 100000000;
     175             :     bool m_bHasResolvedOfflineContent = false;
     176             : 
     177             :     const char *GetKeyword(const char *pszPath, const char *pszDefault = "");
     178             : 
     179             :     double FixLong(double dfLong);
     180             :     void BuildLabel();
     181             :     void BuildHistory();
     182             :     void WriteLabel();
     183             :     void InvalidateLabel();
     184             :     void ResolveOfflineContentOfLabel();
     185             : 
     186             :     static CPLString SerializeAsPDL(const CPLJSONObject &oObj);
     187             :     static void SerializeAsPDL(VSILFILE *fp, const CPLJSONObject &oObj,
     188             :                                int nDepth = 0);
     189             : 
     190             :     CPL_DISALLOW_COPY_ASSIGN(ISIS3Dataset)
     191             : 
     192             :   protected:
     193             :     CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override;
     194             : 
     195             :   public:
     196             :     ISIS3Dataset();
     197             :     ~ISIS3Dataset() override;
     198             : 
     199             :     int CloseDependentDatasets() override;
     200             : 
     201             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
     202             :     CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
     203             : 
     204             :     const OGRSpatialReference *GetSpatialRef() const override;
     205             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
     206             : 
     207             :     char **GetFileList() override;
     208             : 
     209             :     char **GetMetadataDomainList() override;
     210             :     CSLConstList GetMetadata(const char *pszDomain = "") override;
     211             :     CPLErr SetMetadata(CSLConstList papszMD,
     212             :                        const char *pszDomain = "") override;
     213             : 
     214             :     bool GetRawBinaryLayout(GDALDataset::RawBinaryLayout &) override;
     215             : 
     216             :     static GDALDataset *Open(GDALOpenInfo *);
     217             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     218             :                                int nBandsIn, GDALDataType eType,
     219             :                                CSLConstList papszOptions);
     220             :     static GDALDataset *CreateCopy(const char *pszFilename,
     221             :                                    GDALDataset *poSrcDS, int bStrict,
     222             :                                    CSLConstList papszOptions,
     223             :                                    GDALProgressFunc pfnProgress,
     224             :                                    void *pProgressData);
     225             : };
     226             : 
     227             : /************************************************************************/
     228             : /* ==================================================================== */
     229             : /*                             ISISTiledBand                            */
     230             : /* ==================================================================== */
     231             : /************************************************************************/
     232             : 
     233             : class ISISTiledBand final : public GDALPamRasterBand
     234             : {
     235             :     friend class ISIS3Dataset;
     236             : 
     237             :     VSILFILE *const m_fpVSIL{};
     238             :     GIntBig m_nFirstTileOffset{};
     239             :     GIntBig m_nXTileOffset{};
     240             :     GIntBig m_nYTileOffset{};
     241             :     const bool m_bNativeOrder{};
     242             :     bool m_bHasOffset{};
     243             :     bool m_bHasScale{};
     244             :     double m_dfOffset{};
     245             :     double m_dfScale{1.0};
     246             :     double m_dfNoData{};
     247             :     bool m_bValid = false;
     248             : 
     249             :     CPL_DISALLOW_COPY_ASSIGN(ISISTiledBand)
     250             : 
     251             :   public:
     252             :     ISISTiledBand(GDALDataset *poDS, VSILFILE *fpVSIL, int nBand,
     253             :                   GDALDataType eDT, int nTileXSize, int nTileYSize,
     254             :                   GIntBig nFirstTileOffset, GIntBig nXTileOffset,
     255             :                   GIntBig nYTileOffset, int bNativeOrder);
     256             : 
     257          41 :     bool IsValid() const
     258             :     {
     259          41 :         return m_bValid;
     260             :     }
     261             : 
     262             :     CPLErr IReadBlock(int, int, void *) override;
     263             :     CPLErr IWriteBlock(int, int, void *) override;
     264             : 
     265             :     double GetOffset(int *pbSuccess = nullptr) override;
     266             :     double GetScale(int *pbSuccess = nullptr) override;
     267             :     CPLErr SetOffset(double dfNewOffset) override;
     268             :     CPLErr SetScale(double dfNewScale) override;
     269             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
     270             :     CPLErr SetNoDataValue(double dfNewNoData) override;
     271             : 
     272             :     void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
     273             : };
     274             : 
     275             : /************************************************************************/
     276             : /* ==================================================================== */
     277             : /*                        ISIS3RawRasterBand                            */
     278             : /* ==================================================================== */
     279             : /************************************************************************/
     280             : 
     281             : class ISIS3RawRasterBand final : public RawRasterBand
     282             : {
     283             :     friend class ISIS3Dataset;
     284             : 
     285             :     bool m_bHasOffset;
     286             :     bool m_bHasScale;
     287             :     double m_dfOffset;
     288             :     double m_dfScale{1.0};
     289             :     double m_dfNoData;
     290             : 
     291             :   public:
     292             :     ISIS3RawRasterBand(GDALDataset *l_poDS, int l_nBand, VSILFILE *l_fpRaw,
     293             :                        vsi_l_offset l_nImgOffset, int l_nPixelOffset,
     294             :                        int l_nLineOffset, GDALDataType l_eDataType,
     295             :                        int l_bNativeOrder);
     296             : 
     297             :     CPLErr IReadBlock(int, int, void *) override;
     298             :     CPLErr IWriteBlock(int, int, void *) override;
     299             : 
     300             :     CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
     301             :                      GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace,
     302             :                      GDALRasterIOExtraArg *psExtraArg) override;
     303             : 
     304             :     double GetOffset(int *pbSuccess = nullptr) override;
     305             :     double GetScale(int *pbSuccess = nullptr) override;
     306             :     CPLErr SetOffset(double dfNewOffset) override;
     307             :     CPLErr SetScale(double dfNewScale) override;
     308             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
     309             :     CPLErr SetNoDataValue(double dfNewNoData) override;
     310             : 
     311             :     void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
     312             : };
     313             : 
     314             : /************************************************************************/
     315             : /* ==================================================================== */
     316             : /*                         ISIS3WrapperRasterBand                       */
     317             : /*                                                                      */
     318             : /*      proxy for bands stored in other formats.                        */
     319             : /* ==================================================================== */
     320             : /************************************************************************/
     321             : class ISIS3WrapperRasterBand final : public GDALProxyRasterBand
     322             : {
     323             :     friend class ISIS3Dataset;
     324             : 
     325             :     GDALRasterBand *m_poBaseBand{};
     326             :     bool m_bHasOffset{};
     327             :     bool m_bHasScale{};
     328             :     double m_dfOffset{};
     329             :     double m_dfScale{1.0};
     330             :     double m_dfNoData{};
     331             : 
     332             :     CPL_DISALLOW_COPY_ASSIGN(ISIS3WrapperRasterBand)
     333             : 
     334             :   protected:
     335             :     virtual GDALRasterBand *
     336         127 :     RefUnderlyingRasterBand(bool /* bForceOpen */) const override
     337             :     {
     338         127 :         return m_poBaseBand;
     339             :     }
     340             : 
     341             :   public:
     342             :     explicit ISIS3WrapperRasterBand(GDALRasterBand *poBaseBandIn);
     343             : 
     344             :     void InitFile();
     345             : 
     346             :     virtual CPLErr Fill(double dfRealValue,
     347             :                         double dfImaginaryValue = 0) override;
     348             :     CPLErr IWriteBlock(int, int, void *) override;
     349             : 
     350             :     CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
     351             :                      GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace,
     352             :                      GDALRasterIOExtraArg *psExtraArg) override;
     353             : 
     354             :     double GetOffset(int *pbSuccess = nullptr) override;
     355             :     double GetScale(int *pbSuccess = nullptr) override;
     356             :     CPLErr SetOffset(double dfNewOffset) override;
     357             :     CPLErr SetScale(double dfNewScale) override;
     358             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
     359             :     CPLErr SetNoDataValue(double dfNewNoData) override;
     360             : 
     361           4 :     int GetMaskFlags() override
     362             :     {
     363           4 :         return nMaskFlags;
     364             :     }
     365             : 
     366           4 :     GDALRasterBand *GetMaskBand() override
     367             :     {
     368           4 :         return poMask;
     369             :     }
     370             : 
     371             :     void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
     372             : };
     373             : 
     374             : /************************************************************************/
     375             : /* ==================================================================== */
     376             : /*                             ISISMaskBand                             */
     377             : /* ==================================================================== */
     378             : 
     379             : class ISISMaskBand final : public GDALRasterBand
     380             : {
     381             :     GDALRasterBand *m_poBaseBand{};
     382             :     void *m_pBuffer{};
     383             : 
     384             :     CPL_DISALLOW_COPY_ASSIGN(ISISMaskBand)
     385             : 
     386             :   public:
     387             :     explicit ISISMaskBand(GDALRasterBand *poBaseBand);
     388             :     ~ISISMaskBand() override;
     389             : 
     390             :     CPLErr IReadBlock(int, int, void *) override;
     391             : };
     392             : 
     393             : /************************************************************************/
     394             : /*                           ISISTiledBand()                            */
     395             : /************************************************************************/
     396             : 
     397          51 : ISISTiledBand::ISISTiledBand(GDALDataset *poDSIn, VSILFILE *fpVSILIn,
     398             :                              int nBandIn, GDALDataType eDT, int nTileXSize,
     399             :                              int nTileYSize, GIntBig nFirstTileOffsetIn,
     400             :                              GIntBig nXTileOffsetIn, GIntBig nYTileOffsetIn,
     401          51 :                              int bNativeOrderIn)
     402             :     : m_fpVSIL(fpVSILIn), m_nXTileOffset(nXTileOffsetIn),
     403             :       m_nYTileOffset(nYTileOffsetIn),
     404          51 :       m_bNativeOrder(CPL_TO_BOOL(bNativeOrderIn))
     405             : {
     406          51 :     poDS = poDSIn;
     407          51 :     nBand = nBandIn;
     408          51 :     eDataType = eDT;
     409          51 :     nBlockXSize = nTileXSize;
     410          51 :     nBlockYSize = nTileYSize;
     411          51 :     nRasterXSize = poDSIn->GetRasterXSize();
     412          51 :     nRasterYSize = poDSIn->GetRasterYSize();
     413             : 
     414          51 :     const int l_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
     415          51 :     const int l_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
     416             : 
     417          51 :     if (m_nXTileOffset == 0 && m_nYTileOffset == 0)
     418             :     {
     419          51 :         m_nXTileOffset =
     420          51 :             static_cast<GIntBig>(GDALGetDataTypeSizeBytes(eDT)) * nTileXSize;
     421          51 :         if (m_nXTileOffset > GINTBIG_MAX / nTileYSize)
     422             :         {
     423           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
     424           0 :             return;
     425             :         }
     426          51 :         m_nXTileOffset *= nTileYSize;
     427             : 
     428          51 :         if (m_nXTileOffset > GINTBIG_MAX / l_nBlocksPerRow)
     429             :         {
     430           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
     431           0 :             return;
     432             :         }
     433          51 :         m_nYTileOffset = m_nXTileOffset * l_nBlocksPerRow;
     434             :     }
     435             : 
     436          51 :     m_nFirstTileOffset = nFirstTileOffsetIn;
     437          51 :     if (nBand > 1)
     438             :     {
     439          19 :         if (m_nYTileOffset > GINTBIG_MAX / (nBand - 1) ||
     440          19 :             (nBand - 1) * m_nYTileOffset > GINTBIG_MAX / l_nBlocksPerColumn ||
     441          19 :             m_nFirstTileOffset >
     442          19 :                 GINTBIG_MAX - (nBand - 1) * m_nYTileOffset * l_nBlocksPerColumn)
     443             :         {
     444           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
     445           0 :             return;
     446             :         }
     447          19 :         m_nFirstTileOffset += (nBand - 1) * m_nYTileOffset * l_nBlocksPerColumn;
     448             :     }
     449          51 :     m_bValid = true;
     450             : }
     451             : 
     452             : /************************************************************************/
     453             : /*                             IReadBlock()                             */
     454             : /************************************************************************/
     455             : 
     456         476 : CPLErr ISISTiledBand::IReadBlock(int nXBlock, int nYBlock, void *pImage)
     457             : 
     458             : {
     459         476 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
     460         476 :     if (poGDS->m_osExternalFilename.empty())
     461             :     {
     462         232 :         if (!poGDS->m_bIsLabelWritten)
     463           2 :             poGDS->WriteLabel();
     464             :     }
     465             : 
     466         476 :     const vsi_l_offset nOffset = m_nFirstTileOffset + nXBlock * m_nXTileOffset +
     467         476 :                                  nYBlock * m_nYTileOffset;
     468         476 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     469         476 :     const size_t nBlockSize =
     470         476 :         static_cast<size_t>(nDTSize) * nBlockXSize * nBlockYSize;
     471             : 
     472         476 :     if (VSIFSeekL(m_fpVSIL, nOffset, SEEK_SET) != 0)
     473             :     {
     474           0 :         CPLError(CE_Failure, CPLE_FileIO,
     475             :                  "Failed to seek to offset %d to read tile %d,%d.",
     476             :                  static_cast<int>(nOffset), nXBlock, nYBlock);
     477           0 :         return CE_Failure;
     478             :     }
     479             : 
     480         476 :     if (VSIFReadL(pImage, 1, nBlockSize, m_fpVSIL) != nBlockSize)
     481             :     {
     482           0 :         CPLError(CE_Failure, CPLE_FileIO,
     483             :                  "Failed to read %d bytes for tile %d,%d.",
     484             :                  static_cast<int>(nBlockSize), nXBlock, nYBlock);
     485           0 :         return CE_Failure;
     486             :     }
     487             : 
     488         476 :     if (!m_bNativeOrder && eDataType != GDT_UInt8)
     489           0 :         GDALSwapWords(pImage, nDTSize, nBlockXSize * nBlockYSize, nDTSize);
     490             : 
     491         476 :     return CE_None;
     492             : }
     493             : 
     494             : /************************************************************************/
     495             : /*                            RemapNoDataT()                            */
     496             : /************************************************************************/
     497             : 
     498             : template <class T>
     499           7 : static void RemapNoDataT(T *pBuffer, int nItems, T srcNoData, T dstNoData)
     500             : {
     501       67943 :     for (int i = 0; i < nItems; i++)
     502             :     {
     503       67936 :         if (pBuffer[i] == srcNoData)
     504           7 :             pBuffer[i] = dstNoData;
     505             :     }
     506           7 : }
     507             : 
     508             : /************************************************************************/
     509             : /*                            RemapNoData()                             */
     510             : /************************************************************************/
     511             : 
     512           7 : static void RemapNoData(GDALDataType eDataType, void *pBuffer, int nItems,
     513             :                         double dfSrcNoData, double dfDstNoData)
     514             : {
     515           7 :     if (eDataType == GDT_UInt8)
     516             :     {
     517           3 :         RemapNoDataT(reinterpret_cast<GByte *>(pBuffer), nItems,
     518           3 :                      static_cast<GByte>(dfSrcNoData),
     519           3 :                      static_cast<GByte>(dfDstNoData));
     520             :     }
     521           4 :     else if (eDataType == GDT_UInt16)
     522             :     {
     523           1 :         RemapNoDataT(reinterpret_cast<GUInt16 *>(pBuffer), nItems,
     524           1 :                      static_cast<GUInt16>(dfSrcNoData),
     525           1 :                      static_cast<GUInt16>(dfDstNoData));
     526             :     }
     527           3 :     else if (eDataType == GDT_Int16)
     528             :     {
     529           1 :         RemapNoDataT(reinterpret_cast<GInt16 *>(pBuffer), nItems,
     530           1 :                      static_cast<GInt16>(dfSrcNoData),
     531           1 :                      static_cast<GInt16>(dfDstNoData));
     532             :     }
     533           2 :     else if (eDataType == GDT_Float32)
     534             :     {
     535           1 :         CPLAssert(eDataType == GDT_Float32);
     536           1 :         RemapNoDataT(reinterpret_cast<float *>(pBuffer), nItems,
     537             :                      static_cast<float>(dfSrcNoData),
     538             :                      static_cast<float>(dfDstNoData));
     539             :     }
     540             :     else
     541             :     {
     542           1 :         CPLAssert(eDataType == GDT_Float64);
     543           1 :         RemapNoDataT(reinterpret_cast<double *>(pBuffer), nItems,
     544             :                      static_cast<double>(dfSrcNoData),
     545             :                      static_cast<double>(dfDstNoData));
     546             :     }
     547           7 : }
     548             : 
     549             : /************************************************************************/
     550             : /*                             IReadBlock()                             */
     551             : /************************************************************************/
     552             : 
     553         225 : CPLErr ISISTiledBand::IWriteBlock(int nXBlock, int nYBlock, void *pImage)
     554             : 
     555             : {
     556         225 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
     557         225 :     if (poGDS->m_osExternalFilename.empty())
     558             :     {
     559         223 :         if (!poGDS->m_bIsLabelWritten)
     560           2 :             poGDS->WriteLabel();
     561             :     }
     562             : 
     563         225 :     if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData != m_dfNoData)
     564             :     {
     565           1 :         RemapNoData(eDataType, pImage, nBlockXSize * nBlockYSize,
     566             :                     poGDS->m_dfSrcNoData, m_dfNoData);
     567             :     }
     568             : 
     569         225 :     const vsi_l_offset nOffset = m_nFirstTileOffset + nXBlock * m_nXTileOffset +
     570         225 :                                  nYBlock * m_nYTileOffset;
     571         225 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     572         225 :     const size_t nBlockSize =
     573         225 :         static_cast<size_t>(nDTSize) * nBlockXSize * nBlockYSize;
     574             : 
     575         225 :     const int l_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
     576         225 :     const int l_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
     577             : 
     578             :     // Pad partial blocks to nodata value
     579         225 :     if (nXBlock == l_nBlocksPerRow - 1 && (nRasterXSize % nBlockXSize) != 0)
     580             :     {
     581          24 :         GByte *pabyImage = static_cast<GByte *>(pImage);
     582          24 :         int nXStart = nRasterXSize % nBlockXSize;
     583        1688 :         for (int iY = 0; iY < nBlockYSize; iY++)
     584             :         {
     585        1664 :             GDALCopyWords(&m_dfNoData, GDT_Float64, 0,
     586        1664 :                           pabyImage + (iY * nBlockXSize + nXStart) * nDTSize,
     587        1664 :                           eDataType, nDTSize, nBlockXSize - nXStart);
     588             :         }
     589             :     }
     590         225 :     if (nYBlock == l_nBlocksPerColumn - 1 && (nRasterYSize % nBlockYSize) != 0)
     591             :     {
     592          49 :         GByte *pabyImage = static_cast<GByte *>(pImage);
     593        1685 :         for (int iY = nRasterYSize % nBlockYSize; iY < nBlockYSize; iY++)
     594             :         {
     595        1636 :             GDALCopyWords(&m_dfNoData, GDT_Float64, 0,
     596        1636 :                           pabyImage + iY * nBlockXSize * nDTSize, eDataType,
     597             :                           nDTSize, nBlockXSize);
     598             :         }
     599             :     }
     600             : 
     601         225 :     if (VSIFSeekL(m_fpVSIL, nOffset, SEEK_SET) != 0)
     602             :     {
     603           0 :         CPLError(CE_Failure, CPLE_FileIO,
     604             :                  "Failed to seek to offset %d to read tile %d,%d.",
     605             :                  static_cast<int>(nOffset), nXBlock, nYBlock);
     606           0 :         return CE_Failure;
     607             :     }
     608             : 
     609         225 :     if (!m_bNativeOrder && eDataType != GDT_UInt8)
     610           0 :         GDALSwapWords(pImage, nDTSize, nBlockXSize * nBlockYSize, nDTSize);
     611             : 
     612         225 :     if (VSIFWriteL(pImage, 1, nBlockSize, m_fpVSIL) != nBlockSize)
     613             :     {
     614           0 :         CPLError(CE_Failure, CPLE_FileIO,
     615             :                  "Failed to write %d bytes for tile %d,%d.",
     616             :                  static_cast<int>(nBlockSize), nXBlock, nYBlock);
     617           0 :         return CE_Failure;
     618             :     }
     619             : 
     620         225 :     if (!m_bNativeOrder && eDataType != GDT_UInt8)
     621           0 :         GDALSwapWords(pImage, nDTSize, nBlockXSize * nBlockYSize, nDTSize);
     622             : 
     623         225 :     return CE_None;
     624             : }
     625             : 
     626             : /************************************************************************/
     627             : /*                            SetMaskBand()                             */
     628             : /************************************************************************/
     629             : 
     630          41 : void ISISTiledBand::SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand)
     631             : {
     632          41 :     poMask.reset(std::move(poMaskBand));
     633          41 :     nMaskFlags = 0;
     634          41 : }
     635             : 
     636             : /************************************************************************/
     637             : /*                             GetOffset()                              */
     638             : /************************************************************************/
     639             : 
     640          14 : double ISISTiledBand::GetOffset(int *pbSuccess)
     641             : {
     642          14 :     if (pbSuccess)
     643           4 :         *pbSuccess = m_bHasOffset;
     644          14 :     return m_dfOffset;
     645             : }
     646             : 
     647             : /************************************************************************/
     648             : /*                              GetScale()                              */
     649             : /************************************************************************/
     650             : 
     651          14 : double ISISTiledBand::GetScale(int *pbSuccess)
     652             : {
     653          14 :     if (pbSuccess)
     654           4 :         *pbSuccess = m_bHasScale;
     655          14 :     return m_dfScale;
     656             : }
     657             : 
     658             : /************************************************************************/
     659             : /*                             SetOffset()                              */
     660             : /************************************************************************/
     661             : 
     662          14 : CPLErr ISISTiledBand::SetOffset(double dfNewOffset)
     663             : {
     664          14 :     m_dfOffset = dfNewOffset;
     665          14 :     m_bHasOffset = true;
     666          14 :     return CE_None;
     667             : }
     668             : 
     669             : /************************************************************************/
     670             : /*                              SetScale()                              */
     671             : /************************************************************************/
     672             : 
     673          14 : CPLErr ISISTiledBand::SetScale(double dfNewScale)
     674             : {
     675          14 :     m_dfScale = dfNewScale;
     676          14 :     m_bHasScale = true;
     677          14 :     return CE_None;
     678             : }
     679             : 
     680             : /************************************************************************/
     681             : /*                           GetNoDataValue()                           */
     682             : /************************************************************************/
     683             : 
     684          12 : double ISISTiledBand::GetNoDataValue(int *pbSuccess)
     685             : {
     686          12 :     if (pbSuccess)
     687           8 :         *pbSuccess = true;
     688          12 :     return m_dfNoData;
     689             : }
     690             : 
     691             : /************************************************************************/
     692             : /*                           SetNoDataValue()                           */
     693             : /************************************************************************/
     694             : 
     695          51 : CPLErr ISISTiledBand::SetNoDataValue(double dfNewNoData)
     696             : {
     697          51 :     m_dfNoData = dfNewNoData;
     698          51 :     return CE_None;
     699             : }
     700             : 
     701             : /************************************************************************/
     702             : /*                         ISIS3RawRasterBand()                         */
     703             : /************************************************************************/
     704             : 
     705         405 : ISIS3RawRasterBand::ISIS3RawRasterBand(GDALDataset *l_poDS, int l_nBand,
     706             :                                        VSILFILE *l_fpRaw,
     707             :                                        vsi_l_offset l_nImgOffset,
     708             :                                        int l_nPixelOffset, int l_nLineOffset,
     709             :                                        GDALDataType l_eDataType,
     710         405 :                                        int l_bNativeOrder)
     711             :     : RawRasterBand(l_poDS, l_nBand, l_fpRaw, l_nImgOffset, l_nPixelOffset,
     712             :                     l_nLineOffset, l_eDataType, l_bNativeOrder,
     713             :                     RawRasterBand::OwnFP::NO),
     714             :       m_bHasOffset(false), m_bHasScale(false), m_dfOffset(0.0), m_dfScale(1.0),
     715         405 :       m_dfNoData(0.0)
     716             : {
     717         405 : }
     718             : 
     719             : /************************************************************************/
     720             : /*                             IReadBlock()                             */
     721             : /************************************************************************/
     722             : 
     723        2071 : CPLErr ISIS3RawRasterBand::IReadBlock(int nXBlock, int nYBlock, void *pImage)
     724             : 
     725             : {
     726        2071 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
     727        2071 :     if (poGDS->m_osExternalFilename.empty())
     728             :     {
     729         930 :         if (!poGDS->m_bIsLabelWritten)
     730           0 :             poGDS->WriteLabel();
     731             :     }
     732        2071 :     return RawRasterBand::IReadBlock(nXBlock, nYBlock, pImage);
     733             : }
     734             : 
     735             : /************************************************************************/
     736             : /*                            IWriteBlock()                             */
     737             : /************************************************************************/
     738             : 
     739         860 : CPLErr ISIS3RawRasterBand::IWriteBlock(int nXBlock, int nYBlock, void *pImage)
     740             : 
     741             : {
     742         860 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
     743         860 :     if (poGDS->m_osExternalFilename.empty())
     744             :     {
     745         800 :         if (!poGDS->m_bIsLabelWritten)
     746           0 :             poGDS->WriteLabel();
     747             :     }
     748             : 
     749         860 :     if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData != m_dfNoData)
     750             :     {
     751           0 :         RemapNoData(eDataType, pImage, nBlockXSize * nBlockYSize,
     752             :                     poGDS->m_dfSrcNoData, m_dfNoData);
     753             :     }
     754             : 
     755         860 :     return RawRasterBand::IWriteBlock(nXBlock, nYBlock, pImage);
     756             : }
     757             : 
     758             : /************************************************************************/
     759             : /*                             IRasterIO()                              */
     760             : /************************************************************************/
     761             : 
     762        1977 : CPLErr ISIS3RawRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     763             :                                      int nXSize, int nYSize, void *pData,
     764             :                                      int nBufXSize, int nBufYSize,
     765             :                                      GDALDataType eBufType,
     766             :                                      GSpacing nPixelSpace, GSpacing nLineSpace,
     767             :                                      GDALRasterIOExtraArg *psExtraArg)
     768             : 
     769             : {
     770        1977 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
     771        1977 :     if (poGDS->m_osExternalFilename.empty())
     772             :     {
     773        1174 :         if (!poGDS->m_bIsLabelWritten)
     774          59 :             poGDS->WriteLabel();
     775             :     }
     776        1977 :     if (eRWFlag == GF_Write && poGDS->m_bHasSrcNoData &&
     777          31 :         poGDS->m_dfSrcNoData != m_dfNoData)
     778             :     {
     779           5 :         const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     780           5 :         if (eBufType == eDataType && nPixelSpace == nDTSize &&
     781           5 :             nLineSpace == nPixelSpace * nBufXSize)
     782             :         {
     783           5 :             RemapNoData(eDataType, pData, nBufXSize * nBufYSize,
     784             :                         poGDS->m_dfSrcNoData, m_dfNoData);
     785             :         }
     786             :         else
     787             :         {
     788           0 :             const GByte *pabySrc = reinterpret_cast<GByte *>(pData);
     789             :             GByte *pabyTemp = reinterpret_cast<GByte *>(
     790           0 :                 VSI_MALLOC3_VERBOSE(nDTSize, nBufXSize, nBufYSize));
     791           0 :             for (int i = 0; i < nBufYSize; i++)
     792             :             {
     793           0 :                 GDALCopyWords(pabySrc + i * nLineSpace, eBufType,
     794             :                               static_cast<int>(nPixelSpace),
     795           0 :                               pabyTemp + i * nBufXSize * nDTSize, eDataType,
     796             :                               nDTSize, nBufXSize);
     797             :             }
     798           0 :             RemapNoData(eDataType, pabyTemp, nBufXSize * nBufYSize,
     799             :                         poGDS->m_dfSrcNoData, m_dfNoData);
     800           0 :             CPLErr eErr = RawRasterBand::IRasterIO(
     801             :                 eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nBufXSize,
     802             :                 nBufYSize, eDataType, nDTSize,
     803           0 :                 static_cast<GSpacing>(nDTSize) * nBufXSize, psExtraArg);
     804           0 :             VSIFree(pabyTemp);
     805           0 :             return eErr;
     806             :         }
     807             :     }
     808        1977 :     return RawRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     809             :                                     pData, nBufXSize, nBufYSize, eBufType,
     810        1977 :                                     nPixelSpace, nLineSpace, psExtraArg);
     811             : }
     812             : 
     813             : /************************************************************************/
     814             : /*                            SetMaskBand()                             */
     815             : /************************************************************************/
     816             : 
     817         257 : void ISIS3RawRasterBand::SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand)
     818             : {
     819         257 :     poMask.reset(std::move(poMaskBand));
     820         257 :     nMaskFlags = 0;
     821         257 : }
     822             : 
     823             : /************************************************************************/
     824             : /*                             GetOffset()                              */
     825             : /************************************************************************/
     826             : 
     827         181 : double ISIS3RawRasterBand::GetOffset(int *pbSuccess)
     828             : {
     829         181 :     if (pbSuccess)
     830          29 :         *pbSuccess = m_bHasOffset;
     831         181 :     return m_dfOffset;
     832             : }
     833             : 
     834             : /************************************************************************/
     835             : /*                              GetScale()                              */
     836             : /************************************************************************/
     837             : 
     838         181 : double ISIS3RawRasterBand::GetScale(int *pbSuccess)
     839             : {
     840         181 :     if (pbSuccess)
     841          29 :         *pbSuccess = m_bHasScale;
     842         181 :     return m_dfScale;
     843             : }
     844             : 
     845             : /************************************************************************/
     846             : /*                             SetOffset()                              */
     847             : /************************************************************************/
     848             : 
     849          63 : CPLErr ISIS3RawRasterBand::SetOffset(double dfNewOffset)
     850             : {
     851          63 :     m_dfOffset = dfNewOffset;
     852          63 :     m_bHasOffset = true;
     853          63 :     return CE_None;
     854             : }
     855             : 
     856             : /************************************************************************/
     857             : /*                              SetScale()                              */
     858             : /************************************************************************/
     859             : 
     860          63 : CPLErr ISIS3RawRasterBand::SetScale(double dfNewScale)
     861             : {
     862          63 :     m_dfScale = dfNewScale;
     863          63 :     m_bHasScale = true;
     864          63 :     return CE_None;
     865             : }
     866             : 
     867             : /************************************************************************/
     868             : /*                           GetNoDataValue()                           */
     869             : /************************************************************************/
     870             : 
     871         158 : double ISIS3RawRasterBand::GetNoDataValue(int *pbSuccess)
     872             : {
     873         158 :     if (pbSuccess)
     874         101 :         *pbSuccess = true;
     875         158 :     return m_dfNoData;
     876             : }
     877             : 
     878             : /************************************************************************/
     879             : /*                           SetNoDataValue()                           */
     880             : /************************************************************************/
     881             : 
     882         406 : CPLErr ISIS3RawRasterBand::SetNoDataValue(double dfNewNoData)
     883             : {
     884         406 :     m_dfNoData = dfNewNoData;
     885         406 :     return CE_None;
     886             : }
     887             : 
     888             : /************************************************************************/
     889             : /*                       ISIS3WrapperRasterBand()                       */
     890             : /************************************************************************/
     891             : 
     892          45 : ISIS3WrapperRasterBand::ISIS3WrapperRasterBand(GDALRasterBand *poBaseBandIn)
     893          45 :     : m_poBaseBand(poBaseBandIn)
     894             : {
     895          45 :     eDataType = m_poBaseBand->GetRasterDataType();
     896          45 :     m_poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
     897          45 : }
     898             : 
     899             : /************************************************************************/
     900             : /*                            SetMaskBand()                             */
     901             : /************************************************************************/
     902             : 
     903          21 : void ISIS3WrapperRasterBand::SetMaskBand(
     904             :     std::unique_ptr<GDALRasterBand> poMaskBand)
     905             : {
     906          21 :     poMask.reset(std::move(poMaskBand));
     907          21 :     nMaskFlags = 0;
     908          21 : }
     909             : 
     910             : /************************************************************************/
     911             : /*                             GetOffset()                              */
     912             : /************************************************************************/
     913             : 
     914          22 : double ISIS3WrapperRasterBand::GetOffset(int *pbSuccess)
     915             : {
     916          22 :     if (pbSuccess)
     917           4 :         *pbSuccess = m_bHasOffset;
     918          22 :     return m_dfOffset;
     919             : }
     920             : 
     921             : /************************************************************************/
     922             : /*                              GetScale()                              */
     923             : /************************************************************************/
     924             : 
     925          22 : double ISIS3WrapperRasterBand::GetScale(int *pbSuccess)
     926             : {
     927          22 :     if (pbSuccess)
     928           4 :         *pbSuccess = m_bHasScale;
     929          22 :     return m_dfScale;
     930             : }
     931             : 
     932             : /************************************************************************/
     933             : /*                             SetOffset()                              */
     934             : /************************************************************************/
     935             : 
     936          12 : CPLErr ISIS3WrapperRasterBand::SetOffset(double dfNewOffset)
     937             : {
     938          12 :     m_dfOffset = dfNewOffset;
     939          12 :     m_bHasOffset = true;
     940             : 
     941          12 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
     942          12 :     if (poGDS->m_poExternalDS && eAccess == GA_Update)
     943           4 :         poGDS->m_poExternalDS->GetRasterBand(nBand)->SetOffset(dfNewOffset);
     944             : 
     945          12 :     return CE_None;
     946             : }
     947             : 
     948             : /************************************************************************/
     949             : /*                              SetScale()                              */
     950             : /************************************************************************/
     951             : 
     952          12 : CPLErr ISIS3WrapperRasterBand::SetScale(double dfNewScale)
     953             : {
     954          12 :     m_dfScale = dfNewScale;
     955          12 :     m_bHasScale = true;
     956             : 
     957          12 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
     958          12 :     if (poGDS->m_poExternalDS && eAccess == GA_Update)
     959           4 :         poGDS->m_poExternalDS->GetRasterBand(nBand)->SetScale(dfNewScale);
     960             : 
     961          12 :     return CE_None;
     962             : }
     963             : 
     964             : /************************************************************************/
     965             : /*                           GetNoDataValue()                           */
     966             : /************************************************************************/
     967             : 
     968           4 : double ISIS3WrapperRasterBand::GetNoDataValue(int *pbSuccess)
     969             : {
     970           4 :     if (pbSuccess)
     971           4 :         *pbSuccess = true;
     972           4 :     return m_dfNoData;
     973             : }
     974             : 
     975             : /************************************************************************/
     976             : /*                           SetNoDataValue()                           */
     977             : /************************************************************************/
     978             : 
     979          45 : CPLErr ISIS3WrapperRasterBand::SetNoDataValue(double dfNewNoData)
     980             : {
     981          45 :     m_dfNoData = dfNewNoData;
     982             : 
     983          45 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
     984          45 :     if (poGDS->m_poExternalDS && eAccess == GA_Update)
     985          25 :         poGDS->m_poExternalDS->GetRasterBand(nBand)->SetNoDataValue(
     986          25 :             dfNewNoData);
     987             : 
     988          45 :     return CE_None;
     989             : }
     990             : 
     991             : /************************************************************************/
     992             : /*                              InitFile()                              */
     993             : /************************************************************************/
     994             : 
     995           8 : void ISIS3WrapperRasterBand::InitFile()
     996             : {
     997           8 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
     998           8 :     if (poGDS->m_bGeoTIFFAsRegularExternal && !poGDS->m_bGeoTIFFInitDone)
     999             :     {
    1000           8 :         poGDS->m_bGeoTIFFInitDone = true;
    1001             : 
    1002           8 :         const int nBands = poGDS->GetRasterCount();
    1003             :         // We need to make sure that blocks are written in the right order
    1004          22 :         for (int i = 0; i < nBands; i++)
    1005             :         {
    1006          14 :             poGDS->m_poExternalDS->GetRasterBand(i + 1)->Fill(m_dfNoData);
    1007             :         }
    1008           8 :         poGDS->m_poExternalDS->FlushCache(false);
    1009             : 
    1010             :         // Check that blocks are effectively written in expected order.
    1011             :         const int nBlockSizeBytes =
    1012           8 :             nBlockXSize * nBlockYSize * GDALGetDataTypeSizeBytes(eDataType);
    1013             : 
    1014           8 :         GIntBig nLastOffset = 0;
    1015           8 :         bool bGoOn = true;
    1016           8 :         const int l_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
    1017           8 :         const int l_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
    1018          22 :         for (int i = 0; i < nBands && bGoOn; i++)
    1019             :         {
    1020         641 :             for (int y = 0; y < l_nBlocksPerColumn && bGoOn; y++)
    1021             :             {
    1022        1473 :                 for (int x = 0; x < l_nBlocksPerRow && bGoOn; x++)
    1023             :                 {
    1024             :                     const char *pszBlockOffset =
    1025         846 :                         poGDS->m_poExternalDS->GetRasterBand(i + 1)
    1026         846 :                             ->GetMetadataItem(
    1027         846 :                                 CPLSPrintf("BLOCK_OFFSET_%d_%d", x, y), "TIFF");
    1028         846 :                     if (pszBlockOffset)
    1029             :                     {
    1030         846 :                         GIntBig nOffset = CPLAtoGIntBig(pszBlockOffset);
    1031         846 :                         if (i != 0 || x != 0 || y != 0)
    1032             :                         {
    1033         838 :                             if (nOffset != nLastOffset + nBlockSizeBytes)
    1034             :                             {
    1035           0 :                                 CPLError(CE_Warning, CPLE_AppDefined,
    1036             :                                          "Block %d,%d band %d not at expected "
    1037             :                                          "offset",
    1038             :                                          x, y, i + 1);
    1039           0 :                                 bGoOn = false;
    1040           0 :                                 poGDS->m_bGeoTIFFAsRegularExternal = false;
    1041             :                             }
    1042             :                         }
    1043         846 :                         nLastOffset = nOffset;
    1044             :                     }
    1045             :                     else
    1046             :                     {
    1047           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    1048             :                                  "Block %d,%d band %d not at expected "
    1049             :                                  "offset",
    1050             :                                  x, y, i + 1);
    1051           0 :                         bGoOn = false;
    1052           0 :                         poGDS->m_bGeoTIFFAsRegularExternal = false;
    1053             :                     }
    1054             :                 }
    1055             :             }
    1056             :         }
    1057             :     }
    1058           8 : }
    1059             : 
    1060             : /************************************************************************/
    1061             : /*                                Fill()                                */
    1062             : /************************************************************************/
    1063             : 
    1064           4 : CPLErr ISIS3WrapperRasterBand::Fill(double dfRealValue, double dfImaginaryValue)
    1065             : {
    1066           4 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
    1067           4 :     if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData == dfRealValue)
    1068             :     {
    1069           0 :         dfRealValue = m_dfNoData;
    1070             :     }
    1071           4 :     if (poGDS->m_bGeoTIFFAsRegularExternal && !poGDS->m_bGeoTIFFInitDone)
    1072             :     {
    1073           1 :         InitFile();
    1074             :     }
    1075             : 
    1076           4 :     return GDALProxyRasterBand::Fill(dfRealValue, dfImaginaryValue);
    1077             : }
    1078             : 
    1079             : /************************************************************************/
    1080             : /*                            IWriteBlock()                             */
    1081             : /************************************************************************/
    1082             : 
    1083           0 : CPLErr ISIS3WrapperRasterBand::IWriteBlock(int nXBlock, int nYBlock,
    1084             :                                            void *pImage)
    1085             : 
    1086             : {
    1087           0 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
    1088           0 :     if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData != m_dfNoData)
    1089             :     {
    1090           0 :         RemapNoData(eDataType, pImage, nBlockXSize * nBlockYSize,
    1091             :                     poGDS->m_dfSrcNoData, m_dfNoData);
    1092             :     }
    1093           0 :     if (poGDS->m_bGeoTIFFAsRegularExternal && !poGDS->m_bGeoTIFFInitDone)
    1094             :     {
    1095           0 :         InitFile();
    1096             :     }
    1097             : 
    1098           0 :     return GDALProxyRasterBand::IWriteBlock(nXBlock, nYBlock, pImage);
    1099             : }
    1100             : 
    1101             : /************************************************************************/
    1102             : /*                             IRasterIO()                              */
    1103             : /************************************************************************/
    1104             : 
    1105          46 : CPLErr ISIS3WrapperRasterBand::IRasterIO(
    1106             :     GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
    1107             :     void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
    1108             :     GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
    1109             : 
    1110             : {
    1111          46 :     ISIS3Dataset *poGDS = cpl::down_cast<ISIS3Dataset *>(poDS);
    1112          46 :     if (eRWFlag == GF_Write && poGDS->m_bGeoTIFFAsRegularExternal &&
    1113          17 :         !poGDS->m_bGeoTIFFInitDone)
    1114             :     {
    1115           6 :         InitFile();
    1116             :     }
    1117          46 :     if (eRWFlag == GF_Write && poGDS->m_bHasSrcNoData &&
    1118           9 :         poGDS->m_dfSrcNoData != m_dfNoData)
    1119             :     {
    1120           1 :         const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
    1121           1 :         if (eBufType == eDataType && nPixelSpace == nDTSize &&
    1122           1 :             nLineSpace == nPixelSpace * nBufXSize)
    1123             :         {
    1124           1 :             RemapNoData(eDataType, pData, nBufXSize * nBufYSize,
    1125             :                         poGDS->m_dfSrcNoData, m_dfNoData);
    1126             :         }
    1127             :         else
    1128             :         {
    1129           0 :             const GByte *pabySrc = reinterpret_cast<GByte *>(pData);
    1130             :             GByte *pabyTemp = reinterpret_cast<GByte *>(
    1131           0 :                 VSI_MALLOC3_VERBOSE(nDTSize, nBufXSize, nBufYSize));
    1132           0 :             for (int i = 0; i < nBufYSize; i++)
    1133             :             {
    1134           0 :                 GDALCopyWords(pabySrc + i * nLineSpace, eBufType,
    1135             :                               static_cast<int>(nPixelSpace),
    1136           0 :                               pabyTemp + i * nBufXSize * nDTSize, eDataType,
    1137             :                               nDTSize, nBufXSize);
    1138             :             }
    1139           0 :             RemapNoData(eDataType, pabyTemp, nBufXSize * nBufYSize,
    1140             :                         poGDS->m_dfSrcNoData, m_dfNoData);
    1141           0 :             CPLErr eErr = GDALProxyRasterBand::IRasterIO(
    1142             :                 eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nBufXSize,
    1143             :                 nBufYSize, eDataType, nDTSize,
    1144           0 :                 static_cast<GSpacing>(nDTSize) * nBufXSize, psExtraArg);
    1145           0 :             VSIFree(pabyTemp);
    1146           0 :             return eErr;
    1147             :         }
    1148             :     }
    1149          46 :     return GDALProxyRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
    1150             :                                           pData, nBufXSize, nBufYSize, eBufType,
    1151          46 :                                           nPixelSpace, nLineSpace, psExtraArg);
    1152             : }
    1153             : 
    1154             : /************************************************************************/
    1155             : /*                            ISISMaskBand()                            */
    1156             : /************************************************************************/
    1157             : 
    1158         319 : ISISMaskBand::ISISMaskBand(GDALRasterBand *poBaseBand)
    1159         319 :     : m_poBaseBand(poBaseBand), m_pBuffer(nullptr)
    1160             : {
    1161         319 :     eDataType = GDT_UInt8;
    1162         319 :     poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1163         319 :     nRasterXSize = poBaseBand->GetXSize();
    1164         319 :     nRasterYSize = poBaseBand->GetYSize();
    1165         319 : }
    1166             : 
    1167             : /************************************************************************/
    1168             : /*                           ~ISISMaskBand()                            */
    1169             : /************************************************************************/
    1170             : 
    1171         638 : ISISMaskBand::~ISISMaskBand()
    1172             : {
    1173         319 :     VSIFree(m_pBuffer);
    1174         638 : }
    1175             : 
    1176             : /************************************************************************/
    1177             : /*                              FillMask()                              */
    1178             : /************************************************************************/
    1179             : 
    1180             : template <class T>
    1181          80 : static void FillMask(void *pvBuffer, GByte *pabyDst, int nReqXSize,
    1182             :                      int nReqYSize, int nBlockXSize, T NULL_VAL, T LOW_REPR_SAT,
    1183             :                      T LOW_INSTR_SAT, T HIGH_INSTR_SAT, T HIGH_REPR_SAT)
    1184             : {
    1185          80 :     const T *pSrc = static_cast<T *>(pvBuffer);
    1186         164 :     for (int y = 0; y < nReqYSize; y++)
    1187             :     {
    1188        9724 :         for (int x = 0; x < nReqXSize; x++)
    1189             :         {
    1190        9640 :             const T nSrc = pSrc[y * nBlockXSize + x];
    1191        9640 :             if (nSrc == NULL_VAL || nSrc == LOW_REPR_SAT ||
    1192        6418 :                 nSrc == LOW_INSTR_SAT || nSrc == HIGH_INSTR_SAT ||
    1193             :                 nSrc == HIGH_REPR_SAT)
    1194             :             {
    1195        3222 :                 pabyDst[y * nBlockXSize + x] = 0;
    1196             :             }
    1197             :             else
    1198             :             {
    1199        6418 :                 pabyDst[y * nBlockXSize + x] = 255;
    1200             :             }
    1201             :         }
    1202             :     }
    1203          80 : }
    1204             : 
    1205             : /************************************************************************/
    1206             : /*                             IReadBlock()                             */
    1207             : /************************************************************************/
    1208             : 
    1209          80 : CPLErr ISISMaskBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
    1210             : 
    1211             : {
    1212          80 :     const GDALDataType eSrcDT = m_poBaseBand->GetRasterDataType();
    1213          80 :     const int nSrcDTSize = GDALGetDataTypeSizeBytes(eSrcDT);
    1214          80 :     if (m_pBuffer == nullptr)
    1215             :     {
    1216          28 :         m_pBuffer = VSI_MALLOC3_VERBOSE(nBlockXSize, nBlockYSize, nSrcDTSize);
    1217          28 :         if (m_pBuffer == nullptr)
    1218           0 :             return CE_Failure;
    1219             :     }
    1220             : 
    1221          80 :     const int nXOff = nBlockXOff * nBlockXSize;
    1222          80 :     const int nReqXSize = std::min(nBlockXSize, nRasterXSize - nXOff);
    1223          80 :     const int nYOff = nBlockYOff * nBlockYSize;
    1224          80 :     const int nReqYSize = std::min(nBlockYSize, nRasterYSize - nYOff);
    1225             : 
    1226         160 :     if (m_poBaseBand->RasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
    1227             :                                m_pBuffer, nReqXSize, nReqYSize, eSrcDT,
    1228             :                                nSrcDTSize,
    1229          80 :                                static_cast<GSpacing>(nSrcDTSize) * nBlockXSize,
    1230          80 :                                nullptr) != CE_None)
    1231             :     {
    1232           0 :         return CE_Failure;
    1233             :     }
    1234             : 
    1235          80 :     GByte *pabyDst = static_cast<GByte *>(pImage);
    1236          80 :     if (eSrcDT == GDT_UInt8)
    1237             :     {
    1238          48 :         FillMask<GByte>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
    1239             :                         ISIS3_NULL1, LOW_REPR_SAT1, LOW_INSTR_SAT1,
    1240             :                         HIGH_INSTR_SAT1, HIGH_REPR_SAT1);
    1241             :     }
    1242          32 :     else if (eSrcDT == GDT_UInt16)
    1243             :     {
    1244           8 :         FillMask<GUInt16>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
    1245             :                           ISIS3_NULLU2, LOW_REPR_SATU2, LOW_INSTR_SATU2,
    1246             :                           HIGH_INSTR_SATU2, HIGH_REPR_SATU2);
    1247             :     }
    1248          24 :     else if (eSrcDT == GDT_Int16)
    1249             :     {
    1250           8 :         FillMask<GInt16>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
    1251             :                          ISIS3_NULL2, LOW_REPR_SAT2, LOW_INSTR_SAT2,
    1252             :                          HIGH_INSTR_SAT2, HIGH_REPR_SAT2);
    1253             :     }
    1254          16 :     else if (eSrcDT == GDT_Float32)
    1255             :     {
    1256           8 :         CPLAssert(eSrcDT == GDT_Float32);
    1257           8 :         FillMask<float>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
    1258             :                         ISIS3_NULL4, LOW_REPR_SAT4, LOW_INSTR_SAT4,
    1259             :                         HIGH_INSTR_SAT4, HIGH_REPR_SAT4);
    1260             :     }
    1261             :     else
    1262             :     {
    1263           8 :         CPLAssert(eSrcDT == GDT_Float64);
    1264           8 :         FillMask<double>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
    1265             :                          ISIS3_NULL8, LOW_REPR_SAT8, LOW_INSTR_SAT8,
    1266             :                          HIGH_INSTR_SAT8, HIGH_REPR_SAT8);
    1267             :     }
    1268             : 
    1269          80 :     return CE_None;
    1270             : }
    1271             : 
    1272             : /************************************************************************/
    1273             : /*                            ISIS3Dataset()                            */
    1274             : /************************************************************************/
    1275             : 
    1276         420 : ISIS3Dataset::ISIS3Dataset()
    1277             : {
    1278         420 :     m_oKeywords.SetStripSurroundingQuotes(true);
    1279             : 
    1280             :     // Deinit JSON objects
    1281         420 :     m_oJSonLabel.Deinit();
    1282         420 :     m_oSrcJSonLabel.Deinit();
    1283         420 : }
    1284             : 
    1285             : /************************************************************************/
    1286             : /*                           ~ISIS3Dataset()                            */
    1287             : /************************************************************************/
    1288             : 
    1289         840 : ISIS3Dataset::~ISIS3Dataset()
    1290             : 
    1291             : {
    1292         420 :     ISIS3Dataset::Close();
    1293         840 : }
    1294             : 
    1295             : /************************************************************************/
    1296             : /*                               Close()                                */
    1297             : /************************************************************************/
    1298             : 
    1299         814 : CPLErr ISIS3Dataset::Close(GDALProgressFunc, void *)
    1300             : {
    1301         814 :     CPLErr eErr = CE_None;
    1302         814 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
    1303             :     {
    1304         420 :         if (!m_bIsLabelWritten)
    1305          76 :             WriteLabel();
    1306         420 :         if (m_poExternalDS && m_bGeoTIFFAsRegularExternal &&
    1307           8 :             !m_bGeoTIFFInitDone)
    1308             :         {
    1309             :             cpl::down_cast<ISIS3WrapperRasterBand *>(GetRasterBand(1))
    1310           0 :                 ->InitFile();
    1311             :         }
    1312         420 :         if (ISIS3Dataset::FlushCache(true) != CE_None)
    1313           0 :             eErr = CE_Failure;
    1314         420 :         if (m_fpLabel)
    1315             :         {
    1316         139 :             if (VSIFCloseL(m_fpLabel) != 0)
    1317           0 :                 eErr = CE_Failure;
    1318             :         }
    1319         420 :         if (m_fpImage && m_fpImage != m_fpLabel)
    1320             :         {
    1321         259 :             if (VSIFCloseL(m_fpImage) != 0)
    1322           0 :                 eErr = CE_Failure;
    1323             :         }
    1324             : 
    1325         420 :         ISIS3Dataset::CloseDependentDatasets();
    1326         420 :         if (GDALPamDataset::Close() != CE_None)
    1327           0 :             eErr = CE_Failure;
    1328             :     }
    1329         814 :     return eErr;
    1330             : }
    1331             : 
    1332             : /************************************************************************/
    1333             : /*                       CloseDependentDatasets()                       */
    1334             : /************************************************************************/
    1335             : 
    1336         420 : int ISIS3Dataset::CloseDependentDatasets()
    1337             : {
    1338         420 :     int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
    1339             : 
    1340         420 :     if (m_poExternalDS)
    1341             :     {
    1342          43 :         bHasDroppedRef = FALSE;
    1343          43 :         delete m_poExternalDS;
    1344          43 :         m_poExternalDS = nullptr;
    1345             :     }
    1346             : 
    1347         921 :     for (int iBand = 0; iBand < nBands; iBand++)
    1348             :     {
    1349         501 :         delete papoBands[iBand];
    1350             :     }
    1351         420 :     nBands = 0;
    1352             : 
    1353         420 :     return bHasDroppedRef;
    1354             : }
    1355             : 
    1356             : /************************************************************************/
    1357             : /*                            GetFileList()                             */
    1358             : /************************************************************************/
    1359             : 
    1360          95 : char **ISIS3Dataset::GetFileList()
    1361             : 
    1362             : {
    1363          95 :     char **papszFileList = GDALPamDataset::GetFileList();
    1364             : 
    1365          95 :     if (!m_osExternalFilename.empty())
    1366          27 :         papszFileList = CSLAddString(papszFileList, m_osExternalFilename);
    1367         121 :     for (int i = 0; i < m_aosAdditionalFiles.Count(); ++i)
    1368             :     {
    1369          26 :         if (CSLFindString(papszFileList, m_aosAdditionalFiles[i]) < 0)
    1370             :         {
    1371             :             papszFileList =
    1372          26 :                 CSLAddString(papszFileList, m_aosAdditionalFiles[i]);
    1373             :         }
    1374             :     }
    1375             : 
    1376          95 :     return papszFileList;
    1377             : }
    1378             : 
    1379             : /************************************************************************/
    1380             : /*                           GetSpatialRef()                            */
    1381             : /************************************************************************/
    1382             : 
    1383          78 : const OGRSpatialReference *ISIS3Dataset::GetSpatialRef() const
    1384             : 
    1385             : {
    1386          78 :     if (!m_oSRS.IsEmpty())
    1387          54 :         return &m_oSRS;
    1388             : 
    1389          24 :     return GDALPamDataset::GetSpatialRef();
    1390             : }
    1391             : 
    1392             : /************************************************************************/
    1393             : /*                           SetSpatialRef()                            */
    1394             : /************************************************************************/
    1395             : 
    1396          67 : CPLErr ISIS3Dataset::SetSpatialRef(const OGRSpatialReference *poSRS)
    1397             : {
    1398          67 :     if (eAccess == GA_ReadOnly)
    1399           0 :         return GDALPamDataset::SetSpatialRef(poSRS);
    1400          67 :     if (poSRS)
    1401          67 :         m_oSRS = *poSRS;
    1402             :     else
    1403           0 :         m_oSRS.Clear();
    1404          67 :     if (m_poExternalDS)
    1405           6 :         m_poExternalDS->SetSpatialRef(poSRS);
    1406          67 :     InvalidateLabel();
    1407          67 :     return CE_None;
    1408             : }
    1409             : 
    1410             : /************************************************************************/
    1411             : /*                          GetGeoTransform()                           */
    1412             : /************************************************************************/
    1413             : 
    1414          89 : CPLErr ISIS3Dataset::GetGeoTransform(GDALGeoTransform &gt) const
    1415             : 
    1416             : {
    1417          89 :     if (m_bGotTransform)
    1418             :     {
    1419          67 :         gt = m_gt;
    1420          67 :         return CE_None;
    1421             :     }
    1422             : 
    1423          22 :     return GDALPamDataset::GetGeoTransform(gt);
    1424             : }
    1425             : 
    1426             : /************************************************************************/
    1427             : /*                          SetGeoTransform()                           */
    1428             : /************************************************************************/
    1429             : 
    1430          59 : CPLErr ISIS3Dataset::SetGeoTransform(const GDALGeoTransform &gt)
    1431             : 
    1432             : {
    1433          59 :     if (eAccess == GA_ReadOnly)
    1434           0 :         return GDALPamDataset::SetGeoTransform(gt);
    1435          59 :     if (gt.xscale <= 0.0 || gt.xscale != -gt.yscale || gt.xrot != 0.0 ||
    1436          59 :         gt.yrot != 0.0)
    1437             :     {
    1438           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1439             :                  "Only north-up geotransform with square pixels supported");
    1440           0 :         return CE_Failure;
    1441             :     }
    1442          59 :     m_bGotTransform = true;
    1443          59 :     m_gt = gt;
    1444          59 :     if (m_poExternalDS)
    1445           6 :         m_poExternalDS->SetGeoTransform(m_gt);
    1446          59 :     InvalidateLabel();
    1447          59 :     return CE_None;
    1448             : }
    1449             : 
    1450             : /************************************************************************/
    1451             : /*                       GetMetadataDomainList()                        */
    1452             : /************************************************************************/
    1453             : 
    1454           1 : char **ISIS3Dataset::GetMetadataDomainList()
    1455             : {
    1456           1 :     return BuildMetadataDomainList(nullptr, FALSE, "", "json:ISIS3", nullptr);
    1457             : }
    1458             : 
    1459             : /************************************************************************/
    1460             : /*                            GetMetadata()                             */
    1461             : /************************************************************************/
    1462             : 
    1463         129 : CSLConstList ISIS3Dataset::GetMetadata(const char *pszDomain)
    1464             : {
    1465         129 :     if (pszDomain != nullptr && EQUAL(pszDomain, "json:ISIS3"))
    1466             :     {
    1467          66 :         if (m_aosISIS3MD.empty())
    1468             :         {
    1469          60 :             if (eAccess == GA_Update && !m_oJSonLabel.IsValid())
    1470             :             {
    1471           1 :                 BuildLabel();
    1472             :             }
    1473          60 :             CPLAssert(m_oJSonLabel.IsValid());
    1474          60 :             if (m_bResolveOfflineContent && !m_bHasResolvedOfflineContent)
    1475             :             {
    1476          59 :                 ResolveOfflineContentOfLabel();
    1477             :             }
    1478             :             const CPLString osJson =
    1479         120 :                 m_oJSonLabel.Format(CPLJSONObject::PrettyFormat::Pretty);
    1480          60 :             m_aosISIS3MD.InsertString(0, osJson.c_str());
    1481             :         }
    1482          66 :         return m_aosISIS3MD.List();
    1483             :     }
    1484          63 :     return GDALPamDataset::GetMetadata(pszDomain);
    1485             : }
    1486             : 
    1487             : /************************************************************************/
    1488             : /*                    ResolveOfflineContentOfLabel()                    */
    1489             : /************************************************************************/
    1490             : 
    1491          59 : void ISIS3Dataset::ResolveOfflineContentOfLabel()
    1492             : {
    1493          59 :     m_bHasResolvedOfflineContent = true;
    1494             : 
    1495         118 :     std::vector<GByte> abyData;
    1496             : 
    1497         296 :     for (CPLJSONObject &oObj : m_oJSonLabel.GetChildren())
    1498             :     {
    1499         237 :         if (oObj.GetType() == CPLJSONObject::Type::Object)
    1500             :         {
    1501         179 :             CPLString osContainerName = oObj.GetName();
    1502         358 :             CPLJSONObject oContainerName = oObj.GetObj("_container_name");
    1503         179 :             if (oContainerName.GetType() == CPLJSONObject::Type::String)
    1504             :             {
    1505          72 :                 osContainerName = oContainerName.ToString();
    1506             :             }
    1507             : 
    1508         179 :             std::string osFilename;
    1509         179 :             CPLJSONObject oFilename = oObj.GetObj("^" + osContainerName);
    1510         179 :             if (oFilename.GetType() == CPLJSONObject::Type::String)
    1511             :             {
    1512             :                 VSIStatBufL sStat;
    1513         108 :                 osFilename = CPLFormFilenameSafe(
    1514          72 :                     CPLGetPathSafe(GetDescription()).c_str(),
    1515         108 :                     oFilename.ToString().c_str(), nullptr);
    1516          36 :                 if (CPLHasPathTraversal(oFilename.ToString().c_str()))
    1517             :                 {
    1518           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1519             :                              "Path traversal detected for ^%s: %s",
    1520             :                              osContainerName.c_str(),
    1521           0 :                              oFilename.ToString().c_str());
    1522          17 :                     continue;
    1523             :                 }
    1524          36 :                 else if (VSIStatL(osFilename.c_str(), &sStat) != 0)
    1525             :                 {
    1526          17 :                     CPLDebug("ISIS3", "File %s referenced but not foud",
    1527             :                              osFilename.c_str());
    1528          17 :                     continue;
    1529             :                 }
    1530             :             }
    1531             :             else
    1532             :             {
    1533         143 :                 osFilename = GetDescription();
    1534             :             }
    1535             : 
    1536         324 :             CPLJSONObject oBytes = oObj.GetObj("Bytes");
    1537         162 :             if (oBytes.GetType() == CPLJSONObject::Type::Long)
    1538             :             {
    1539           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1540             :                          "Too large content reference by %s to be captured in "
    1541             :                          "json:ISIS3 metadata domain",
    1542           0 :                          oObj.GetName().c_str());
    1543           0 :                 continue;
    1544             :             }
    1545         257 :             else if (oBytes.GetType() != CPLJSONObject::Type::Integer ||
    1546          95 :                      oBytes.ToInteger() <= 0)
    1547             :             {
    1548          67 :                 continue;
    1549             :             }
    1550          95 :             if (oBytes.ToInteger() > m_nMaxOfflineContentSize)
    1551             :             {
    1552           1 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1553             :                          "Too large content reference by %s to be captured in "
    1554             :                          "json:ISIS3 metadata domain",
    1555           2 :                          oObj.GetName().c_str());
    1556           1 :                 continue;
    1557             :             }
    1558             : 
    1559         188 :             CPLJSONObject oStartByte = oObj.GetObj("StartByte");
    1560          94 :             if ((oStartByte.GetType() != CPLJSONObject::Type::Integer &&
    1561         143 :                  oStartByte.GetType() != CPLJSONObject::Type::Long) ||
    1562          49 :                 oStartByte.ToLong() <= 0)
    1563             :             {
    1564          45 :                 continue;
    1565             :             }
    1566             : 
    1567             :             // 1-based offsets
    1568             :             const auto nOffset =
    1569          49 :                 static_cast<vsi_l_offset>(oStartByte.ToLong() - 1);
    1570             : 
    1571          49 :             const auto nSize = static_cast<size_t>(oBytes.ToInteger());
    1572             :             try
    1573             :             {
    1574          49 :                 abyData.resize(nSize);
    1575             :             }
    1576           0 :             catch (const std::exception &)
    1577             :             {
    1578           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    1579             :                          "Out of memory: too large content referenced by %s "
    1580             :                          "to be captured in json:ISIS3 metadata domain",
    1581           0 :                          oObj.GetName().c_str());
    1582           0 :                 continue;
    1583             :             }
    1584             : 
    1585          49 :             VSIVirtualHandleUniquePtr fp(VSIFOpenL(osFilename.c_str(), "rb"));
    1586          49 :             if (!fp)
    1587             :             {
    1588           0 :                 CPLError(CE_Warning, CPLE_FileIO,
    1589             :                          "Cannot open %s referenced by %s", osFilename.c_str(),
    1590           0 :                          oObj.GetName().c_str());
    1591           0 :                 continue;
    1592             :             }
    1593             : 
    1594          98 :             if (fp->Seek(nOffset, SEEK_SET) != 0 ||
    1595          49 :                 fp->Read(abyData.data(), abyData.size(), 1) != 1)
    1596             :             {
    1597           0 :                 CPLError(CE_Warning, CPLE_FileIO,
    1598             :                          "Cannot read %u bytes at offset %" PRIu64
    1599             :                          " in %s, referenced by %s",
    1600           0 :                          static_cast<unsigned>(abyData.size()),
    1601             :                          static_cast<uint64_t>(nOffset), osFilename.c_str(),
    1602           0 :                          oObj.GetName().c_str());
    1603           0 :                 continue;
    1604             :             }
    1605             : 
    1606          98 :             CPLJSONObject oData;
    1607          49 :             char *pszHex = CPLBinaryToHex(static_cast<int>(abyData.size()),
    1608          49 :                                           abyData.data());
    1609          49 :             oObj.Add("_data", pszHex);
    1610          49 :             CPLFree(pszHex);
    1611             :         }
    1612             :     }
    1613          59 : }
    1614             : 
    1615             : /************************************************************************/
    1616             : /*                          InvalidateLabel()                           */
    1617             : /************************************************************************/
    1618             : 
    1619         154 : void ISIS3Dataset::InvalidateLabel()
    1620             : {
    1621         154 :     m_oJSonLabel.Deinit();
    1622         154 :     m_aosISIS3MD.Clear();
    1623         154 :     m_bHasResolvedOfflineContent = false;
    1624         154 : }
    1625             : 
    1626             : /************************************************************************/
    1627             : /*                            SetMetadata()                             */
    1628             : /************************************************************************/
    1629             : 
    1630          28 : CPLErr ISIS3Dataset::SetMetadata(CSLConstList papszMD, const char *pszDomain)
    1631             : {
    1632          28 :     if (m_bUseSrcLabel && eAccess == GA_Update && pszDomain != nullptr &&
    1633          28 :         EQUAL(pszDomain, "json:ISIS3"))
    1634             :     {
    1635          28 :         m_oSrcJSonLabel.Deinit();
    1636          28 :         InvalidateLabel();
    1637          28 :         if (papszMD != nullptr && papszMD[0] != nullptr)
    1638             :         {
    1639          28 :             CPLJSONDocument oJSONDocument;
    1640          28 :             const GByte *pabyData = reinterpret_cast<const GByte *>(papszMD[0]);
    1641          28 :             if (!oJSONDocument.LoadMemory(pabyData))
    1642             :             {
    1643           1 :                 return CE_Failure;
    1644             :             }
    1645             : 
    1646          27 :             m_oSrcJSonLabel = oJSONDocument.GetRoot();
    1647          27 :             if (!m_oSrcJSonLabel.IsValid())
    1648             :             {
    1649           0 :                 return CE_Failure;
    1650             :             }
    1651             :         }
    1652          27 :         return CE_None;
    1653             :     }
    1654           0 :     return GDALPamDataset::SetMetadata(papszMD, pszDomain);
    1655             : }
    1656             : 
    1657             : /************************************************************************/
    1658             : /*                         GetRawBinaryLayout()                         */
    1659             : /************************************************************************/
    1660             : 
    1661           2 : bool ISIS3Dataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
    1662             : {
    1663           2 :     if (m_sLayout.osRawFilename.empty())
    1664           0 :         return false;
    1665           2 :     sLayout = m_sLayout;
    1666           2 :     return true;
    1667             : }
    1668             : 
    1669             : /************************************************************************/
    1670             : /*                          GetValueAndUnits()                          */
    1671             : /************************************************************************/
    1672             : 
    1673          50 : static void GetValueAndUnits(const CPLJSONObject &obj,
    1674             :                              std::vector<double> &adfValues,
    1675             :                              std::vector<std::string> &aosUnits,
    1676             :                              int nExpectedVals)
    1677             : {
    1678         100 :     if (obj.GetType() == CPLJSONObject::Type::Integer ||
    1679          50 :         obj.GetType() == CPLJSONObject::Type::Double)
    1680             :     {
    1681          32 :         adfValues.push_back(obj.ToDouble());
    1682             :     }
    1683          18 :     else if (obj.GetType() == CPLJSONObject::Type::Object)
    1684             :     {
    1685          24 :         auto oValue = obj.GetObj("value");
    1686          24 :         auto oUnit = obj.GetObj("unit");
    1687           8 :         if (oValue.IsValid() &&
    1688           8 :             (oValue.GetType() == CPLJSONObject::Type::Integer ||
    1689           2 :              oValue.GetType() == CPLJSONObject::Type::Double ||
    1690           2 :              oValue.GetType() == CPLJSONObject::Type::Array) &&
    1691          16 :             oUnit.IsValid() && oUnit.GetType() == CPLJSONObject::Type::String)
    1692             :         {
    1693           8 :             if (oValue.GetType() == CPLJSONObject::Type::Array)
    1694             :             {
    1695           2 :                 GetValueAndUnits(oValue, adfValues, aosUnits, nExpectedVals);
    1696             :             }
    1697             :             else
    1698             :             {
    1699           6 :                 adfValues.push_back(oValue.ToDouble());
    1700             :             }
    1701           8 :             aosUnits.push_back(oUnit.ToString());
    1702             :         }
    1703             :     }
    1704          10 :     else if (obj.GetType() == CPLJSONObject::Type::Array)
    1705             :     {
    1706          10 :         auto oArray = obj.ToArray();
    1707          10 :         if (oArray.Size() == nExpectedVals)
    1708             :         {
    1709          34 :             for (int i = 0; i < nExpectedVals; i++)
    1710             :             {
    1711          60 :                 if (oArray[i].GetType() == CPLJSONObject::Type::Integer ||
    1712          36 :                     oArray[i].GetType() == CPLJSONObject::Type::Double)
    1713             :                 {
    1714          24 :                     adfValues.push_back(oArray[i].ToDouble());
    1715             :                 }
    1716             :                 else
    1717             :                 {
    1718           0 :                     adfValues.clear();
    1719           0 :                     return;
    1720             :                 }
    1721             :             }
    1722             :         }
    1723             :     }
    1724             : }
    1725             : 
    1726             : /************************************************************************/
    1727             : /*                                Open()                                */
    1728             : /************************************************************************/
    1729             : 
    1730         281 : GDALDataset *ISIS3Dataset::Open(GDALOpenInfo *poOpenInfo)
    1731             : 
    1732             : {
    1733             :     /* -------------------------------------------------------------------- */
    1734             :     /*      Does this look like a CUBE dataset?                             */
    1735             :     /* -------------------------------------------------------------------- */
    1736         281 :     if (!ISIS3DriverIdentify(poOpenInfo))
    1737           0 :         return nullptr;
    1738             : 
    1739             :     /* -------------------------------------------------------------------- */
    1740             :     /*      Open the file using the large file API.                         */
    1741             :     /* -------------------------------------------------------------------- */
    1742         562 :     auto poDS = std::make_unique<ISIS3Dataset>();
    1743             : 
    1744         281 :     if (!poDS->m_oKeywords.Ingest(poOpenInfo->fpL, 0))
    1745             :     {
    1746           1 :         VSIFCloseL(poOpenInfo->fpL);
    1747           1 :         poOpenInfo->fpL = nullptr;
    1748           1 :         return nullptr;
    1749             :     }
    1750             : 
    1751         280 :     poDS->m_bResolveOfflineContent = CPLTestBool(CSLFetchNameValueDef(
    1752         280 :         poOpenInfo->papszOpenOptions, "INCLUDE_OFFLINE_CONTENT", "YES"));
    1753         280 :     poDS->m_nMaxOfflineContentSize = std::max(
    1754         280 :         0, atoi(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
    1755         280 :                                      "MAX_SIZE_OFFLINE_CONTENT", "100000000")));
    1756             : 
    1757         280 :     poDS->m_oJSonLabel = poDS->m_oKeywords.GetJsonObject();
    1758         280 :     poDS->m_oJSonLabel.Add("_filename", poOpenInfo->pszFilename);
    1759             : 
    1760             :     // Find additional files from the label
    1761        1389 :     for (const CPLJSONObject &oObj : poDS->m_oJSonLabel.GetChildren())
    1762             :     {
    1763        1109 :         if (oObj.GetType() == CPLJSONObject::Type::Object)
    1764             :         {
    1765        1658 :             CPLString osContainerName = oObj.GetName();
    1766        2487 :             CPLJSONObject oContainerName = oObj.GetObj("_container_name");
    1767         829 :             if (oContainerName.GetType() == CPLJSONObject::Type::String)
    1768             :             {
    1769         298 :                 osContainerName = oContainerName.ToString();
    1770             :             }
    1771             : 
    1772        1658 :             CPLJSONObject oFilename = oObj.GetObj("^" + osContainerName);
    1773         829 :             if (oFilename.GetType() == CPLJSONObject::Type::String)
    1774             :             {
    1775             :                 VSIStatBufL sStat;
    1776         369 :                 const CPLString osFilename(CPLFormFilenameSafe(
    1777         246 :                     CPLGetPathSafe(poOpenInfo->pszFilename).c_str(),
    1778         492 :                     oFilename.ToString().c_str(), nullptr));
    1779         123 :                 if (CPLHasPathTraversal(oFilename.ToString().c_str()))
    1780             :                 {
    1781           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    1782             :                              "Path traversal detected for ^%s: %s",
    1783             :                              osContainerName.c_str(),
    1784           0 :                              oFilename.ToString().c_str());
    1785             :                 }
    1786         123 :                 else if (VSIStatL(osFilename, &sStat) == 0)
    1787             :                 {
    1788          81 :                     poDS->m_aosAdditionalFiles.AddString(osFilename);
    1789             :                 }
    1790             :                 else
    1791             :                 {
    1792          42 :                     CPLDebug("ISIS3", "File %s referenced but not foud",
    1793             :                              osFilename.c_str());
    1794             :                 }
    1795             :             }
    1796             :         }
    1797             :     }
    1798             : 
    1799         280 :     VSIFCloseL(poOpenInfo->fpL);
    1800         280 :     poOpenInfo->fpL = nullptr;
    1801             : 
    1802             :     /* -------------------------------------------------------------------- */
    1803             :     /* Assume user is pointing to label (i.e. .lbl) file for detached option */
    1804             :     /* -------------------------------------------------------------------- */
    1805             :     //  Image can be inline or detached and point to an image name
    1806             :     //  the Format can be Tiled or Raw
    1807             :     //  Object = Core
    1808             :     //      StartByte   = 65537
    1809             :     //      Format      = Tile
    1810             :     //      TileSamples = 128
    1811             :     //      TileLines   = 128
    1812             :     // OR-----
    1813             :     //  Object = Core
    1814             :     //      StartByte = 1
    1815             :     //      ^Core     = r0200357_detatched.cub
    1816             :     //      Format    = BandSequential
    1817             :     // OR-----
    1818             :     //  Object = Core
    1819             :     //      StartByte = 1
    1820             :     //      ^Core     = r0200357_detached_tiled.cub
    1821             :     //      Format      = Tile
    1822             :     //      TileSamples = 128
    1823             :     //      TileLines   = 128
    1824             :     // OR-----
    1825             :     //  Object = Core
    1826             :     //      StartByte = 1
    1827             :     //      ^Core     = some.tif
    1828             :     //      Format    = GeoTIFF
    1829             : 
    1830             :     /* -------------------------------------------------------------------- */
    1831             :     /*      What file contains the actual data?                             */
    1832             :     /* -------------------------------------------------------------------- */
    1833         280 :     const char *pszCore = poDS->GetKeyword("IsisCube.Core.^Core");
    1834             :     CPLString osQubeFile(
    1835         280 :         EQUAL(pszCore, "")
    1836         376 :             ? CPLString(poOpenInfo->pszFilename)
    1837             :             : CPLFormFilenameSafe(
    1838         472 :                   CPLGetPathSafe(poOpenInfo->pszFilename).c_str(), pszCore,
    1839         560 :                   nullptr));
    1840         280 :     if (CPLHasPathTraversal(pszCore))
    1841             :     {
    1842           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1843             :                  "Path traversal detected in IsisCube.Core.^Core: %s", pszCore);
    1844           0 :         return nullptr;
    1845             :     }
    1846         280 :     if (!EQUAL(pszCore, ""))
    1847             :     {
    1848          96 :         poDS->m_osExternalFilename = osQubeFile;
    1849             :     }
    1850             : 
    1851             :     /* -------------------------------------------------------------------- */
    1852             :     /*      Check if file an ISIS3 header file?  Read a few lines of text   */
    1853             :     /*      searching for something starting with nrows or ncols.           */
    1854             :     /* -------------------------------------------------------------------- */
    1855             : 
    1856             :     /*************   Skipbytes     *****************************/
    1857         280 :     int nSkipBytes = atoi(poDS->GetKeyword("IsisCube.Core.StartByte", "1"));
    1858         280 :     if (nSkipBytes <= 1)
    1859          99 :         nSkipBytes = 0;
    1860             :     else
    1861         181 :         nSkipBytes -= 1;
    1862             : 
    1863             :     /*******   Grab format type (BandSequential, Tiled)  *******/
    1864         560 :     CPLString osFormat = poDS->GetKeyword("IsisCube.Core.Format");
    1865             : 
    1866         280 :     int tileSizeX = 0;
    1867         280 :     int tileSizeY = 0;
    1868             : 
    1869         280 :     if (EQUAL(osFormat, "Tile"))
    1870             :     {
    1871          26 :         poDS->m_bIsTiled = true;
    1872             :         /******* Get Tile Sizes *********/
    1873          26 :         tileSizeX = atoi(poDS->GetKeyword("IsisCube.Core.TileSamples"));
    1874          26 :         tileSizeY = atoi(poDS->GetKeyword("IsisCube.Core.TileLines"));
    1875          26 :         if (tileSizeX <= 0 || tileSizeY <= 0)
    1876             :         {
    1877           1 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1878             :                      "Wrong tile dimensions : %d x %d", tileSizeX, tileSizeY);
    1879           1 :             return nullptr;
    1880             :         }
    1881             :     }
    1882         254 :     else if (!EQUAL(osFormat, "BandSequential") && !EQUAL(osFormat, "GeoTIFF"))
    1883             :     {
    1884           1 :         CPLError(CE_Failure, CPLE_OpenFailed, "%s format not supported.",
    1885             :                  osFormat.c_str());
    1886           1 :         return nullptr;
    1887             :     }
    1888             : 
    1889             :     /***********   Grab samples lines band ************/
    1890             :     const int nCols =
    1891         278 :         atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Samples"));
    1892         278 :     const int nRows = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Lines"));
    1893         278 :     const int nBands = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Bands"));
    1894             : 
    1895             :     /****** Grab format type - ISIS3 only supports 8,U16,S16,32 *****/
    1896         278 :     GDALDataType eDataType = GDT_UInt8;
    1897         278 :     double dfNoData = 0.0;
    1898             : 
    1899         278 :     const char *itype = poDS->GetKeyword("IsisCube.Core.Pixels.Type");
    1900         278 :     if (EQUAL(itype, "UnsignedByte"))
    1901             :     {
    1902         215 :         eDataType = GDT_UInt8;
    1903         215 :         dfNoData = ISIS3_NULL1;
    1904             :     }
    1905          63 :     else if (EQUAL(itype, "UnsignedWord"))
    1906             :     {
    1907          17 :         eDataType = GDT_UInt16;
    1908          17 :         dfNoData = ISIS3_NULLU2;
    1909             :     }
    1910          46 :     else if (EQUAL(itype, "SignedWord"))
    1911             :     {
    1912          14 :         eDataType = GDT_Int16;
    1913          14 :         dfNoData = ISIS3_NULL2;
    1914             :     }
    1915          32 :     else if (EQUAL(itype, "Real") || EQUAL(itype, ""))
    1916             :     {
    1917          19 :         eDataType = GDT_Float32;
    1918          19 :         dfNoData = ISIS3_NULL4;
    1919             :     }
    1920          13 :     else if (EQUAL(itype, "Double"))
    1921             :     {
    1922          12 :         eDataType = GDT_Float64;
    1923          12 :         dfNoData = ISIS3_NULL8;
    1924             :     }
    1925             :     else
    1926             :     {
    1927           1 :         CPLError(CE_Failure, CPLE_OpenFailed, "%s pixel type not supported.",
    1928             :                  itype);
    1929           1 :         return nullptr;
    1930             :     }
    1931             : 
    1932             :     /***********   Grab samples lines band ************/
    1933             : 
    1934             :     // default to MSB
    1935             :     const bool bIsLSB =
    1936         277 :         EQUAL(poDS->GetKeyword("IsisCube.Core.Pixels.ByteOrder"), "Lsb");
    1937             : 
    1938             :     /***********   Grab Cellsize ************/
    1939         277 :     double dfXDim = 1.0;
    1940         277 :     double dfYDim = 1.0;
    1941             : 
    1942         277 :     const char *pszRes = poDS->GetKeyword("IsisCube.Mapping.PixelResolution");
    1943         277 :     if (strlen(pszRes) > 0)
    1944             :     {
    1945         107 :         dfXDim = CPLAtof(pszRes); /* values are in meters */
    1946         107 :         dfYDim = -CPLAtof(pszRes);
    1947             :     }
    1948             : 
    1949             :     /***********   Grab UpperLeftCornerY ************/
    1950         277 :     double dfULYMap = 0.5;
    1951             : 
    1952         277 :     const char *pszULY = poDS->GetKeyword("IsisCube.Mapping.UpperLeftCornerY");
    1953         277 :     if (strlen(pszULY) > 0)
    1954             :     {
    1955         107 :         dfULYMap = CPLAtof(pszULY);
    1956             :     }
    1957             : 
    1958             :     /***********   Grab UpperLeftCornerX ************/
    1959         277 :     double dfULXMap = 0.5;
    1960             : 
    1961         277 :     const char *pszULX = poDS->GetKeyword("IsisCube.Mapping.UpperLeftCornerX");
    1962         277 :     if (strlen(pszULX) > 0)
    1963             :     {
    1964         107 :         dfULXMap = CPLAtof(pszULX);
    1965             :     }
    1966             : 
    1967             :     // Read the projection from the ISIS PVL Mapping group.
    1968             :     const CPLJSONObject oMapping =
    1969         831 :         poDS->m_oJSonLabel.GetObj("IsisCube").GetObj("Mapping");
    1970         277 :     if (oMapping.IsValid())
    1971         123 :         poDS->m_oSRS.importFromISISPVL(oMapping);
    1972             : 
    1973             :     /* END ISIS3 Label Read */
    1974             :     /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
    1975             : 
    1976             :     /* -------------------------------------------------------------------- */
    1977             :     /*      Did we get the required keywords?  If not we return with        */
    1978             :     /*      this never having been considered to be a match. This isn't     */
    1979             :     /*      an error!                                                       */
    1980             :     /* -------------------------------------------------------------------- */
    1981         553 :     if (!GDALCheckDatasetDimensions(nCols, nRows) ||
    1982         276 :         !GDALCheckBandCount(nBands, false))
    1983             :     {
    1984           2 :         return nullptr;
    1985             :     }
    1986             : 
    1987             :     /* -------------------------------------------------------------------- */
    1988             :     /*      Capture some information from the file that is of interest.     */
    1989             :     /* -------------------------------------------------------------------- */
    1990         275 :     poDS->nRasterXSize = nCols;
    1991         275 :     poDS->nRasterYSize = nRows;
    1992             : 
    1993             :     /* -------------------------------------------------------------------- */
    1994             :     /*      Open target binary file.                                        */
    1995             :     /* -------------------------------------------------------------------- */
    1996         275 :     if (EQUAL(osFormat, "GeoTIFF"))
    1997             :     {
    1998          27 :         if (nSkipBytes != 0)
    1999             :         {
    2000           2 :             CPLError(CE_Warning, CPLE_NotSupported,
    2001             :                      "Ignoring StartByte=%d for format=GeoTIFF",
    2002             :                      1 + nSkipBytes);
    2003             :         }
    2004          27 :         if (osQubeFile == poOpenInfo->pszFilename)
    2005             :         {
    2006           0 :             CPLError(CE_Failure, CPLE_AppDefined, "A ^Core file must be set");
    2007           0 :             return nullptr;
    2008             :         }
    2009          54 :         poDS->m_poExternalDS =
    2010          27 :             GDALDataset::Open(osQubeFile, poOpenInfo->eAccess);
    2011          27 :         if (poDS->m_poExternalDS == nullptr)
    2012             :         {
    2013           2 :             return nullptr;
    2014             :         }
    2015          25 :         if (poDS->m_poExternalDS->GetRasterXSize() != poDS->nRasterXSize ||
    2016          24 :             poDS->m_poExternalDS->GetRasterYSize() != poDS->nRasterYSize ||
    2017          71 :             poDS->m_poExternalDS->GetRasterCount() != nBands ||
    2018          22 :             poDS->m_poExternalDS->GetRasterBand(1)->GetRasterDataType() !=
    2019             :                 eDataType)
    2020             :         {
    2021           4 :             CPLError(CE_Failure, CPLE_AppDefined,
    2022             :                      "%s has incompatible characteristics with the ones "
    2023             :                      "declared in the label.",
    2024             :                      osQubeFile.c_str());
    2025           4 :             return nullptr;
    2026             :         }
    2027             :     }
    2028             :     else
    2029             :     {
    2030         248 :         if (poOpenInfo->eAccess == GA_ReadOnly)
    2031         246 :             poDS->m_fpImage = VSIFOpenL(osQubeFile, "r");
    2032             :         else
    2033           2 :             poDS->m_fpImage = VSIFOpenL(osQubeFile, "r+");
    2034             : 
    2035         248 :         if (poDS->m_fpImage == nullptr)
    2036             :         {
    2037           2 :             CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s: %s.",
    2038           2 :                      osQubeFile.c_str(), VSIStrerror(errno));
    2039           2 :             return nullptr;
    2040             :         }
    2041             : 
    2042             :         // Sanity checks in case the external raw file appears to be a
    2043             :         // TIFF file
    2044         246 :         if (EQUAL(CPLGetExtensionSafe(osQubeFile).c_str(), "tif"))
    2045             :         {
    2046          23 :             const char *const apszAllowedDrivers[] = {"GTiff", nullptr};
    2047             :             auto poTIF_DS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
    2048             :                 osQubeFile, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR,
    2049          46 :                 apszAllowedDrivers));
    2050          23 :             if (poTIF_DS)
    2051             :             {
    2052          23 :                 bool bWarned = false;
    2053          23 :                 if (poTIF_DS->GetRasterXSize() != poDS->nRasterXSize ||
    2054          22 :                     poTIF_DS->GetRasterYSize() != poDS->nRasterYSize ||
    2055          21 :                     poTIF_DS->GetRasterCount() != nBands ||
    2056          20 :                     poTIF_DS->GetRasterBand(1)->GetRasterDataType() !=
    2057          45 :                         eDataType ||
    2058          38 :                     poTIF_DS->GetMetadataItem(GDALMD_COMPRESSION,
    2059          19 :                                               GDAL_MDD_IMAGE_STRUCTURE) !=
    2060             :                         nullptr)
    2061             :                 {
    2062           5 :                     bWarned = true;
    2063           5 :                     CPLError(
    2064             :                         CE_Warning, CPLE_AppDefined,
    2065             :                         "%s has incompatible characteristics with the ones "
    2066             :                         "declared in the label.",
    2067             :                         osQubeFile.c_str());
    2068             :                 }
    2069          23 :                 int nBlockXSize = 1, nBlockYSize = 1;
    2070          23 :                 poTIF_DS->GetRasterBand(1)->GetBlockSize(&nBlockXSize,
    2071             :                                                          &nBlockYSize);
    2072          23 :                 if ((poDS->m_bIsTiled &&
    2073          46 :                      (nBlockXSize != tileSizeX || nBlockYSize != tileSizeY)) ||
    2074          23 :                     (!poDS->m_bIsTiled && (nBlockXSize != nCols ||
    2075           2 :                                            (nBands > 1 && nBlockYSize != 1))))
    2076             :                 {
    2077           2 :                     if (!bWarned)
    2078             :                     {
    2079           1 :                         bWarned = true;
    2080           1 :                         CPLError(
    2081             :                             CE_Warning, CPLE_AppDefined,
    2082             :                             "%s has incompatible characteristics with the ones "
    2083             :                             "declared in the label.",
    2084             :                             osQubeFile.c_str());
    2085             :                     }
    2086             :                 }
    2087             :                 // to please Clang Static Analyzer
    2088          23 :                 nBlockXSize = std::max(1, nBlockXSize);
    2089          23 :                 nBlockYSize = std::max(1, nBlockYSize);
    2090             : 
    2091             :                 // Check that blocks are effectively written in expected order.
    2092          23 :                 const int nBlockSizeBytes = nBlockXSize * nBlockYSize *
    2093          23 :                                             GDALGetDataTypeSizeBytes(eDataType);
    2094          23 :                 bool bGoOn = !bWarned;
    2095          23 :                 const int l_nBlocksPerRow = DIV_ROUND_UP(nCols, nBlockXSize);
    2096          23 :                 const int l_nBlocksPerColumn = DIV_ROUND_UP(nRows, nBlockYSize);
    2097          23 :                 int nBlockNo = 0;
    2098          52 :                 for (int i = 0; i < nBands && bGoOn; i++)
    2099             :                 {
    2100        1285 :                     for (int y = 0; y < l_nBlocksPerColumn && bGoOn; y++)
    2101             :                     {
    2102        2950 :                         for (int x = 0; x < l_nBlocksPerRow && bGoOn; x++)
    2103             :                         {
    2104             :                             const char *pszBlockOffset =
    2105        3388 :                                 poTIF_DS->GetRasterBand(i + 1)->GetMetadataItem(
    2106             :                                     CPLSPrintf("BLOCK_OFFSET_%d_%d", x, y),
    2107        1694 :                                     "TIFF");
    2108        1694 :                             if (pszBlockOffset)
    2109             :                             {
    2110        1694 :                                 GIntBig nOffset = CPLAtoGIntBig(pszBlockOffset);
    2111        1694 :                                 if (nOffset !=
    2112        1694 :                                     nSkipBytes + nBlockNo * nBlockSizeBytes)
    2113             :                                 {
    2114             :                                     // bWarned = true;
    2115           2 :                                     CPLError(CE_Warning, CPLE_AppDefined,
    2116             :                                              "%s has incompatible "
    2117             :                                              "characteristics with the ones "
    2118             :                                              "declared in the label.",
    2119             :                                              osQubeFile.c_str());
    2120           2 :                                     bGoOn = false;
    2121             :                                 }
    2122             :                             }
    2123        1694 :                             nBlockNo++;
    2124             :                         }
    2125             :                     }
    2126             :                 }
    2127             :             }
    2128             :         }
    2129             :     }
    2130             : 
    2131         267 :     poDS->eAccess = poOpenInfo->eAccess;
    2132             : 
    2133             :     /* -------------------------------------------------------------------- */
    2134             :     /*      Compute the line offset.                                        */
    2135             :     /* -------------------------------------------------------------------- */
    2136         267 :     int nLineOffset = 0;
    2137         267 :     int nPixelOffset = 0;
    2138         267 :     vsi_l_offset nBandOffset = 0;
    2139             : 
    2140         267 :     if (EQUAL(osFormat, "BandSequential"))
    2141             :     {
    2142         221 :         const int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
    2143         221 :         nPixelOffset = nItemSize;
    2144             :         try
    2145             :         {
    2146         221 :             nLineOffset = (CPLSM(nPixelOffset) * CPLSM(nCols)).v();
    2147             :         }
    2148           0 :         catch (const CPLSafeIntOverflow &)
    2149             :         {
    2150           0 :             return nullptr;
    2151             :         }
    2152         221 :         nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
    2153             : 
    2154         221 :         poDS->m_sLayout.osRawFilename = std::move(osQubeFile);
    2155         221 :         if (nBands > 1)
    2156          17 :             poDS->m_sLayout.eInterleaving = RawBinaryLayout::Interleaving::BSQ;
    2157         221 :         poDS->m_sLayout.eDataType = eDataType;
    2158         221 :         poDS->m_sLayout.bLittleEndianOrder = bIsLSB;
    2159         221 :         poDS->m_sLayout.nImageOffset = nSkipBytes;
    2160         221 :         poDS->m_sLayout.nPixelOffset = nPixelOffset;
    2161         221 :         poDS->m_sLayout.nLineOffset = nLineOffset;
    2162         221 :         poDS->m_sLayout.nBandOffset = static_cast<GIntBig>(nBandOffset);
    2163             :     }
    2164             :     /* else Tiled or external */
    2165             : 
    2166             :     /* -------------------------------------------------------------------- */
    2167             :     /*      Extract BandBin info.                                           */
    2168             :     /* -------------------------------------------------------------------- */
    2169         534 :     std::vector<std::string> aosBandNames;
    2170         534 :     std::vector<std::string> aosBandUnits;
    2171         534 :     std::vector<double> adfWavelengths;
    2172         534 :     std::vector<std::string> aosWavelengthsUnit;
    2173         534 :     std::vector<double> adfBandwidth;
    2174         534 :     std::vector<std::string> aosBandwidthUnit;
    2175         801 :     const auto oIsisCube = poDS->m_oJSonLabel.GetObj("IsisCube");
    2176         267 :     if (oIsisCube.GetType() == CPLJSONObject::Type::Object)
    2177             :     {
    2178        1017 :         for (const auto &oChildIsisCube : oIsisCube.GetChildren())
    2179             :         {
    2180         475 :             if (oChildIsisCube.GetType() == CPLJSONObject::Type::Object &&
    2181        2143 :                 oChildIsisCube.GetString("_type") == "group" &&
    2182        1124 :                 (oChildIsisCube.GetName() == "BandBin" ||
    2183         918 :                  oChildIsisCube.GetString("_container_name") == "BandBin"))
    2184             :             {
    2185         187 :                 for (const auto &child : oChildIsisCube.GetChildren())
    2186             :                 {
    2187         146 :                     if (CPLString(child.GetName()).ifind("name") !=
    2188             :                         std::string::npos)
    2189             :                     {
    2190             :                         // Use "name" in priority
    2191          15 :                         if (EQUAL(child.GetName().c_str(), "name"))
    2192             :                         {
    2193           5 :                             aosBandNames.clear();
    2194             :                         }
    2195          10 :                         else if (!aosBandNames.empty())
    2196             :                         {
    2197           3 :                             continue;
    2198             :                         }
    2199             : 
    2200          12 :                         if (child.GetType() == CPLJSONObject::Type::String &&
    2201             :                             nBands == 1)
    2202             :                         {
    2203           4 :                             aosBandNames.push_back(child.ToString());
    2204             :                         }
    2205           8 :                         else if (child.GetType() == CPLJSONObject::Type::Array)
    2206             :                         {
    2207          16 :                             auto oArray = child.ToArray();
    2208           8 :                             if (oArray.Size() == nBands)
    2209             :                             {
    2210          28 :                                 for (int i = 0; i < nBands; i++)
    2211             :                                 {
    2212          20 :                                     if (oArray[i].GetType() ==
    2213             :                                         CPLJSONObject::Type::String)
    2214             :                                     {
    2215          20 :                                         aosBandNames.push_back(
    2216          40 :                                             oArray[i].ToString());
    2217             :                                     }
    2218             :                                     else
    2219             :                                     {
    2220           0 :                                         aosBandNames.clear();
    2221           0 :                                         break;
    2222             :                                     }
    2223             :                                 }
    2224             :                             }
    2225             :                         }
    2226             :                     }
    2227         135 :                     else if (EQUAL(child.GetName().c_str(), "BandSuffixUnit") &&
    2228           4 :                              child.GetType() == CPLJSONObject::Type::Array)
    2229             :                     {
    2230           8 :                         auto oArray = child.ToArray();
    2231           4 :                         if (oArray.Size() == nBands)
    2232             :                         {
    2233          12 :                             for (int i = 0; i < nBands; i++)
    2234             :                             {
    2235           8 :                                 if (oArray[i].GetType() ==
    2236             :                                     CPLJSONObject::Type::String)
    2237             :                                 {
    2238           8 :                                     aosBandUnits.push_back(
    2239          16 :                                         oArray[i].ToString());
    2240             :                                 }
    2241             :                                 else
    2242             :                                 {
    2243           0 :                                     aosBandUnits.clear();
    2244           0 :                                     break;
    2245             :                                 }
    2246             :                             }
    2247             :                         }
    2248             :                     }
    2249         377 :                     else if (EQUAL(child.GetName().c_str(), "BandBinCenter") ||
    2250         250 :                              EQUAL(child.GetName().c_str(), "Center"))
    2251             :                     {
    2252          41 :                         GetValueAndUnits(child, adfWavelengths,
    2253             :                                          aosWavelengthsUnit, nBands);
    2254             :                     }
    2255          90 :                     else if (EQUAL(child.GetName().c_str(), "BandBinUnit") &&
    2256           4 :                              child.GetType() == CPLJSONObject::Type::String)
    2257             :                     {
    2258          12 :                         CPLString unit(child.ToString());
    2259           4 :                         if (STARTS_WITH_CI(unit, "micromet") ||
    2260           0 :                             EQUAL(unit, "um") ||
    2261           4 :                             STARTS_WITH_CI(unit, "nanomet") ||
    2262           0 :                             EQUAL(unit, "nm"))
    2263             :                         {
    2264           4 :                             aosWavelengthsUnit.push_back(child.ToString());
    2265             :                         }
    2266             :                     }
    2267          82 :                     else if (EQUAL(child.GetName().c_str(), "Width"))
    2268             :                     {
    2269           7 :                         GetValueAndUnits(child, adfBandwidth, aosBandwidthUnit,
    2270             :                                          nBands);
    2271             :                     }
    2272             :                 }
    2273             : 
    2274          41 :                 if (!adfWavelengths.empty() && aosWavelengthsUnit.size() == 1)
    2275             :                 {
    2276          11 :                     for (int i = 1; i < nBands; i++)
    2277             :                     {
    2278           4 :                         aosWavelengthsUnit.push_back(aosWavelengthsUnit[0]);
    2279             :                     }
    2280             :                 }
    2281          41 :                 if (!adfBandwidth.empty() && aosBandwidthUnit.size() == 1)
    2282             :                 {
    2283           7 :                     for (int i = 1; i < nBands; i++)
    2284             :                     {
    2285           2 :                         aosBandwidthUnit.push_back(aosBandwidthUnit[0]);
    2286             :                     }
    2287             :                 }
    2288             :             }
    2289             :         }
    2290             :     }
    2291             : 
    2292             : /* -------------------------------------------------------------------- */
    2293             : /*      Create band information objects.                                */
    2294             : /* -------------------------------------------------------------------- */
    2295             : #ifdef CPL_LSB
    2296         267 :     const bool bNativeOrder = bIsLSB;
    2297             : #else
    2298             :     const bool bNativeOrder = !bIsLSB;
    2299             : #endif
    2300             : 
    2301         586 :     for (int i = 0; i < nBands; i++)
    2302             :     {
    2303             :         GDALRasterBand *poBand;
    2304             : 
    2305         319 :         if (poDS->m_poExternalDS != nullptr)
    2306             :         {
    2307             :             auto poISISBand = std::make_unique<ISIS3WrapperRasterBand>(
    2308          21 :                 poDS->m_poExternalDS->GetRasterBand(i + 1));
    2309          42 :             poISISBand->SetMaskBand(
    2310          42 :                 std::make_unique<ISISMaskBand>(poISISBand.get()));
    2311          21 :             poDS->SetBand(i + 1, std::move(poISISBand));
    2312          21 :             poBand = poDS->GetRasterBand(i + 1);
    2313             :         }
    2314         298 :         else if (poDS->m_bIsTiled)
    2315             :         {
    2316             :             auto poISISBand = std::make_unique<ISISTiledBand>(
    2317          41 :                 poDS.get(), poDS->m_fpImage, i + 1, eDataType, tileSizeX,
    2318          82 :                 tileSizeY, nSkipBytes, 0, 0, bNativeOrder);
    2319          41 :             if (!poISISBand->IsValid())
    2320             :             {
    2321           0 :                 return nullptr;
    2322             :             }
    2323          82 :             poISISBand->SetMaskBand(
    2324          82 :                 std::make_unique<ISISMaskBand>(poISISBand.get()));
    2325          41 :             poDS->SetBand(i + 1, std::move(poISISBand));
    2326          41 :             poBand = poDS->GetRasterBand(i + 1);
    2327             :         }
    2328             :         else
    2329             :         {
    2330             :             auto poISISBand = std::make_unique<ISIS3RawRasterBand>(
    2331         257 :                 poDS.get(), i + 1, poDS->m_fpImage,
    2332         257 :                 nSkipBytes + nBandOffset * i, nPixelOffset, nLineOffset,
    2333         257 :                 eDataType, bNativeOrder);
    2334         257 :             if (!poISISBand->IsValid())
    2335             :             {
    2336           0 :                 return nullptr;
    2337             :             }
    2338         514 :             poISISBand->SetMaskBand(
    2339         514 :                 std::make_unique<ISISMaskBand>(poISISBand.get()));
    2340         257 :             poDS->SetBand(i + 1, std::move(poISISBand));
    2341         257 :             poBand = poDS->GetRasterBand(i + 1);
    2342             :         }
    2343             : 
    2344         319 :         if (i < static_cast<int>(aosBandNames.size()))
    2345             :         {
    2346          17 :             poBand->SetDescription(aosBandNames[i].c_str());
    2347             :         }
    2348         368 :         if (i < static_cast<int>(adfWavelengths.size()) &&
    2349          49 :             i < static_cast<int>(aosWavelengthsUnit.size()))
    2350             :         {
    2351          11 :             poBand->SetMetadataItem("WAVELENGTH",
    2352          11 :                                     CPLSPrintf("%f", adfWavelengths[i]));
    2353          11 :             poBand->SetMetadataItem("WAVELENGTH_UNIT",
    2354          11 :                                     aosWavelengthsUnit[i].c_str());
    2355          18 :             if (i < static_cast<int>(adfBandwidth.size()) &&
    2356           7 :                 i < static_cast<int>(aosBandwidthUnit.size()))
    2357             :             {
    2358           7 :                 poBand->SetMetadataItem("BANDWIDTH",
    2359           7 :                                         CPLSPrintf("%f", adfBandwidth[i]));
    2360           7 :                 poBand->SetMetadataItem("BANDWIDTH_UNIT",
    2361           7 :                                         aosBandwidthUnit[i].c_str());
    2362             :             }
    2363             :         }
    2364         319 :         if (i < static_cast<int>(aosBandUnits.size()))
    2365             :         {
    2366           8 :             poBand->SetUnitType(aosBandUnits[i].c_str());
    2367             :         }
    2368             : 
    2369         319 :         poBand->SetNoDataValue(dfNoData);
    2370             : 
    2371             :         // Set offset/scale values.
    2372             :         const double dfOffset =
    2373         319 :             CPLAtofM(poDS->GetKeyword("IsisCube.Core.Pixels.Base", "0.0"));
    2374         319 :         const double dfScale = CPLAtofM(
    2375             :             poDS->GetKeyword("IsisCube.Core.Pixels.Multiplier", "1.0"));
    2376         319 :         if (dfOffset != 0.0 || dfScale != 1.0)
    2377             :         {
    2378          60 :             poBand->SetOffset(dfOffset);
    2379          60 :             poBand->SetScale(dfScale);
    2380             :         }
    2381             :     }
    2382             : 
    2383             :     /* -------------------------------------------------------------------- */
    2384             :     /*      Check for a .prj file. For ISIS3 I would like to keep this in   */
    2385             :     /* -------------------------------------------------------------------- */
    2386         534 :     const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
    2387         534 :     const CPLString osName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
    2388         534 :     const std::string osPrjFile = CPLFormCIFilenameSafe(osPath, osName, "prj");
    2389             : 
    2390         267 :     VSILFILE *fp = VSIFOpenL(osPrjFile.c_str(), "r");
    2391         267 :     if (fp != nullptr)
    2392             :     {
    2393           0 :         VSIFCloseL(fp);
    2394             : 
    2395           0 :         char **papszLines = CSLLoad(osPrjFile.c_str());
    2396             : 
    2397           0 :         OGRSpatialReference oSRS2;
    2398           0 :         if (oSRS2.importFromESRI(papszLines) == OGRERR_NONE)
    2399             :         {
    2400           0 :             poDS->m_aosAdditionalFiles.AddString(osPrjFile.c_str());
    2401           0 :             poDS->m_oSRS = std::move(oSRS2);
    2402           0 :             poDS->m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2403             :         }
    2404             : 
    2405           0 :         CSLDestroy(papszLines);
    2406             :     }
    2407             : 
    2408         267 :     if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0)
    2409             :     {
    2410         104 :         poDS->m_bGotTransform = true;
    2411         104 :         poDS->m_gt.xorig = dfULXMap;
    2412         104 :         poDS->m_gt.xscale = dfXDim;
    2413         104 :         poDS->m_gt.xrot = 0.0;
    2414         104 :         poDS->m_gt.yorig = dfULYMap;
    2415         104 :         poDS->m_gt.yrot = 0.0;
    2416         104 :         poDS->m_gt.yscale = dfYDim;
    2417             :     }
    2418             : 
    2419         267 :     if (!poDS->m_bGotTransform)
    2420             :     {
    2421         326 :         poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
    2422         326 :             poOpenInfo->pszFilename, "cbw", poDS->m_gt.data()));
    2423         163 :         if (poDS->m_bGotTransform)
    2424             :         {
    2425           0 :             poDS->m_aosAdditionalFiles.AddString(
    2426           0 :                 CPLResetExtensionSafe(poOpenInfo->pszFilename, "cbw").c_str());
    2427             :         }
    2428             :     }
    2429             : 
    2430         267 :     if (!poDS->m_bGotTransform)
    2431             :     {
    2432         326 :         poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
    2433         326 :             poOpenInfo->pszFilename, "wld", poDS->m_gt.data()));
    2434         163 :         if (poDS->m_bGotTransform)
    2435             :         {
    2436           0 :             poDS->m_aosAdditionalFiles.AddString(
    2437           0 :                 CPLResetExtensionSafe(poOpenInfo->pszFilename, "wld").c_str());
    2438             :         }
    2439             :     }
    2440             : 
    2441             :     /* -------------------------------------------------------------------- */
    2442             :     /*      Initialize any PAM information.                                 */
    2443             :     /* -------------------------------------------------------------------- */
    2444         267 :     poDS->SetDescription(poOpenInfo->pszFilename);
    2445         267 :     poDS->TryLoadXML();
    2446             : 
    2447             :     /* -------------------------------------------------------------------- */
    2448             :     /*      Check for overviews.                                            */
    2449             :     /* -------------------------------------------------------------------- */
    2450         267 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
    2451             : 
    2452         267 :     return poDS.release();
    2453             : }
    2454             : 
    2455             : /************************************************************************/
    2456             : /*                             GetKeyword()                             */
    2457             : /************************************************************************/
    2458             : 
    2459        3750 : const char *ISIS3Dataset::GetKeyword(const char *pszPath,
    2460             :                                      const char *pszDefault)
    2461             : 
    2462             : {
    2463        3750 :     return m_oKeywords.GetKeyword(pszPath, pszDefault);
    2464             : }
    2465             : 
    2466             : /************************************************************************/
    2467             : /*                              FixLong()                               */
    2468             : /************************************************************************/
    2469             : 
    2470         259 : double ISIS3Dataset::FixLong(double dfLong)
    2471             : {
    2472         259 :     if (m_osLongitudeDirection == "PositiveWest")
    2473           4 :         dfLong = -dfLong;
    2474         259 :     if (m_bForce360 && dfLong < 0)
    2475           2 :         dfLong += 360.0;
    2476         259 :     return dfLong;
    2477             : }
    2478             : 
    2479             : /************************************************************************/
    2480             : /*                             BuildLabel()                             */
    2481             : /************************************************************************/
    2482             : 
    2483         140 : void ISIS3Dataset::BuildLabel()
    2484             : {
    2485         280 :     CPLJSONObject oLabel = m_oSrcJSonLabel;
    2486         140 :     if (!oLabel.IsValid())
    2487             :     {
    2488         113 :         oLabel = CPLJSONObject();
    2489             :     }
    2490             :     // If we have a source label, then edit it directly
    2491         420 :     CPLJSONObject oIsisCube = GetOrCreateJSONObject(oLabel, "IsisCube");
    2492         140 :     oIsisCube.Set("_type", "object");
    2493             : 
    2494         140 :     if (!m_osComment.empty())
    2495           1 :         oIsisCube.Set("_comment", m_osComment);
    2496             : 
    2497         420 :     CPLJSONObject oCore = GetOrCreateJSONObject(oIsisCube, "Core");
    2498         140 :     if (oCore.GetType() != CPLJSONObject::Type::Object)
    2499             :     {
    2500           0 :         oIsisCube.Delete("Core");
    2501           0 :         oCore = CPLJSONObject();
    2502           0 :         oIsisCube.Add("Core", oCore);
    2503             :     }
    2504         140 :     oCore.Set("_type", "object");
    2505             : 
    2506         140 :     if (!m_osExternalFilename.empty())
    2507             :     {
    2508          31 :         if (m_poExternalDS && m_bGeoTIFFAsRegularExternal)
    2509             :         {
    2510           8 :             if (!m_bGeoTIFFInitDone)
    2511             :             {
    2512             :                 cpl::down_cast<ISIS3WrapperRasterBand *>(GetRasterBand(1))
    2513           1 :                     ->InitFile();
    2514             :             }
    2515             : 
    2516             :             const char *pszOffset =
    2517           8 :                 m_poExternalDS->GetRasterBand(1)->GetMetadataItem(
    2518           8 :                     "BLOCK_OFFSET_0_0", "TIFF");
    2519           8 :             if (pszOffset)
    2520             :             {
    2521           8 :                 oCore.Set("StartByte", 1 + atoi(pszOffset));
    2522             :             }
    2523             :             else
    2524             :             {
    2525             :                 // Shouldn't happen normally
    2526           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    2527             :                          "Missing BLOCK_OFFSET_0_0");
    2528           0 :                 m_bGeoTIFFAsRegularExternal = false;
    2529           0 :                 oCore.Set("StartByte", 1);
    2530           8 :             }
    2531             :         }
    2532             :         else
    2533             :         {
    2534          23 :             oCore.Set("StartByte", 1);
    2535             :         }
    2536          31 :         if (!m_osExternalFilename.empty())
    2537             :         {
    2538             :             const CPLString osExternalFilename =
    2539          31 :                 CPLGetFilename(m_osExternalFilename);
    2540          31 :             oCore.Set("^Core", osExternalFilename);
    2541             :         }
    2542             :     }
    2543             :     else
    2544             :     {
    2545         109 :         oCore.Set("StartByte", pszSTARTBYTE_PLACEHOLDER);
    2546         109 :         oCore.Delete("^Core");
    2547             :     }
    2548             : 
    2549         140 :     if (m_poExternalDS && !m_bGeoTIFFAsRegularExternal)
    2550             :     {
    2551          10 :         oCore.Set("Format", "GeoTIFF");
    2552          10 :         oCore.Delete("TileSamples");
    2553          10 :         oCore.Delete("TileLines");
    2554             :     }
    2555         130 :     else if (m_bIsTiled)
    2556             :     {
    2557           9 :         oCore.Set("Format", "Tile");
    2558           9 :         int nBlockXSize = 1, nBlockYSize = 1;
    2559           9 :         GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    2560           9 :         oCore.Set("TileSamples", nBlockXSize);
    2561           9 :         oCore.Set("TileLines", nBlockYSize);
    2562             :     }
    2563             :     else
    2564             :     {
    2565         121 :         oCore.Set("Format", "BandSequential");
    2566         121 :         oCore.Delete("TileSamples");
    2567         121 :         oCore.Delete("TileLines");
    2568             :     }
    2569             : 
    2570         420 :     CPLJSONObject oDimensions = GetOrCreateJSONObject(oCore, "Dimensions");
    2571         140 :     oDimensions.Set("_type", "group");
    2572         140 :     oDimensions.Set("Samples", nRasterXSize);
    2573         140 :     oDimensions.Set("Lines", nRasterYSize);
    2574         140 :     oDimensions.Set("Bands", nBands);
    2575             : 
    2576         420 :     CPLJSONObject oPixels = GetOrCreateJSONObject(oCore, "Pixels");
    2577         140 :     oPixels.Set("_type", "group");
    2578         140 :     const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
    2579         175 :     oPixels.Set("Type", (eDT == GDT_Byte)      ? "UnsignedByte"
    2580          60 :                         : (eDT == GDT_UInt16)  ? "UnsignedWord"
    2581          42 :                         : (eDT == GDT_Int16)   ? "SignedWord"
    2582          25 :                         : (eDT == GDT_Float32) ? "Real"
    2583           8 :                         : (eDT == GDT_Float64) ? "Double"
    2584             :                                                : "Real");
    2585             : 
    2586         140 :     oPixels.Set("ByteOrder", "Lsb");
    2587         140 :     oPixels.Set("Base", GetRasterBand(1)->GetOffset());
    2588         140 :     oPixels.Set("Multiplier", GetRasterBand(1)->GetScale());
    2589             : 
    2590         140 :     const OGRSpatialReference &oSRS = m_oSRS;
    2591             : 
    2592         140 :     if (!m_bUseSrcMapping)
    2593             :     {
    2594         138 :         oIsisCube.Delete("Mapping");
    2595             :     }
    2596             : 
    2597         420 :     CPLJSONObject oMapping = GetOrCreateJSONObject(oIsisCube, "Mapping");
    2598         142 :     if (m_bUseSrcMapping && oMapping.IsValid() &&
    2599           2 :         oMapping.GetType() == CPLJSONObject::Type::Object)
    2600             :     {
    2601           2 :         if (!m_osTargetName.empty())
    2602           1 :             oMapping.Set("TargetName", m_osTargetName);
    2603           2 :         if (!m_osLatitudeType.empty())
    2604           1 :             oMapping.Set("LatitudeType", m_osLatitudeType);
    2605           2 :         if (!m_osLongitudeDirection.empty())
    2606           1 :             oMapping.Set("LongitudeDirection", m_osLongitudeDirection);
    2607             :     }
    2608         138 :     else if (!m_bUseSrcMapping && !m_oSRS.IsEmpty())
    2609             :     {
    2610          65 :         oMapping.Add("_type", "group");
    2611             : 
    2612          65 :         if (oSRS.IsProjected() || oSRS.IsGeographic())
    2613             :         {
    2614          65 :             const char *pszDatum = oSRS.GetAttrValue("DATUM");
    2615         130 :             CPLString osTargetName(m_osTargetName);
    2616          65 :             if (osTargetName.empty())
    2617             :             {
    2618          64 :                 if (pszDatum && STARTS_WITH(pszDatum, "D_"))
    2619             :                 {
    2620          24 :                     osTargetName = pszDatum + 2;
    2621             :                 }
    2622          40 :                 else if (pszDatum)
    2623             :                 {
    2624          40 :                     osTargetName = pszDatum;
    2625             :                 }
    2626             :             }
    2627          65 :             if (!osTargetName.empty())
    2628          65 :                 oMapping.Add("TargetName", osTargetName);
    2629             : 
    2630          65 :             oMapping.Add("EquatorialRadius/value", oSRS.GetSemiMajor());
    2631          65 :             oMapping.Add("EquatorialRadius/unit", "meters");
    2632          65 :             oMapping.Add("PolarRadius/value", oSRS.GetSemiMinor());
    2633          65 :             oMapping.Add("PolarRadius/unit", "meters");
    2634             : 
    2635          65 :             if (!m_osLatitudeType.empty())
    2636           1 :                 oMapping.Add("LatitudeType", m_osLatitudeType);
    2637             :             else
    2638          64 :                 oMapping.Add("LatitudeType", "Planetocentric");
    2639             : 
    2640          65 :             if (!m_osLongitudeDirection.empty())
    2641           1 :                 oMapping.Add("LongitudeDirection", m_osLongitudeDirection);
    2642             :             else
    2643          64 :                 oMapping.Add("LongitudeDirection", "PositiveEast");
    2644             : 
    2645          65 :             double adfX[4] = {0};
    2646          65 :             double adfY[4] = {0};
    2647          65 :             bool bLongLatCorners = false;
    2648          65 :             if (m_bGotTransform)
    2649             :             {
    2650         285 :                 for (int i = 0; i < 4; i++)
    2651             :                 {
    2652         228 :                     adfX[i] = m_gt.xorig + (i % 2) * nRasterXSize * m_gt.xscale;
    2653         228 :                     adfY[i] = m_gt.yorig + ((i == 0 || i == 3) ? 0 : 1) *
    2654         228 :                                                nRasterYSize * m_gt.yscale;
    2655             :                 }
    2656          57 :                 if (oSRS.IsGeographic())
    2657             :                 {
    2658          34 :                     bLongLatCorners = true;
    2659             :                 }
    2660             :                 else
    2661             :                 {
    2662          23 :                     OGRSpatialReference *poSRSLongLat = oSRS.CloneGeogCS();
    2663          23 :                     if (poSRSLongLat)
    2664             :                     {
    2665          23 :                         poSRSLongLat->SetAxisMappingStrategy(
    2666             :                             OAMS_TRADITIONAL_GIS_ORDER);
    2667             :                         OGRCoordinateTransformation *poCT =
    2668          23 :                             OGRCreateCoordinateTransformation(&oSRS,
    2669             :                                                               poSRSLongLat);
    2670          23 :                         if (poCT)
    2671             :                         {
    2672          23 :                             if (poCT->Transform(4, adfX, adfY))
    2673             :                             {
    2674          23 :                                 bLongLatCorners = true;
    2675             :                             }
    2676          23 :                             delete poCT;
    2677             :                         }
    2678          23 :                         delete poSRSLongLat;
    2679             :                     }
    2680             :                 }
    2681             :             }
    2682          65 :             if (bLongLatCorners)
    2683             :             {
    2684         285 :                 for (int i = 0; i < 4; i++)
    2685             :                 {
    2686         228 :                     adfX[i] = FixLong(adfX[i]);
    2687             :                 }
    2688             :             }
    2689             : 
    2690          65 :             if (bLongLatCorners &&
    2691          57 :                 (m_bForce360 || adfX[0] < -180.0 || adfX[3] > 180.0))
    2692             :             {
    2693           1 :                 oMapping.Add("LongitudeDomain", 360);
    2694             :             }
    2695             :             else
    2696             :             {
    2697          64 :                 oMapping.Add("LongitudeDomain", 180);
    2698             :             }
    2699             : 
    2700          65 :             if (m_bWriteBoundingDegrees && !m_osBoundingDegrees.empty())
    2701             :             {
    2702             :                 char **papszTokens =
    2703           1 :                     CSLTokenizeString2(m_osBoundingDegrees, ",", 0);
    2704           1 :                 if (CSLCount(papszTokens) == 4)
    2705             :                 {
    2706           1 :                     oMapping.Add("MinimumLatitude", CPLAtof(papszTokens[1]));
    2707           1 :                     oMapping.Add("MinimumLongitude", CPLAtof(papszTokens[0]));
    2708           1 :                     oMapping.Add("MaximumLatitude", CPLAtof(papszTokens[3]));
    2709           1 :                     oMapping.Add("MaximumLongitude", CPLAtof(papszTokens[2]));
    2710             :                 }
    2711           1 :                 CSLDestroy(papszTokens);
    2712             :             }
    2713          64 :             else if (m_bWriteBoundingDegrees && bLongLatCorners)
    2714             :             {
    2715          55 :                 oMapping.Add("MinimumLatitude",
    2716             :                              std::min(std::min(adfY[0], adfY[1]),
    2717          55 :                                       std::min(adfY[2], adfY[3])));
    2718          55 :                 oMapping.Add("MinimumLongitude",
    2719             :                              std::min(std::min(adfX[0], adfX[1]),
    2720          55 :                                       std::min(adfX[2], adfX[3])));
    2721          55 :                 oMapping.Add("MaximumLatitude",
    2722             :                              std::max(std::max(adfY[0], adfY[1]),
    2723          55 :                                       std::max(adfY[2], adfY[3])));
    2724          55 :                 oMapping.Add("MaximumLongitude",
    2725             :                              std::max(std::max(adfX[0], adfX[1]),
    2726          55 :                                       std::max(adfX[2], adfX[3])));
    2727             :             }
    2728             : 
    2729          65 :             const char *pszProjection = oSRS.GetAttrValue("PROJECTION");
    2730          65 :             if (pszProjection == nullptr)
    2731             :             {
    2732          34 :                 oMapping.Add("ProjectionName", "SimpleCylindrical");
    2733          34 :                 oMapping.Add("CenterLongitude", 0.0);
    2734          34 :                 oMapping.Add("CenterLatitude", 0.0);
    2735          34 :                 oMapping.Add("CenterLatitudeRadius", oSRS.GetSemiMajor());
    2736             :             }
    2737          31 :             else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
    2738             :             {
    2739          14 :                 oMapping.Add("ProjectionName", "Equirectangular");
    2740          14 :                 if (oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
    2741             :                 {
    2742           1 :                     CPLError(CE_Warning, CPLE_NotSupported,
    2743             :                              "Ignoring %s. Only 0 value supported",
    2744             :                              SRS_PP_LATITUDE_OF_ORIGIN);
    2745             :                 }
    2746          14 :                 oMapping.Add("CenterLongitude",
    2747             :                              FixLong(oSRS.GetNormProjParm(
    2748             :                                  SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2749             :                 const double dfCenterLat =
    2750          14 :                     oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
    2751          14 :                 oMapping.Add("CenterLatitude", dfCenterLat);
    2752             : 
    2753             :                 // in radians
    2754          14 :                 const double radLat = dfCenterLat * M_PI / 180;
    2755          14 :                 const double semi_major = oSRS.GetSemiMajor();
    2756          14 :                 const double semi_minor = oSRS.GetSemiMinor();
    2757             :                 const double localRadius =
    2758          14 :                     semi_major * semi_minor /
    2759          14 :                     sqrt(pow(semi_minor * cos(radLat), 2) +
    2760          14 :                          pow(semi_major * sin(radLat), 2));
    2761          14 :                 oMapping.Add("CenterLatitudeRadius", localRadius);
    2762             :             }
    2763             : 
    2764          17 :             else if (EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC))
    2765             :             {
    2766           1 :                 oMapping.Add("ProjectionName", "Orthographic");
    2767           1 :                 oMapping.Add("CenterLongitude",
    2768             :                              FixLong(oSRS.GetNormProjParm(
    2769             :                                  SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2770           1 :                 oMapping.Add(
    2771             :                     "CenterLatitude",
    2772             :                     oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
    2773             :             }
    2774             : 
    2775          16 :             else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
    2776             :             {
    2777           1 :                 oMapping.Add("ProjectionName", "Sinusoidal");
    2778           1 :                 oMapping.Add("CenterLongitude",
    2779             :                              FixLong(oSRS.GetNormProjParm(
    2780             :                                  SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
    2781             :             }
    2782             : 
    2783          15 :             else if (EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
    2784             :             {
    2785           1 :                 oMapping.Add("ProjectionName", "Mercator");
    2786           1 :                 oMapping.Add("CenterLongitude",
    2787             :                              FixLong(oSRS.GetNormProjParm(
    2788             :                                  SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2789           1 :                 oMapping.Add(
    2790             :                     "CenterLatitude",
    2791             :                     oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
    2792           1 :                 oMapping.Add("scaleFactor",
    2793             :                              oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2794             :             }
    2795             : 
    2796          14 :             else if (EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC))
    2797             :             {
    2798           1 :                 oMapping.Add("ProjectionName", "PolarStereographic");
    2799           1 :                 oMapping.Add("CenterLongitude",
    2800             :                              FixLong(oSRS.GetNormProjParm(
    2801             :                                  SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2802           1 :                 oMapping.Add(
    2803             :                     "CenterLatitude",
    2804             :                     oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
    2805           1 :                 oMapping.Add("scaleFactor",
    2806             :                              oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2807             :             }
    2808             : 
    2809          13 :             else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
    2810             :             {
    2811          11 :                 oMapping.Add("ProjectionName", "TransverseMercator");
    2812          11 :                 oMapping.Add("CenterLongitude",
    2813             :                              FixLong(oSRS.GetNormProjParm(
    2814             :                                  SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2815          11 :                 oMapping.Add(
    2816             :                     "CenterLatitude",
    2817             :                     oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
    2818          11 :                 oMapping.Add("scaleFactor",
    2819             :                              oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
    2820             :             }
    2821             : 
    2822           2 :             else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
    2823             :             {
    2824           1 :                 oMapping.Add("ProjectionName", "LambertConformal");
    2825           1 :                 oMapping.Add("CenterLongitude",
    2826             :                              FixLong(oSRS.GetNormProjParm(
    2827             :                                  SRS_PP_CENTRAL_MERIDIAN, 0.0)));
    2828           1 :                 oMapping.Add(
    2829             :                     "CenterLatitude",
    2830             :                     oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
    2831           1 :                 oMapping.Add(
    2832             :                     "FirstStandardParallel",
    2833             :                     oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0));
    2834           1 :                 oMapping.Add(
    2835             :                     "SecondStandardParallel",
    2836             :                     oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0));
    2837             :             }
    2838             : 
    2839           1 :             else if (EQUAL(pszProjection,
    2840             :                            "Vertical Perspective"))  // PROJ 7 required
    2841             :             {
    2842           0 :                 oMapping.Add("ProjectionName", "PointPerspective");
    2843           0 :                 oMapping.Add("CenterLongitude",
    2844             :                              FixLong(oSRS.GetNormProjParm(
    2845             :                                  "Longitude of topocentric origin", 0.0)));
    2846           0 :                 oMapping.Add("CenterLatitude",
    2847             :                              oSRS.GetNormProjParm(
    2848             :                                  "Latitude of topocentric origin", 0.0));
    2849             :                 // ISIS3 value is the distance from center of ellipsoid, in km
    2850           0 :                 oMapping.Add("Distance",
    2851           0 :                              (oSRS.GetNormProjParm("Viewpoint height", 0.0) +
    2852           0 :                               oSRS.GetSemiMajor()) /
    2853             :                                  1000.0);
    2854             :             }
    2855             : 
    2856           1 :             else if (EQUAL(pszProjection, "custom_proj4"))
    2857             :             {
    2858             :                 const char *pszProj4 =
    2859           1 :                     oSRS.GetExtension("PROJCS", "PROJ4", nullptr);
    2860           1 :                 if (pszProj4 && strstr(pszProj4, "+proj=ob_tran") &&
    2861           1 :                     strstr(pszProj4, "+o_proj=eqc"))
    2862             :                 {
    2863             :                     const auto FetchParam =
    2864           3 :                         [](const char *pszProj4Str, const char *pszKey)
    2865             :                     {
    2866           6 :                         CPLString needle;
    2867           3 :                         needle.Printf("+%s=", pszKey);
    2868             :                         const char *pszVal =
    2869           3 :                             strstr(pszProj4Str, needle.c_str());
    2870           3 :                         if (pszVal)
    2871           3 :                             return CPLAtof(pszVal + needle.size());
    2872           0 :                         return 0.0;
    2873             :                     };
    2874             : 
    2875           1 :                     double dfLonP = FetchParam(pszProj4, "o_lon_p");
    2876           1 :                     double dfLatP = FetchParam(pszProj4, "o_lat_p");
    2877           1 :                     double dfLon0 = FetchParam(pszProj4, "lon_0");
    2878           1 :                     double dfPoleRotation = -dfLonP;
    2879           1 :                     double dfPoleLatitude = 180 - dfLatP;
    2880           1 :                     double dfPoleLongitude = dfLon0;
    2881           1 :                     oMapping.Add("ProjectionName", "ObliqueCylindrical");
    2882           1 :                     oMapping.Add("PoleLatitude", dfPoleLatitude);
    2883           1 :                     oMapping.Add("PoleLongitude", FixLong(dfPoleLongitude));
    2884           1 :                     oMapping.Add("PoleRotation", dfPoleRotation);
    2885             :                 }
    2886             :                 else
    2887             :                 {
    2888           0 :                     CPLError(CE_Warning, CPLE_NotSupported,
    2889             :                              "Projection %s not supported", pszProjection);
    2890             :                 }
    2891             :             }
    2892             :             else
    2893             :             {
    2894           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    2895             :                          "Projection %s not supported", pszProjection);
    2896             :             }
    2897             : 
    2898          65 :             if (oMapping["ProjectionName"].IsValid())
    2899             :             {
    2900          65 :                 if (oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) != 0.0)
    2901             :                 {
    2902           9 :                     CPLError(CE_Warning, CPLE_NotSupported,
    2903             :                              "Ignoring %s. Only 0 value supported",
    2904             :                              SRS_PP_FALSE_EASTING);
    2905             :                 }
    2906          65 :                 if (oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) != 0.0)
    2907             :                 {
    2908           1 :                     CPLError(CE_Warning, CPLE_AppDefined,
    2909             :                              "Ignoring %s. Only 0 value supported",
    2910             :                              SRS_PP_FALSE_NORTHING);
    2911             :                 }
    2912             :             }
    2913             :         }
    2914             :         else
    2915             :         {
    2916           0 :             CPLError(CE_Warning, CPLE_NotSupported, "SRS not supported");
    2917             :         }
    2918             :     }
    2919             : 
    2920         140 :     if (!m_bUseSrcMapping && m_bGotTransform)
    2921             :     {
    2922          57 :         oMapping.Add("_type", "group");
    2923             : 
    2924          57 :         const double dfDegToMeter = oSRS.GetSemiMajor() * M_PI / 180.0;
    2925          57 :         if (!m_oSRS.IsEmpty() && oSRS.IsProjected())
    2926             :         {
    2927          23 :             const double dfLinearUnits = oSRS.GetLinearUnits();
    2928             :             // Maybe we should deal differently with non meter units ?
    2929          23 :             const double dfRes = m_gt.xscale * dfLinearUnits;
    2930          23 :             const double dfScale = dfDegToMeter / dfRes;
    2931          23 :             oMapping.Add("UpperLeftCornerX", m_gt.xorig);
    2932          23 :             oMapping.Add("UpperLeftCornerY", m_gt.yorig);
    2933          23 :             oMapping.Add("PixelResolution/value", dfRes);
    2934          23 :             oMapping.Add("PixelResolution/unit", "meters/pixel");
    2935          23 :             oMapping.Add("Scale/value", dfScale);
    2936          23 :             oMapping.Add("Scale/unit", "pixels/degree");
    2937             :         }
    2938          34 :         else if (!m_oSRS.IsEmpty() && oSRS.IsGeographic())
    2939             :         {
    2940          34 :             const double dfScale = 1.0 / m_gt.xscale;
    2941          34 :             const double dfRes = m_gt.xscale * dfDegToMeter;
    2942          34 :             oMapping.Add("UpperLeftCornerX", m_gt.xorig * dfDegToMeter);
    2943          34 :             oMapping.Add("UpperLeftCornerY", m_gt.yorig * dfDegToMeter);
    2944          34 :             oMapping.Add("PixelResolution/value", dfRes);
    2945          34 :             oMapping.Add("PixelResolution/unit", "meters/pixel");
    2946          34 :             oMapping.Add("Scale/value", dfScale);
    2947          34 :             oMapping.Add("Scale/unit", "pixels/degree");
    2948             :         }
    2949             :         else
    2950             :         {
    2951           0 :             oMapping.Add("UpperLeftCornerX", m_gt.xorig);
    2952           0 :             oMapping.Add("UpperLeftCornerY", m_gt.yorig);
    2953           0 :             oMapping.Add("PixelResolution", m_gt.xscale);
    2954             :         }
    2955             :     }
    2956             : 
    2957         420 :     CPLJSONObject oLabelLabel = GetOrCreateJSONObject(oLabel, "Label");
    2958         140 :     oLabelLabel.Set("_type", "object");
    2959         140 :     oLabelLabel.Set("Bytes", pszLABEL_BYTES_PLACEHOLDER);
    2960             : 
    2961             :     // Deal with History object
    2962         140 :     BuildHistory();
    2963             : 
    2964         140 :     oLabel.Delete("History_IsisCube");
    2965         140 :     if (!m_osHistory.empty())
    2966             :     {
    2967         138 :         CPLJSONObject oHistory;
    2968         138 :         oHistory.Add("_type", "object");
    2969         138 :         oHistory.Add("_container_name", "History");
    2970         138 :         oHistory.Add("Name", "IsisCube");
    2971         138 :         if (m_osExternalFilename.empty())
    2972         108 :             oHistory.Add("StartByte", pszHISTORY_STARTBYTE_PLACEHOLDER);
    2973             :         else
    2974          30 :             oHistory.Add("StartByte", 1);
    2975         138 :         oHistory.Add("Bytes", static_cast<GIntBig>(m_osHistory.size()));
    2976         138 :         if (!m_osExternalFilename.empty())
    2977             :         {
    2978          30 :             CPLString osFilename(CPLGetBasenameSafe(GetDescription()));
    2979          30 :             osFilename += ".History.IsisCube";
    2980          30 :             oHistory.Add("^History", osFilename);
    2981             :         }
    2982         138 :         oLabel.Add("History_IsisCube", oHistory);
    2983             :     }
    2984             : 
    2985             :     // Deal with other objects that have StartByte & Bytes
    2986         140 :     m_aoNonPixelSections.clear();
    2987         140 :     if (m_oSrcJSonLabel.IsValid())
    2988             :     {
    2989          54 :         CPLString osLabelSrcFilename;
    2990          81 :         CPLJSONObject oFilename = oLabel["_filename"];
    2991          27 :         if (oFilename.GetType() == CPLJSONObject::Type::String)
    2992             :         {
    2993          22 :             osLabelSrcFilename = oFilename.ToString();
    2994             :         }
    2995             : 
    2996         148 :         for (CPLJSONObject &oObj : oLabel.GetChildren())
    2997             :         {
    2998         121 :             CPLString osKey = oObj.GetName();
    2999         121 :             if (osKey == "History_IsisCube")
    3000             :             {
    3001          25 :                 continue;
    3002             :             }
    3003             : 
    3004         192 :             CPLJSONObject oBytes = oObj.GetObj("Bytes");
    3005         109 :             if (oBytes.GetType() != CPLJSONObject::Type::Integer ||
    3006          13 :                 oBytes.ToInteger() <= 0)
    3007             :             {
    3008          83 :                 continue;
    3009             :             }
    3010             : 
    3011          26 :             CPLJSONObject oStartByte = oObj.GetObj("StartByte");
    3012          26 :             if (oStartByte.GetType() != CPLJSONObject::Type::Integer ||
    3013          13 :                 oStartByte.ToInteger() <= 0)
    3014             :             {
    3015           0 :                 continue;
    3016             :             }
    3017             : 
    3018          13 :             if (osLabelSrcFilename.empty())
    3019             :             {
    3020           0 :                 CPLError(CE_Warning, CPLE_AppDefined,
    3021             :                          "Cannot find _filename attribute in "
    3022             :                          "source ISIS3 metadata. Removing object "
    3023             :                          "%s from the label.",
    3024             :                          osKey.c_str());
    3025           0 :                 oLabel.Delete(osKey);
    3026           0 :                 continue;
    3027             :             }
    3028             : 
    3029          13 :             NonPixelSection oSection;
    3030          13 :             oSection.osSrcFilename = osLabelSrcFilename;
    3031          13 :             oSection.nSrcOffset =
    3032          13 :                 static_cast<vsi_l_offset>(oObj.GetInteger("StartByte")) - 1U;
    3033          13 :             oSection.nSize =
    3034          13 :                 static_cast<vsi_l_offset>(oObj.GetInteger("Bytes"));
    3035             : 
    3036          13 :             CPLString osName;
    3037          26 :             CPLJSONObject oName = oObj.GetObj("Name");
    3038          13 :             if (oName.GetType() == CPLJSONObject::Type::String)
    3039             :             {
    3040          13 :                 osName = oName.ToString();
    3041             :             }
    3042             : 
    3043          13 :             CPLString osContainerName(osKey);
    3044          26 :             CPLJSONObject oContainerName = oObj.GetObj("_container_name");
    3045          13 :             if (oContainerName.GetType() == CPLJSONObject::Type::String)
    3046             :             {
    3047          13 :                 osContainerName = oContainerName.ToString();
    3048             :             }
    3049             : 
    3050          13 :             const CPLString osKeyFilename("^" + osContainerName);
    3051          13 :             CPLJSONObject oFilenameCap = oObj.GetObj(osKeyFilename);
    3052          13 :             if (oFilenameCap.GetType() == CPLJSONObject::Type::String)
    3053             :             {
    3054             :                 VSIStatBufL sStat;
    3055          30 :                 CPLString osSrcFilename(CPLFormFilenameSafe(
    3056          20 :                     CPLGetPathSafe(osLabelSrcFilename).c_str(),
    3057          30 :                     oFilenameCap.ToString().c_str(), nullptr));
    3058             : 
    3059          10 :                 if (CPLHasPathTraversal(oFilenameCap.ToString().c_str()))
    3060             :                 {
    3061           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    3062             :                              "Path traversal detected for %s: %s. Removing "
    3063             :                              "this section from the label",
    3064           0 :                              osKey.c_str(), oFilenameCap.ToString().c_str());
    3065           0 :                     oLabel.Delete(osKey);
    3066           0 :                     continue;
    3067             :                 }
    3068          10 :                 else if (VSIStatL(osSrcFilename, &sStat) == 0)
    3069             :                 {
    3070           4 :                     oSection.osSrcFilename = std::move(osSrcFilename);
    3071             :                 }
    3072             :                 else
    3073             :                 {
    3074           6 :                     CPLError(CE_Warning, CPLE_AppDefined,
    3075             :                              "Object %s points to %s, which does "
    3076             :                              "not exist. Removing this section "
    3077             :                              "from the label",
    3078             :                              osKey.c_str(), osSrcFilename.c_str());
    3079           6 :                     oLabel.Delete(osKey);
    3080           6 :                     continue;
    3081             :                 }
    3082             :             }
    3083             : 
    3084           7 :             if (!m_osExternalFilename.empty())
    3085             :             {
    3086           2 :                 oObj.Set("StartByte", 1);
    3087             :             }
    3088             :             else
    3089             :             {
    3090          10 :                 CPLString osPlaceHolder;
    3091             :                 osPlaceHolder.Printf(
    3092             :                     "!*^PLACEHOLDER_%d_STARTBYTE^*!",
    3093           5 :                     static_cast<int>(m_aoNonPixelSections.size()) + 1);
    3094           5 :                 oObj.Set("StartByte", osPlaceHolder);
    3095           5 :                 oSection.osPlaceHolder = std::move(osPlaceHolder);
    3096             :             }
    3097             : 
    3098           7 :             if (!m_osExternalFilename.empty())
    3099             :             {
    3100           4 :                 CPLString osDstFilename(CPLGetBasenameSafe(GetDescription()));
    3101           2 :                 osDstFilename += ".";
    3102           2 :                 osDstFilename += osContainerName;
    3103           2 :                 if (!osName.empty())
    3104             :                 {
    3105           2 :                     osDstFilename += ".";
    3106           2 :                     osDstFilename += osName;
    3107             :                 }
    3108             : 
    3109           4 :                 oSection.osDstFilename = CPLFormFilenameSafe(
    3110           4 :                     CPLGetPathSafe(GetDescription()).c_str(), osDstFilename,
    3111           2 :                     nullptr);
    3112             : 
    3113           2 :                 oObj.Set(osKeyFilename, osDstFilename);
    3114             :             }
    3115             :             else
    3116             :             {
    3117           5 :                 oObj.Delete(osKeyFilename);
    3118             :             }
    3119             : 
    3120           7 :             m_aoNonPixelSections.push_back(std::move(oSection));
    3121             :         }
    3122             :     }
    3123         140 :     m_oJSonLabel = std::move(oLabel);
    3124         140 : }
    3125             : 
    3126             : /************************************************************************/
    3127             : /*                            BuildHistory()                            */
    3128             : /************************************************************************/
    3129             : 
    3130         140 : void ISIS3Dataset::BuildHistory()
    3131             : {
    3132         280 :     CPLString osHistory;
    3133             : 
    3134         140 :     if (m_oSrcJSonLabel.IsValid() && m_bUseSrcHistory)
    3135             :     {
    3136          25 :         vsi_l_offset nHistoryOffset = 0;
    3137          25 :         int nHistorySize = 0;
    3138          50 :         CPLString osSrcFilename;
    3139             : 
    3140          75 :         CPLJSONObject oFilename = m_oSrcJSonLabel["_filename"];
    3141          25 :         if (oFilename.GetType() == CPLJSONObject::Type::String)
    3142             :         {
    3143          20 :             osSrcFilename = oFilename.ToString();
    3144             :         }
    3145          50 :         CPLString osHistoryFilename(osSrcFilename);
    3146          75 :         CPLJSONObject oHistory = m_oSrcJSonLabel["History_IsisCube"];
    3147          25 :         if (oHistory.GetType() == CPLJSONObject::Type::Object)
    3148             :         {
    3149          33 :             CPLJSONObject oHistoryFilename = oHistory["^History"];
    3150          11 :             if (oHistoryFilename.GetType() == CPLJSONObject::Type::String)
    3151             :             {
    3152          14 :                 osHistoryFilename = CPLFormFilenameSafe(
    3153          14 :                     CPLGetPathSafe(osSrcFilename).c_str(),
    3154          21 :                     oHistoryFilename.ToString().c_str(), nullptr);
    3155           7 :                 if (CPLHasPathTraversal(oHistoryFilename.ToString().c_str()))
    3156             :                 {
    3157           0 :                     CPLError(CE_Warning, CPLE_AppDefined,
    3158             :                              "Path traversal detected for History: %s. Not "
    3159             :                              "including it in the label",
    3160           0 :                              oHistoryFilename.ToString().c_str());
    3161           0 :                     osHistoryFilename.clear();
    3162             :                 }
    3163             :             }
    3164             : 
    3165          33 :             CPLJSONObject oStartByte = oHistory["StartByte"];
    3166          11 :             if (oStartByte.GetType() == CPLJSONObject::Type::Integer)
    3167             :             {
    3168          11 :                 if (oStartByte.ToInteger() > 0)
    3169             :                 {
    3170          11 :                     nHistoryOffset =
    3171          11 :                         static_cast<vsi_l_offset>(oStartByte.ToInteger()) - 1U;
    3172             :                 }
    3173             :             }
    3174             : 
    3175          33 :             CPLJSONObject oBytes = oHistory["Bytes"];
    3176          11 :             if (oBytes.GetType() == CPLJSONObject::Type::Integer)
    3177             :             {
    3178          11 :                 nHistorySize = static_cast<int>(oBytes.ToInteger());
    3179             :             }
    3180             :         }
    3181             : 
    3182          25 :         if (osHistoryFilename.empty())
    3183             :         {
    3184           5 :             CPLDebug("ISIS3", "Cannot find filename for source history");
    3185             :         }
    3186          20 :         else if (nHistorySize <= 0 || nHistorySize > 1000000)
    3187             :         {
    3188           9 :             CPLDebug("ISIS3", "Invalid or missing value for History.Bytes "
    3189             :                               "for source history");
    3190             :         }
    3191             :         else
    3192             :         {
    3193          11 :             VSILFILE *fpHistory = VSIFOpenL(osHistoryFilename, "rb");
    3194          11 :             if (fpHistory != nullptr)
    3195             :             {
    3196           6 :                 VSIFSeekL(fpHistory, nHistoryOffset, SEEK_SET);
    3197           6 :                 osHistory.resize(nHistorySize);
    3198           6 :                 if (VSIFReadL(&osHistory[0], nHistorySize, 1, fpHistory) != 1)
    3199             :                 {
    3200           0 :                     CPLError(CE_Warning, CPLE_FileIO,
    3201             :                              "Cannot read %d bytes at offset " CPL_FRMT_GUIB
    3202             :                              "of %s: history will not be preserved",
    3203             :                              nHistorySize, nHistoryOffset,
    3204             :                              osHistoryFilename.c_str());
    3205           0 :                     osHistory.clear();
    3206             :                 }
    3207           6 :                 VSIFCloseL(fpHistory);
    3208             :             }
    3209             :             else
    3210             :             {
    3211           5 :                 CPLError(CE_Warning, CPLE_FileIO,
    3212             :                          "Cannot open %s: history will not be preserved",
    3213             :                          osHistoryFilename.c_str());
    3214             :             }
    3215             :         }
    3216             :     }
    3217             : 
    3218         140 :     if (m_bAddGDALHistory && !m_osGDALHistory.empty())
    3219             :     {
    3220           1 :         if (!osHistory.empty())
    3221           0 :             osHistory += "\n";
    3222           1 :         osHistory += m_osGDALHistory;
    3223             :     }
    3224         139 :     else if (m_bAddGDALHistory)
    3225             :     {
    3226         137 :         if (!osHistory.empty())
    3227           6 :             osHistory += "\n";
    3228             : 
    3229         274 :         CPLJSONObject oHistoryObj;
    3230         137 :         char szFullFilename[2048] = {0};
    3231         137 :         if (!CPLGetExecPath(szFullFilename, sizeof(szFullFilename) - 1))
    3232           0 :             strcpy(szFullFilename, "unknown_program");
    3233         274 :         const CPLString osProgram(CPLGetBasenameSafe(szFullFilename));
    3234         274 :         const CPLString osPath(CPLGetPathSafe(szFullFilename));
    3235             : 
    3236         274 :         CPLJSONObject oObj;
    3237         137 :         oHistoryObj.Add(osProgram, oObj);
    3238             : 
    3239         137 :         oObj.Add("_type", "object");
    3240         137 :         oObj.Add("GdalVersion", GDALVersionInfo("RELEASE_NAME"));
    3241         137 :         if (osPath != ".")
    3242         137 :             oObj.Add("ProgramPath", osPath);
    3243         137 :         time_t nCurTime = time(nullptr);
    3244         137 :         if (nCurTime != -1)
    3245             :         {
    3246             :             struct tm mytm;
    3247         137 :             CPLUnixTimeToYMDHMS(nCurTime, &mytm);
    3248         137 :             oObj.Add("ExecutionDateTime",
    3249             :                      CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
    3250         137 :                                 mytm.tm_year + 1900, mytm.tm_mon + 1,
    3251             :                                 mytm.tm_mday, mytm.tm_hour, mytm.tm_min,
    3252             :                                 mytm.tm_sec));
    3253             :         }
    3254         137 :         char szHostname[256] = {0};
    3255         137 :         if (gethostname(szHostname, sizeof(szHostname) - 1) == 0)
    3256             :         {
    3257         137 :             oObj.Add("HostName", std::string(szHostname));
    3258             :         }
    3259         137 :         const char *pszUsername = CPLGetConfigOption("USERNAME", nullptr);
    3260         137 :         if (pszUsername == nullptr)
    3261         137 :             pszUsername = CPLGetConfigOption("USER", nullptr);
    3262         137 :         if (pszUsername != nullptr)
    3263             :         {
    3264           0 :             oObj.Add("UserName", pszUsername);
    3265             :         }
    3266         137 :         oObj.Add("Description", "GDAL conversion");
    3267             : 
    3268         137 :         CPLJSONObject oUserParameters;
    3269         137 :         oObj.Add("UserParameters", oUserParameters);
    3270             : 
    3271         137 :         oUserParameters.Add("_type", "group");
    3272         137 :         if (!m_osFromFilename.empty())
    3273             :         {
    3274          38 :             const CPLString osFromFilename = CPLGetFilename(m_osFromFilename);
    3275          38 :             oUserParameters.Add("FROM", osFromFilename);
    3276             :         }
    3277         137 :         if (nullptr != GetDescription())
    3278             :         {
    3279         137 :             const CPLString osToFileName = CPLGetFilename(GetDescription());
    3280         137 :             oUserParameters.Add("TO", osToFileName);
    3281             :         }
    3282         137 :         if (m_bForce360)
    3283           1 :             oUserParameters.Add("Force_360", "true");
    3284             : 
    3285         137 :         osHistory += SerializeAsPDL(oHistoryObj);
    3286             :     }
    3287             : 
    3288         140 :     m_osHistory = std::move(osHistory);
    3289         140 : }
    3290             : 
    3291             : /************************************************************************/
    3292             : /*                             WriteLabel()                             */
    3293             : /************************************************************************/
    3294             : 
    3295         139 : void ISIS3Dataset::WriteLabel()
    3296             : {
    3297         139 :     m_bIsLabelWritten = true;
    3298             : 
    3299         139 :     if (!m_oJSonLabel.IsValid())
    3300         139 :         BuildLabel();
    3301             : 
    3302             :     // Serialize label
    3303         278 :     CPLString osLabel(SerializeAsPDL(m_oJSonLabel));
    3304         139 :     osLabel += "End\n";
    3305         139 :     if (m_osExternalFilename.empty() && osLabel.size() < 65536)
    3306             :     {
    3307             :         // In-line labels have conventionally a minimize size of 65536 bytes
    3308             :         // See #2741
    3309         108 :         osLabel.resize(65536);
    3310             :     }
    3311         139 :     char *pszLabel = &osLabel[0];
    3312         139 :     const int nLabelSize = static_cast<int>(osLabel.size());
    3313             : 
    3314             :     // Hack back StartByte value
    3315             :     {
    3316         139 :         char *pszStartByte = strstr(pszLabel, pszSTARTBYTE_PLACEHOLDER);
    3317         139 :         if (pszStartByte != nullptr)
    3318             :         {
    3319         108 :             const char *pszOffset = CPLSPrintf("%d", 1 + nLabelSize);
    3320         108 :             memcpy(pszStartByte, pszOffset, strlen(pszOffset));
    3321         108 :             memset(pszStartByte + strlen(pszOffset), ' ',
    3322         108 :                    strlen(pszSTARTBYTE_PLACEHOLDER) - strlen(pszOffset));
    3323             :         }
    3324             :     }
    3325             : 
    3326             :     // Hack back Label.Bytes value
    3327             :     {
    3328         139 :         char *pszLabelBytes = strstr(pszLabel, pszLABEL_BYTES_PLACEHOLDER);
    3329         139 :         if (pszLabelBytes != nullptr)
    3330             :         {
    3331         139 :             const char *pszBytes = CPLSPrintf("%d", nLabelSize);
    3332         139 :             memcpy(pszLabelBytes, pszBytes, strlen(pszBytes));
    3333         139 :             memset(pszLabelBytes + strlen(pszBytes), ' ',
    3334         139 :                    strlen(pszLABEL_BYTES_PLACEHOLDER) - strlen(pszBytes));
    3335             :         }
    3336             :     }
    3337             : 
    3338         139 :     const GDALDataType eType = GetRasterBand(1)->GetRasterDataType();
    3339         139 :     const int nDTSize = GDALGetDataTypeSizeBytes(eType);
    3340         139 :     vsi_l_offset nImagePixels = 0;
    3341         139 :     if (m_poExternalDS == nullptr)
    3342             :     {
    3343         121 :         if (m_bIsTiled)
    3344             :         {
    3345           7 :             int nBlockXSize = 1, nBlockYSize = 1;
    3346           7 :             GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
    3347           7 :             nImagePixels = static_cast<vsi_l_offset>(nBlockXSize) *
    3348          14 :                            nBlockYSize * nBands *
    3349           7 :                            DIV_ROUND_UP(nRasterXSize, nBlockXSize) *
    3350           7 :                            DIV_ROUND_UP(nRasterYSize, nBlockYSize);
    3351             :         }
    3352             :         else
    3353             :         {
    3354         114 :             nImagePixels =
    3355         114 :                 static_cast<vsi_l_offset>(nRasterXSize) * nRasterYSize * nBands;
    3356             :         }
    3357             :     }
    3358             : 
    3359             :     // Hack back History.StartBytes value
    3360             :     char *pszHistoryStartByte =
    3361         139 :         strstr(pszLabel, pszHISTORY_STARTBYTE_PLACEHOLDER);
    3362             : 
    3363         139 :     vsi_l_offset nHistoryOffset = 0;
    3364         139 :     vsi_l_offset nLastOffset = 0;
    3365         139 :     if (pszHistoryStartByte != nullptr)
    3366             :     {
    3367         107 :         CPLAssert(m_osExternalFilename.empty());
    3368         107 :         nHistoryOffset = nLabelSize + nImagePixels * nDTSize;
    3369         107 :         nLastOffset = nHistoryOffset + m_osHistory.size();
    3370             :         const char *pszStartByte =
    3371         107 :             CPLSPrintf(CPL_FRMT_GUIB, nHistoryOffset + 1);
    3372         107 :         CPLAssert(strlen(pszStartByte) <
    3373             :                   strlen(pszHISTORY_STARTBYTE_PLACEHOLDER));
    3374         107 :         memcpy(pszHistoryStartByte, pszStartByte, strlen(pszStartByte));
    3375         107 :         memset(pszHistoryStartByte + strlen(pszStartByte), ' ',
    3376         107 :                strlen(pszHISTORY_STARTBYTE_PLACEHOLDER) - strlen(pszStartByte));
    3377             :     }
    3378             : 
    3379             :     // Replace placeholders in other sections
    3380         146 :     for (size_t i = 0; i < m_aoNonPixelSections.size(); ++i)
    3381             :     {
    3382           7 :         if (!m_aoNonPixelSections[i].osPlaceHolder.empty())
    3383             :         {
    3384             :             char *pszPlaceHolder =
    3385           5 :                 strstr(pszLabel, m_aoNonPixelSections[i].osPlaceHolder.c_str());
    3386           5 :             CPLAssert(pszPlaceHolder != nullptr);
    3387             :             const char *pszStartByte =
    3388           5 :                 CPLSPrintf(CPL_FRMT_GUIB, nLastOffset + 1);
    3389           5 :             nLastOffset += m_aoNonPixelSections[i].nSize;
    3390           5 :             CPLAssert(strlen(pszStartByte) <
    3391             :                       m_aoNonPixelSections[i].osPlaceHolder.size());
    3392             : 
    3393           5 :             memcpy(pszPlaceHolder, pszStartByte, strlen(pszStartByte));
    3394           5 :             memset(pszPlaceHolder + strlen(pszStartByte), ' ',
    3395           5 :                    m_aoNonPixelSections[i].osPlaceHolder.size() -
    3396           5 :                        strlen(pszStartByte));
    3397             :         }
    3398             :     }
    3399             : 
    3400             :     // Write to final file
    3401         139 :     VSIFSeekL(m_fpLabel, 0, SEEK_SET);
    3402         139 :     VSIFWriteL(pszLabel, 1, osLabel.size(), m_fpLabel);
    3403             : 
    3404         139 :     if (m_osExternalFilename.empty())
    3405             :     {
    3406             :         // Update image offset in bands
    3407         108 :         if (m_bIsTiled)
    3408             :         {
    3409          15 :             for (int i = 0; i < nBands; i++)
    3410             :             {
    3411             :                 ISISTiledBand *poBand =
    3412           9 :                     reinterpret_cast<ISISTiledBand *>(GetRasterBand(i + 1));
    3413           9 :                 poBand->m_nFirstTileOffset += nLabelSize;
    3414             :             }
    3415             :         }
    3416             :         else
    3417             :         {
    3418         236 :             for (int i = 0; i < nBands; i++)
    3419             :             {
    3420             :                 ISIS3RawRasterBand *poBand =
    3421             :                     reinterpret_cast<ISIS3RawRasterBand *>(
    3422         134 :                         GetRasterBand(i + 1));
    3423         134 :                 poBand->nImgOffset += nLabelSize;
    3424             :             }
    3425             :         }
    3426             :     }
    3427             : 
    3428         139 :     if (m_bInitToNodata)
    3429             :     {
    3430             :         // Initialize the image to nodata
    3431          61 :         const double dfNoData = GetRasterBand(1)->GetNoDataValue();
    3432          61 :         if (dfNoData == 0.0)
    3433             :         {
    3434          43 :             VSIFTruncateL(m_fpImage,
    3435          43 :                           VSIFTellL(m_fpImage) + nImagePixels * nDTSize);
    3436             :         }
    3437          18 :         else if (nDTSize != 0)  // to make Coverity not warn about div by 0
    3438             :         {
    3439          18 :             const int nPageSize = 4096;  // Must be multiple of 4 since
    3440             :                                          // Float32 is the largest type
    3441          18 :             CPLAssert((nPageSize % nDTSize) == 0);
    3442          18 :             const int nMaxPerPage = nPageSize / nDTSize;
    3443          18 :             GByte *pabyTemp = static_cast<GByte *>(CPLMalloc(nPageSize));
    3444          18 :             GDALCopyWords(&dfNoData, GDT_Float64, 0, pabyTemp, eType, nDTSize,
    3445             :                           nMaxPerPage);
    3446             : #ifdef CPL_MSB
    3447             :             GDALSwapWords(pabyTemp, nDTSize, nMaxPerPage, nDTSize);
    3448             : #endif
    3449         169 :             for (vsi_l_offset i = 0; i < nImagePixels; i += nMaxPerPage)
    3450             :             {
    3451             :                 int n;
    3452         151 :                 if (i + nMaxPerPage <= nImagePixels)
    3453         133 :                     n = nMaxPerPage;
    3454             :                 else
    3455          18 :                     n = static_cast<int>(nImagePixels - i);
    3456         151 :                 if (VSIFWriteL(pabyTemp, static_cast<size_t>(n) * nDTSize, 1,
    3457         151 :                                m_fpImage) != 1)
    3458             :                 {
    3459           0 :                     CPLError(CE_Failure, CPLE_FileIO,
    3460             :                              "Cannot initialize imagery to null");
    3461           0 :                     break;
    3462             :                 }
    3463             :             }
    3464             : 
    3465          18 :             CPLFree(pabyTemp);
    3466             :         }
    3467             :     }
    3468             : 
    3469             :     // Write history
    3470         139 :     if (!m_osHistory.empty())
    3471             :     {
    3472         137 :         if (m_osExternalFilename.empty())
    3473             :         {
    3474         107 :             VSIFSeekL(m_fpLabel, nHistoryOffset, SEEK_SET);
    3475         107 :             VSIFWriteL(m_osHistory.c_str(), 1, m_osHistory.size(), m_fpLabel);
    3476             :         }
    3477             :         else
    3478             :         {
    3479          60 :             CPLString osFilename(CPLGetBasenameSafe(GetDescription()));
    3480          30 :             osFilename += ".History.IsisCube";
    3481          60 :             osFilename = CPLFormFilenameSafe(
    3482          60 :                 CPLGetPathSafe(GetDescription()).c_str(), osFilename, nullptr);
    3483          30 :             VSILFILE *fp = VSIFOpenL(osFilename, "wb");
    3484          30 :             if (fp)
    3485             :             {
    3486          30 :                 m_aosAdditionalFiles.AddString(osFilename);
    3487             : 
    3488          30 :                 VSIFWriteL(m_osHistory.c_str(), 1, m_osHistory.size(), fp);
    3489          30 :                 VSIFCloseL(fp);
    3490             :             }
    3491             :             else
    3492             :             {
    3493           0 :                 CPLError(CE_Warning, CPLE_FileIO, "Cannot write %s",
    3494             :                          osFilename.c_str());
    3495             :             }
    3496             :         }
    3497             :     }
    3498             : 
    3499             :     // Write other non pixel sections
    3500         146 :     for (size_t i = 0; i < m_aoNonPixelSections.size(); ++i)
    3501             :     {
    3502             :         VSILFILE *fpSrc =
    3503           7 :             VSIFOpenL(m_aoNonPixelSections[i].osSrcFilename, "rb");
    3504           7 :         if (fpSrc == nullptr)
    3505             :         {
    3506           0 :             CPLError(CE_Warning, CPLE_FileIO, "Cannot open %s",
    3507           0 :                      m_aoNonPixelSections[i].osSrcFilename.c_str());
    3508           0 :             continue;
    3509             :         }
    3510             : 
    3511           7 :         VSILFILE *fpDest = m_fpLabel;
    3512           7 :         if (!m_aoNonPixelSections[i].osDstFilename.empty())
    3513             :         {
    3514           2 :             fpDest = VSIFOpenL(m_aoNonPixelSections[i].osDstFilename, "wb");
    3515           2 :             if (fpDest == nullptr)
    3516             :             {
    3517           0 :                 CPLError(CE_Warning, CPLE_FileIO, "Cannot create %s",
    3518           0 :                          m_aoNonPixelSections[i].osDstFilename.c_str());
    3519           0 :                 VSIFCloseL(fpSrc);
    3520           0 :                 continue;
    3521             :             }
    3522             : 
    3523             :             m_aosAdditionalFiles.AddString(
    3524           2 :                 m_aoNonPixelSections[i].osDstFilename);
    3525             :         }
    3526             : 
    3527           7 :         VSIFSeekL(fpSrc, m_aoNonPixelSections[i].nSrcOffset, SEEK_SET);
    3528             :         GByte abyBuffer[4096];
    3529           7 :         vsi_l_offset nRemaining = m_aoNonPixelSections[i].nSize;
    3530          14 :         while (nRemaining)
    3531             :         {
    3532           7 :             size_t nToRead = 4096;
    3533           7 :             if (nRemaining < nToRead)
    3534           7 :                 nToRead = static_cast<size_t>(nRemaining);
    3535           7 :             size_t nRead = VSIFReadL(abyBuffer, 1, nToRead, fpSrc);
    3536           7 :             if (nRead != nToRead)
    3537             :             {
    3538           0 :                 CPLError(CE_Warning, CPLE_FileIO,
    3539             :                          "Could not read " CPL_FRMT_GUIB " bytes from %s",
    3540           0 :                          m_aoNonPixelSections[i].nSize,
    3541           0 :                          m_aoNonPixelSections[i].osSrcFilename.c_str());
    3542           0 :                 break;
    3543             :             }
    3544           7 :             VSIFWriteL(abyBuffer, 1, nRead, fpDest);
    3545           7 :             nRemaining -= nRead;
    3546             :         }
    3547             : 
    3548           7 :         VSIFCloseL(fpSrc);
    3549           7 :         if (fpDest != m_fpLabel)
    3550           2 :             VSIFCloseL(fpDest);
    3551             :     }
    3552         139 : }
    3553             : 
    3554             : /************************************************************************/
    3555             : /*                           SerializeAsPDL()                           */
    3556             : /************************************************************************/
    3557             : 
    3558         276 : CPLString ISIS3Dataset::SerializeAsPDL(const CPLJSONObject &oObj)
    3559             : {
    3560         552 :     const CPLString osTmpFile(VSIMemGenerateHiddenFilename("isis3_pdl"));
    3561         276 :     VSILFILE *fpTmp = VSIFOpenL(osTmpFile, "wb+");
    3562         276 :     SerializeAsPDL(fpTmp, oObj);
    3563         276 :     VSIFCloseL(fpTmp);
    3564             :     CPLString osContent(reinterpret_cast<char *>(
    3565         276 :         VSIGetMemFileBuffer(osTmpFile, nullptr, FALSE)));
    3566         276 :     VSIUnlink(osTmpFile);
    3567         552 :     return osContent;
    3568             : }
    3569             : 
    3570             : /************************************************************************/
    3571             : /*                           SerializeAsPDL()                           */
    3572             : /************************************************************************/
    3573             : 
    3574             : constexpr size_t WIDTH = 79;
    3575             : 
    3576        1481 : void ISIS3Dataset::SerializeAsPDL(VSILFILE *fp, const CPLJSONObject &oObj,
    3577             :                                   int nDepth)
    3578             : {
    3579        2962 :     CPLString osIndentation;
    3580        3603 :     for (int i = 0; i < nDepth; i++)
    3581        2122 :         osIndentation += "  ";
    3582             : 
    3583        2962 :     std::vector<CPLJSONObject> aoChildren = oObj.GetChildren();
    3584        1481 :     size_t nMaxKeyLength = 0;
    3585        2962 :     std::vector<std::pair<CPLString, CPLJSONObject>> aoChildren2;
    3586        8133 :     for (const CPLJSONObject &oChild : aoChildren)
    3587             :     {
    3588        6652 :         const CPLString osKey = oChild.GetName();
    3589       12099 :         if (EQUAL(osKey, "_type") || EQUAL(osKey, "_container_name") ||
    3590       12099 :             EQUAL(osKey, "_filename") || EQUAL(osKey, "_data"))
    3591             :         {
    3592        1382 :             continue;
    3593             :         }
    3594             : 
    3595        5270 :         const auto eType = oChild.GetType();
    3596        5270 :         if (eType == CPLJSONObject::Type::String ||
    3597        2383 :             eType == CPLJSONObject::Type::Integer ||
    3598        1546 :             eType == CPLJSONObject::Type::Double ||
    3599             :             eType == CPLJSONObject::Type::Array)
    3600             :         {
    3601        3734 :             aoChildren2.emplace_back(osKey, oChild);
    3602        3734 :             if (osKey.size() > nMaxKeyLength)
    3603             :             {
    3604        1777 :                 nMaxKeyLength = osKey.size();
    3605             :             }
    3606             :         }
    3607        1536 :         else if (eType == CPLJSONObject::Type::Object)
    3608             :         {
    3609        4608 :             CPLJSONObject oValue = oChild.GetObj("value");
    3610        4608 :             CPLJSONObject oUnit = oChild.GetObj("unit");
    3611        1791 :             if (oValue.IsValid() &&
    3612         255 :                 oUnit.GetType() == CPLJSONObject::Type::String)
    3613             :             {
    3614         255 :                 aoChildren2.emplace_back(osKey, oChild);
    3615         255 :                 if (osKey.size() > nMaxKeyLength)
    3616             :                 {
    3617          67 :                     nMaxKeyLength = osKey.size();
    3618             :                 }
    3619             :             }
    3620        1281 :             else if (oChild.GetObj("values").GetType() ==
    3621             :                      CPLJSONObject::Type::Array)
    3622             :             {
    3623           4 :                 if (osKey.size() > nMaxKeyLength)
    3624             :                 {
    3625           1 :                     nMaxKeyLength = osKey.size();
    3626             :                 }
    3627          13 :                 for (const auto &oSubChild : oChild.GetObj("values").ToArray())
    3628             :                 {
    3629           9 :                     aoChildren2.emplace_back(osKey, oSubChild);
    3630             :                 }
    3631             :             }
    3632             :             else
    3633             :             {
    3634        1277 :                 aoChildren2.emplace_back(osKey, oChild);
    3635             :             }
    3636             :         }
    3637             :         else
    3638             :         {
    3639           0 :             aoChildren2.emplace_back(osKey, oChild);
    3640             :         }
    3641             :     }
    3642             : 
    3643        6756 :     for (const auto &[osKey, oChild] : aoChildren2)
    3644             :     {
    3645        5275 :         if (STARTS_WITH(osKey, "_comment"))
    3646             :         {
    3647           1 :             if (oChild.GetType() == CPLJSONObject::Type::String)
    3648             :             {
    3649           1 :                 VSIFPrintfL(fp, "#%s\n", oChild.ToString().c_str());
    3650             :             }
    3651           1 :             continue;
    3652             :         }
    3653       10548 :         CPLString osPadding;
    3654        5274 :         size_t nLen = osKey.size();
    3655        5274 :         if (nLen < nMaxKeyLength)
    3656             :         {
    3657        3210 :             osPadding.append(nMaxKeyLength - nLen, ' ');
    3658             :         }
    3659             : 
    3660        5274 :         const auto eType = oChild.GetType();
    3661        5274 :         if (eType == CPLJSONObject::Type::Object)
    3662             :         {
    3663        4602 :             CPLJSONObject oType = oChild.GetObj("_type");
    3664        4602 :             CPLJSONObject oContainerName = oChild.GetObj("_container_name");
    3665        3068 :             CPLString osContainerName = osKey;
    3666        1534 :             if (oContainerName.GetType() == CPLJSONObject::Type::String)
    3667             :             {
    3668         148 :                 osContainerName = oContainerName.ToString();
    3669             :             }
    3670        1534 :             if (oType.GetType() == CPLJSONObject::Type::String)
    3671             :             {
    3672        3615 :                 const CPLString osType = oType.ToString();
    3673        1205 :                 if (EQUAL(osType, "Object"))
    3674             :                 {
    3675         705 :                     if (nDepth == 0 && VSIFTellL(fp) != 0)
    3676         290 :                         VSIFPrintfL(fp, "\n");
    3677         705 :                     VSIFPrintfL(fp, "%sObject = %s\n", osIndentation.c_str(),
    3678             :                                 osContainerName.c_str());
    3679         705 :                     SerializeAsPDL(fp, oChild, nDepth + 1);
    3680         705 :                     VSIFPrintfL(fp, "%sEnd_Object\n", osIndentation.c_str());
    3681             :                 }
    3682         500 :                 else if (EQUAL(osType, "Group"))
    3683             :                 {
    3684         500 :                     VSIFPrintfL(fp, "\n");
    3685         500 :                     VSIFPrintfL(fp, "%sGroup = %s\n", osIndentation.c_str(),
    3686             :                                 osContainerName.c_str());
    3687         500 :                     SerializeAsPDL(fp, oChild, nDepth + 1);
    3688         500 :                     VSIFPrintfL(fp, "%sEnd_Group\n", osIndentation.c_str());
    3689             :                 }
    3690             :             }
    3691             :             else
    3692             :             {
    3693         987 :                 CPLJSONObject oValue = oChild.GetObj("value");
    3694         987 :                 CPLJSONObject oUnit = oChild.GetObj("unit");
    3695         586 :                 if (oValue.IsValid() &&
    3696         257 :                     oUnit.GetType() == CPLJSONObject::Type::String)
    3697             :                 {
    3698         771 :                     const CPLString osUnit = oUnit.ToString();
    3699         257 :                     const auto eValueType = oValue.GetType();
    3700         257 :                     if (eValueType == CPLJSONObject::Type::Integer)
    3701             :                     {
    3702           3 :                         VSIFPrintfL(fp, "%s%s%s = %d <%s>\n",
    3703             :                                     osIndentation.c_str(), osKey.c_str(),
    3704             :                                     osPadding.c_str(), oValue.ToInteger(),
    3705             :                                     osUnit.c_str());
    3706             :                     }
    3707         254 :                     else if (eValueType == CPLJSONObject::Type::Double)
    3708             :                     {
    3709         253 :                         const double dfVal = oValue.ToDouble();
    3710         253 :                         if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
    3711         253 :                             static_cast<int>(dfVal) == dfVal)
    3712             :                         {
    3713          96 :                             VSIFPrintfL(fp, "%s%s%s = %d.0 <%s>\n",
    3714             :                                         osIndentation.c_str(), osKey.c_str(),
    3715             :                                         osPadding.c_str(),
    3716             :                                         static_cast<int>(dfVal),
    3717             :                                         osUnit.c_str());
    3718             :                         }
    3719             :                         else
    3720             :                         {
    3721         157 :                             VSIFPrintfL(fp, "%s%s%s = %.17g <%s>\n",
    3722             :                                         osIndentation.c_str(), osKey.c_str(),
    3723             :                                         osPadding.c_str(), dfVal,
    3724             :                                         osUnit.c_str());
    3725             :                         }
    3726             :                     }
    3727             :                 }
    3728             :             }
    3729             :         }
    3730        3740 :         else if (eType == CPLJSONObject::Type::String)
    3731             :         {
    3732        6489 :             CPLString osVal = oChild.ToString();
    3733        2163 :             const char *pszVal = osVal.c_str();
    3734        2163 :             if (pszVal[0] == '\0' || strchr(pszVal, ' ') ||
    3735        2017 :                 strstr(pszVal, "\\n") || strstr(pszVal, "\\r"))
    3736             :             {
    3737         146 :                 osVal.replaceAll("\\n", "\n");
    3738         146 :                 osVal.replaceAll("\\r", "\r");
    3739         146 :                 VSIFPrintfL(fp, "%s%s%s = \"%s\"\n", osIndentation.c_str(),
    3740             :                             osKey.c_str(), osPadding.c_str(), osVal.c_str());
    3741             :             }
    3742             :             else
    3743             :             {
    3744        2017 :                 if (osIndentation.size() + osKey.size() + osPadding.size() +
    3745        2017 :                             strlen(" = ") + strlen(pszVal) >
    3746        2018 :                         WIDTH &&
    3747           1 :                     osIndentation.size() + osKey.size() + osPadding.size() +
    3748             :                             strlen(" = ") <
    3749             :                         WIDTH)
    3750             :                 {
    3751           1 :                     size_t nFirstPos = osIndentation.size() + osKey.size() +
    3752           1 :                                        osPadding.size() + strlen(" = ");
    3753           1 :                     VSIFPrintfL(fp, "%s%s%s = ", osIndentation.c_str(),
    3754             :                                 osKey.c_str(), osPadding.c_str());
    3755           1 :                     size_t nCurPos = nFirstPos;
    3756          96 :                     for (int j = 0; pszVal[j] != '\0'; j++)
    3757             :                     {
    3758          95 :                         nCurPos++;
    3759          95 :                         if (nCurPos == WIDTH && pszVal[j + 1] != '\0')
    3760             :                         {
    3761           1 :                             VSIFPrintfL(fp, "-\n");
    3762          15 :                             for (size_t k = 0; k < nFirstPos; k++)
    3763             :                             {
    3764          14 :                                 const char chSpace = ' ';
    3765          14 :                                 VSIFWriteL(&chSpace, 1, 1, fp);
    3766             :                             }
    3767           1 :                             nCurPos = nFirstPos + 1;
    3768             :                         }
    3769          95 :                         VSIFWriteL(&pszVal[j], 1, 1, fp);
    3770             :                     }
    3771           1 :                     VSIFPrintfL(fp, "\n");
    3772             :                 }
    3773             :                 else
    3774             :                 {
    3775        2016 :                     VSIFPrintfL(fp, "%s%s%s = %s\n", osIndentation.c_str(),
    3776             :                                 osKey.c_str(), osPadding.c_str(), pszVal);
    3777             :                 }
    3778             :             }
    3779             :         }
    3780        1577 :         else if (eType == CPLJSONObject::Type::Integer)
    3781             :         {
    3782         728 :             const int nVal = oChild.ToInteger();
    3783         728 :             VSIFPrintfL(fp, "%s%s%s = %d\n", osIndentation.c_str(),
    3784             :                         osKey.c_str(), osPadding.c_str(), nVal);
    3785             :         }
    3786         849 :         else if (eType == CPLJSONObject::Type::Double)
    3787             :         {
    3788         837 :             const double dfVal = oChild.ToDouble();
    3789         837 :             if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
    3790         837 :                 static_cast<int>(dfVal) == dfVal)
    3791             :             {
    3792         587 :                 VSIFPrintfL(fp, "%s%s%s = %d.0\n", osIndentation.c_str(),
    3793             :                             osKey.c_str(), osPadding.c_str(),
    3794             :                             static_cast<int>(dfVal));
    3795             :             }
    3796             :             else
    3797             :             {
    3798         250 :                 VSIFPrintfL(fp, "%s%s%s = %.17g\n", osIndentation.c_str(),
    3799             :                             osKey.c_str(), osPadding.c_str(), dfVal);
    3800             :             }
    3801             :         }
    3802          12 :         else if (eType == CPLJSONObject::Type::Array)
    3803             :         {
    3804          24 :             CPLJSONArray oArrayItem(oChild);
    3805          12 :             const int nLength = oArrayItem.Size();
    3806          12 :             size_t nFirstPos = osIndentation.size() + osKey.size() +
    3807          12 :                                osPadding.size() + strlen(" = (");
    3808          12 :             VSIFPrintfL(fp, "%s%s%s = (", osIndentation.c_str(), osKey.c_str(),
    3809             :                         osPadding.c_str());
    3810          12 :             size_t nCurPos = nFirstPos;
    3811             : 
    3812          57 :             for (int idx = 0; idx < nLength; idx++)
    3813             :             {
    3814          90 :                 CPLJSONObject oItem = oArrayItem[idx];
    3815          45 :                 const auto eArrayItemType = oItem.GetType();
    3816             : 
    3817             :                 const auto outputArrayVal =
    3818         244 :                     [fp, idx, nFirstPos, &nCurPos](const std::string &osVal)
    3819             :                 {
    3820          31 :                     const size_t nValLen = osVal.size();
    3821          31 :                     if (nFirstPos < WIDTH && idx > 0 &&
    3822          23 :                         nCurPos + nValLen > WIDTH)
    3823             :                     {
    3824           2 :                         VSIFPrintfL(fp, "\n");
    3825          32 :                         for (size_t j = 0; j < nFirstPos; j++)
    3826             :                         {
    3827          30 :                             constexpr char chSpace = ' ';
    3828          30 :                             VSIFWriteL(&chSpace, 1, 1, fp);
    3829             :                         }
    3830           2 :                         nCurPos = nFirstPos;
    3831             :                     }
    3832          31 :                     VSIFPrintfL(fp, "%s", osVal.c_str());
    3833          31 :                     nCurPos += nValLen;
    3834          31 :                 };
    3835             : 
    3836          45 :                 if (eArrayItemType == CPLJSONObject::Type::Object)
    3837             :                 {
    3838           9 :                     const auto oValue = oItem["value"];
    3839           9 :                     const auto oUnit = oItem["unit"];
    3840           6 :                     if (oValue.IsValid() && oUnit.IsValid() &&
    3841           3 :                         (oValue.GetType() == CPLJSONObject::Type::Integer ||
    3842           1 :                          oValue.GetType() == CPLJSONObject::Type::Double))
    3843             :                     {
    3844           3 :                         if (oValue.GetType() == CPLJSONObject::Type::Integer)
    3845             :                         {
    3846           2 :                             const int nVal = oValue.ToInteger();
    3847           6 :                             outputArrayVal(CPLSPrintf(
    3848           4 :                                 "%d <%s>", nVal, oUnit.ToString().c_str()));
    3849             :                         }
    3850             :                         else
    3851             :                         {
    3852           1 :                             const double dfVal = oValue.ToDouble();
    3853           1 :                             if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
    3854           1 :                                 static_cast<int>(dfVal) == dfVal)
    3855             :                             {
    3856           0 :                                 outputArrayVal(CPLSPrintf(
    3857             :                                     "%d.0 <%s>", static_cast<int>(dfVal),
    3858           0 :                                     oUnit.ToString().c_str()));
    3859             :                             }
    3860             :                             else
    3861             :                             {
    3862           3 :                                 outputArrayVal(
    3863             :                                     CPLSPrintf("%.17g <%s>", dfVal,
    3864           2 :                                                oUnit.ToString().c_str()));
    3865             :                             }
    3866             :                         }
    3867             :                     }
    3868             :                     else
    3869             :                     {
    3870           0 :                         CPLError(CE_Warning, CPLE_AppDefined,
    3871             :                                  "Invalid JSON object");
    3872             :                     }
    3873             :                 }
    3874          42 :                 else if (eArrayItemType == CPLJSONObject::Type::String)
    3875             :                 {
    3876          42 :                     CPLString osVal = oItem.ToString();
    3877          14 :                     const char *pszVal = osVal.c_str();
    3878          14 :                     if (pszVal[0] == '\0' || strchr(pszVal, ' ') ||
    3879           7 :                         strstr(pszVal, "\\n") || strstr(pszVal, "\\r"))
    3880             :                     {
    3881           7 :                         osVal.replaceAll("\\n", "\n");
    3882           7 :                         osVal.replaceAll("\\r", "\r");
    3883           7 :                         VSIFPrintfL(fp, "\"%s\"", osVal.c_str());
    3884             :                     }
    3885           7 :                     else if (nFirstPos < WIDTH &&
    3886           7 :                              nCurPos + strlen(pszVal) > WIDTH)
    3887             :                     {
    3888           1 :                         if (idx > 0)
    3889             :                         {
    3890           1 :                             VSIFPrintfL(fp, "\n");
    3891          16 :                             for (size_t j = 0; j < nFirstPos; j++)
    3892             :                             {
    3893          15 :                                 const char chSpace = ' ';
    3894          15 :                                 VSIFWriteL(&chSpace, 1, 1, fp);
    3895             :                             }
    3896           1 :                             nCurPos = nFirstPos;
    3897             :                         }
    3898             : 
    3899         102 :                         for (int j = 0; pszVal[j] != '\0'; j++)
    3900             :                         {
    3901         101 :                             nCurPos++;
    3902         101 :                             if (nCurPos == WIDTH && pszVal[j + 1] != '\0')
    3903             :                             {
    3904           1 :                                 VSIFPrintfL(fp, "-\n");
    3905          16 :                                 for (size_t k = 0; k < nFirstPos; k++)
    3906             :                                 {
    3907          15 :                                     const char chSpace = ' ';
    3908          15 :                                     VSIFWriteL(&chSpace, 1, 1, fp);
    3909             :                                 }
    3910           1 :                                 nCurPos = nFirstPos + 1;
    3911             :                             }
    3912         101 :                             VSIFWriteL(&pszVal[j], 1, 1, fp);
    3913           1 :                         }
    3914             :                     }
    3915             :                     else
    3916             :                     {
    3917           6 :                         VSIFPrintfL(fp, "%s", pszVal);
    3918           6 :                         nCurPos += strlen(pszVal);
    3919             :                     }
    3920             :                 }
    3921          28 :                 else if (eArrayItemType == CPLJSONObject::Type::Integer)
    3922             :                 {
    3923          19 :                     const int nVal = oItem.ToInteger();
    3924          19 :                     outputArrayVal(CPLSPrintf("%d", nVal));
    3925             :                 }
    3926           9 :                 else if (eArrayItemType == CPLJSONObject::Type::Double)
    3927             :                 {
    3928           9 :                     const double dfVal = oItem.ToDouble();
    3929          18 :                     CPLString osVal;
    3930           9 :                     if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
    3931           9 :                         static_cast<int>(dfVal) == dfVal)
    3932             :                     {
    3933           8 :                         osVal = CPLSPrintf("%d.0", static_cast<int>(dfVal));
    3934             :                     }
    3935             :                     else
    3936             :                     {
    3937           1 :                         osVal = CPLSPrintf("%.17g", dfVal);
    3938             :                     }
    3939           9 :                     outputArrayVal(osVal);
    3940             :                 }
    3941          45 :                 if (idx < nLength - 1)
    3942             :                 {
    3943          33 :                     VSIFPrintfL(fp, ", ");
    3944          33 :                     nCurPos += 2;
    3945             :                 }
    3946             :             }
    3947          12 :             VSIFPrintfL(fp, ")\n");
    3948             :         }
    3949             :     }
    3950        1481 : }
    3951             : 
    3952             : /************************************************************************/
    3953             : /*                               Create()                               */
    3954             : /************************************************************************/
    3955             : 
    3956         170 : GDALDataset *ISIS3Dataset::Create(const char *pszFilename, int nXSize,
    3957             :                                   int nYSize, int nBandsIn, GDALDataType eType,
    3958             :                                   CSLConstList papszOptions)
    3959             : {
    3960         170 :     if (eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Int16 &&
    3961          32 :         eType != GDT_Float32 && eType != GDT_Float64)
    3962             :     {
    3963          24 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type");
    3964          24 :         return nullptr;
    3965             :     }
    3966         146 :     if (nBandsIn == 0 || nBandsIn > 32767)
    3967             :     {
    3968           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
    3969           1 :         return nullptr;
    3970             :     }
    3971             : 
    3972             :     const char *pszDataLocation =
    3973         145 :         CSLFetchNameValueDef(papszOptions, "DATA_LOCATION", "LABEL");
    3974         145 :     const bool bIsTiled = CPLFetchBool(papszOptions, "TILED", false);
    3975             :     const int nBlockXSize = std::max(
    3976         145 :         1, atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256")));
    3977             :     const int nBlockYSize = std::max(
    3978         145 :         1, atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256")));
    3979         179 :     if (!EQUAL(pszDataLocation, "LABEL") &&
    3980         179 :         !EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "LBL"))
    3981             :     {
    3982           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    3983             :                  "For DATA_LOCATION=%s, "
    3984             :                  "the main filename should have a .lbl extension",
    3985             :                  pszDataLocation);
    3986           1 :         return nullptr;
    3987             :     }
    3988             : 
    3989             :     const char *pszPermission =
    3990         144 :         VSISupportsRandomWrite(pszFilename, true) ? "wb+" : "wb";
    3991             : 
    3992         144 :     VSILFILE *fp = VSIFOpenExL(pszFilename, pszPermission, true);
    3993         144 :     if (fp == nullptr)
    3994             :     {
    3995           3 :         CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s", pszFilename,
    3996             :                  VSIGetLastErrorMsg());
    3997           3 :         return nullptr;
    3998             :     }
    3999         141 :     VSILFILE *fpImage = nullptr;
    4000         282 :     std::string osExternalFilename;
    4001         141 :     GDALDataset *poExternalDS = nullptr;
    4002         141 :     bool bGeoTIFFAsRegularExternal = false;
    4003         141 :     if (EQUAL(pszDataLocation, "EXTERNAL"))
    4004             :     {
    4005             :         osExternalFilename = CSLFetchNameValueDef(
    4006             :             papszOptions, "EXTERNAL_FILENAME",
    4007          14 :             CPLResetExtensionSafe(pszFilename, "cub").c_str());
    4008          14 :         fpImage = VSIFOpenExL(osExternalFilename.c_str(), pszPermission, true);
    4009          14 :         if (fpImage == nullptr)
    4010             :         {
    4011           1 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s",
    4012             :                      osExternalFilename.c_str(), VSIGetLastErrorMsg());
    4013           1 :             VSIFCloseL(fp);
    4014           1 :             return nullptr;
    4015             :         }
    4016             :     }
    4017         127 :     else if (EQUAL(pszDataLocation, "GEOTIFF"))
    4018             :     {
    4019             :         osExternalFilename = CSLFetchNameValueDef(
    4020             :             papszOptions, "EXTERNAL_FILENAME",
    4021          19 :             CPLResetExtensionSafe(pszFilename, "tif").c_str());
    4022             :         GDALDriver *poDrv =
    4023          19 :             static_cast<GDALDriver *>(GDALGetDriverByName("GTiff"));
    4024          19 :         if (poDrv == nullptr)
    4025             :         {
    4026           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GTiff driver");
    4027           0 :             VSIFCloseL(fp);
    4028           0 :             return nullptr;
    4029             :         }
    4030          19 :         char **papszGTiffOptions = nullptr;
    4031             :         papszGTiffOptions =
    4032          19 :             CSLSetNameValue(papszGTiffOptions, "ENDIANNESS", "LITTLE");
    4033          19 :         if (bIsTiled)
    4034             :         {
    4035             :             papszGTiffOptions =
    4036           3 :                 CSLSetNameValue(papszGTiffOptions, "TILED", "YES");
    4037           3 :             papszGTiffOptions = CSLSetNameValue(papszGTiffOptions, "BLOCKXSIZE",
    4038             :                                                 CPLSPrintf("%d", nBlockXSize));
    4039           3 :             papszGTiffOptions = CSLSetNameValue(papszGTiffOptions, "BLOCKYSIZE",
    4040             :                                                 CPLSPrintf("%d", nBlockYSize));
    4041             :         }
    4042             :         const char *pszGTiffOptions =
    4043          19 :             CSLFetchNameValueDef(papszOptions, "GEOTIFF_OPTIONS", "");
    4044          19 :         char **papszTokens = CSLTokenizeString2(pszGTiffOptions, ",", 0);
    4045          28 :         for (int i = 0; papszTokens[i] != nullptr; i++)
    4046             :         {
    4047           9 :             papszGTiffOptions = CSLAddString(papszGTiffOptions, papszTokens[i]);
    4048             :         }
    4049          19 :         CSLDestroy(papszTokens);
    4050             : 
    4051             :         // If the user didn't specify any compression and
    4052             :         // GEOTIFF_AS_REGULAR_EXTERNAL is set (or unspecified), then the
    4053             :         // GeoTIFF file can be seen as a regular external raw file, provided
    4054             :         // we make some provision on its organization.
    4055          29 :         if (CSLFetchNameValue(papszGTiffOptions, "COMPRESS") == nullptr &&
    4056          10 :             CPLFetchBool(papszOptions, "GEOTIFF_AS_REGULAR_EXTERNAL", true))
    4057             :         {
    4058           9 :             bGeoTIFFAsRegularExternal = true;
    4059             :             papszGTiffOptions =
    4060           9 :                 CSLSetNameValue(papszGTiffOptions, GDALMD_INTERLEAVE, "BAND");
    4061             :             // Will make sure that our blocks at nodata are not optimized
    4062             :             // away but indeed well written
    4063           9 :             papszGTiffOptions = CSLSetNameValue(
    4064             :                 papszGTiffOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "YES");
    4065           9 :             if (!bIsTiled && nBandsIn > 1)
    4066             :             {
    4067             :                 papszGTiffOptions =
    4068           1 :                     CSLSetNameValue(papszGTiffOptions, "BLOCKYSIZE", "1");
    4069             :             }
    4070             :         }
    4071             : 
    4072          19 :         poExternalDS = poDrv->Create(osExternalFilename.c_str(), nXSize, nYSize,
    4073             :                                      nBandsIn, eType, papszGTiffOptions);
    4074          19 :         CSLDestroy(papszGTiffOptions);
    4075          19 :         if (poExternalDS == nullptr)
    4076             :         {
    4077           1 :             CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s",
    4078             :                      osExternalFilename.c_str());
    4079           1 :             VSIFCloseL(fp);
    4080           1 :             return nullptr;
    4081             :         }
    4082             :     }
    4083             : 
    4084         139 :     ISIS3Dataset *poDS = new ISIS3Dataset();
    4085         139 :     poDS->SetDescription(pszFilename);
    4086         139 :     poDS->eAccess = GA_Update;
    4087         139 :     poDS->nRasterXSize = nXSize;
    4088         139 :     poDS->nRasterYSize = nYSize;
    4089         139 :     poDS->m_osExternalFilename = std::move(osExternalFilename);
    4090         139 :     poDS->m_poExternalDS = poExternalDS;
    4091         139 :     poDS->m_bGeoTIFFAsRegularExternal = bGeoTIFFAsRegularExternal;
    4092         139 :     if (bGeoTIFFAsRegularExternal)
    4093           8 :         poDS->m_bGeoTIFFInitDone = false;
    4094         139 :     poDS->m_fpLabel = fp;
    4095         139 :     poDS->m_fpImage = fpImage ? fpImage : fp;
    4096         139 :     poDS->m_bIsLabelWritten = false;
    4097         139 :     poDS->m_bIsTiled = bIsTiled;
    4098         139 :     poDS->m_bInitToNodata = (poDS->m_poExternalDS == nullptr);
    4099         139 :     poDS->m_osComment = CSLFetchNameValueDef(papszOptions, "COMMENT", "");
    4100             :     poDS->m_osLatitudeType =
    4101         139 :         CSLFetchNameValueDef(papszOptions, "LATITUDE_TYPE", "");
    4102             :     poDS->m_osLongitudeDirection =
    4103         139 :         CSLFetchNameValueDef(papszOptions, "LONGITUDE_DIRECTION", "");
    4104             :     poDS->m_osTargetName =
    4105         139 :         CSLFetchNameValueDef(papszOptions, "TARGET_NAME", "");
    4106         139 :     poDS->m_bForce360 = CPLFetchBool(papszOptions, "FORCE_360", false);
    4107         139 :     poDS->m_bWriteBoundingDegrees =
    4108         139 :         CPLFetchBool(papszOptions, "WRITE_BOUNDING_DEGREES", true);
    4109             :     poDS->m_osBoundingDegrees =
    4110         139 :         CSLFetchNameValueDef(papszOptions, "BOUNDING_DEGREES", "");
    4111         139 :     poDS->m_bUseSrcLabel = CPLFetchBool(papszOptions, "USE_SRC_LABEL", true);
    4112         139 :     poDS->m_bUseSrcMapping =
    4113         139 :         CPLFetchBool(papszOptions, "USE_SRC_MAPPING", false);
    4114         139 :     poDS->m_bUseSrcHistory =
    4115         139 :         CPLFetchBool(papszOptions, "USE_SRC_HISTORY", true);
    4116         139 :     poDS->m_bAddGDALHistory =
    4117         139 :         CPLFetchBool(papszOptions, "ADD_GDAL_HISTORY", true);
    4118         139 :     if (poDS->m_bAddGDALHistory)
    4119             :     {
    4120             :         poDS->m_osGDALHistory =
    4121         137 :             CSLFetchNameValueDef(papszOptions, "GDAL_HISTORY", "");
    4122             :     }
    4123         174 :     const double dfNoData = (eType == GDT_Byte)      ? ISIS3_NULL1
    4124          60 :                             : (eType == GDT_UInt16)  ? ISIS3_NULLU2
    4125          42 :                             : (eType == GDT_Int16)   ? ISIS3_NULL2
    4126          25 :                             : (eType == GDT_Float32) ? ISIS3_NULL4
    4127           8 :                             : (eType == GDT_Float64) ? ISIS3_NULL8
    4128             :                                                      : ISIS3_NULL4;
    4129             : 
    4130         321 :     for (int i = 0; i < nBandsIn; i++)
    4131             :     {
    4132         182 :         GDALRasterBand *poBand = nullptr;
    4133             : 
    4134         182 :         if (poDS->m_poExternalDS != nullptr)
    4135             :         {
    4136             :             ISIS3WrapperRasterBand *poISISBand = new ISIS3WrapperRasterBand(
    4137          24 :                 poDS->m_poExternalDS->GetRasterBand(i + 1));
    4138          24 :             poBand = poISISBand;
    4139             :         }
    4140         158 :         else if (bIsTiled)
    4141             :         {
    4142             :             ISISTiledBand *poISISBand = new ISISTiledBand(
    4143             :                 poDS, poDS->m_fpImage, i + 1, eType, nBlockXSize, nBlockYSize,
    4144             :                 0,  // nSkipBytes, to be hacked
    4145             :                 // afterwards for in-label imagery
    4146          10 :                 0, 0, CPL_IS_LSB);
    4147             : 
    4148          10 :             poBand = poISISBand;
    4149             :         }
    4150             :         else
    4151             :         {
    4152         148 :             const int nPixelOffset = GDALGetDataTypeSizeBytes(eType);
    4153         148 :             const int nLineOffset = nPixelOffset * nXSize;
    4154         148 :             const vsi_l_offset nBandOffset =
    4155         148 :                 static_cast<vsi_l_offset>(nLineOffset) * nYSize;
    4156             :             ISIS3RawRasterBand *poISISBand = new ISIS3RawRasterBand(
    4157             :                 poDS, i + 1, poDS->m_fpImage,
    4158         148 :                 nBandOffset * i,  // nImgOffset, to be
    4159             :                 // hacked afterwards for in-label imagery
    4160         148 :                 nPixelOffset, nLineOffset, eType, CPL_IS_LSB);
    4161             : 
    4162         148 :             poBand = poISISBand;
    4163             :         }
    4164         182 :         poDS->SetBand(i + 1, poBand);
    4165         182 :         poBand->SetNoDataValue(dfNoData);
    4166             :     }
    4167             : 
    4168         139 :     return poDS;
    4169             : }
    4170             : 
    4171             : /************************************************************************/
    4172             : /*                        GetUnderlyingDataset()                        */
    4173             : /************************************************************************/
    4174             : 
    4175          80 : static GDALDataset *GetUnderlyingDataset(GDALDataset *poSrcDS)
    4176             : {
    4177         160 :     if (poSrcDS->GetDriver() != nullptr &&
    4178          80 :         poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
    4179             :     {
    4180           2 :         VRTDataset *poVRTDS = cpl::down_cast<VRTDataset *>(poSrcDS);
    4181           2 :         poSrcDS = poVRTDS->GetSingleSimpleSource();
    4182             :     }
    4183             : 
    4184          80 :     return poSrcDS;
    4185             : }
    4186             : 
    4187             : /************************************************************************/
    4188             : /*                             CreateCopy()                             */
    4189             : /************************************************************************/
    4190             : 
    4191          80 : GDALDataset *ISIS3Dataset::CreateCopy(const char *pszFilename,
    4192             :                                       GDALDataset *poSrcDS, int /*bStrict*/,
    4193             :                                       CSLConstList papszOptions,
    4194             :                                       GDALProgressFunc pfnProgress,
    4195             :                                       void *pProgressData)
    4196             : {
    4197             :     const char *pszDataLocation =
    4198          80 :         CSLFetchNameValueDef(papszOptions, "DATA_LOCATION", "LABEL");
    4199          80 :     GDALDataset *poSrcUnderlyingDS = GetUnderlyingDataset(poSrcDS);
    4200          80 :     if (poSrcUnderlyingDS == nullptr)
    4201           2 :         poSrcUnderlyingDS = poSrcDS;
    4202          90 :     if (EQUAL(pszDataLocation, "GEOTIFF") &&
    4203          10 :         strcmp(poSrcUnderlyingDS->GetDescription(),
    4204             :                CSLFetchNameValueDef(
    4205             :                    papszOptions, "EXTERNAL_FILENAME",
    4206          90 :                    CPLResetExtensionSafe(pszFilename, "tif").c_str())) == 0)
    4207             :     {
    4208           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    4209             :                  "Output file has same name as input file");
    4210           1 :         return nullptr;
    4211             :     }
    4212          79 :     if (poSrcDS->GetRasterCount() == 0)
    4213             :     {
    4214           1 :         CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
    4215           1 :         return nullptr;
    4216             :     }
    4217             : 
    4218          78 :     const int nXSize = poSrcDS->GetRasterXSize();
    4219          78 :     const int nYSize = poSrcDS->GetRasterYSize();
    4220          78 :     const int nBands = poSrcDS->GetRasterCount();
    4221          78 :     GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
    4222             :     ISIS3Dataset *poDS = reinterpret_cast<ISIS3Dataset *>(
    4223          78 :         Create(pszFilename, nXSize, nYSize, nBands, eType, papszOptions));
    4224          78 :     if (poDS == nullptr)
    4225           9 :         return nullptr;
    4226          69 :     poDS->m_osFromFilename = poSrcUnderlyingDS->GetDescription();
    4227             : 
    4228          69 :     GDALGeoTransform gt;
    4229          69 :     if (poSrcDS->GetGeoTransform(gt) == CE_None && gt != GDALGeoTransform())
    4230             :     {
    4231          41 :         poDS->SetGeoTransform(gt);
    4232             :     }
    4233             : 
    4234          69 :     auto poSrcSRS = poSrcDS->GetSpatialRef();
    4235          69 :     if (poSrcSRS)
    4236             :     {
    4237          41 :         poDS->SetSpatialRef(poSrcSRS);
    4238             :     }
    4239             : 
    4240         163 :     for (int i = 1; i <= nBands; i++)
    4241             :     {
    4242          94 :         const double dfOffset = poSrcDS->GetRasterBand(i)->GetOffset();
    4243          94 :         if (dfOffset != 0.0)
    4244           1 :             poDS->GetRasterBand(i)->SetOffset(dfOffset);
    4245             : 
    4246          94 :         const double dfScale = poSrcDS->GetRasterBand(i)->GetScale();
    4247          94 :         if (dfScale != 1.0)
    4248           1 :             poDS->GetRasterBand(i)->SetScale(dfScale);
    4249             :     }
    4250             : 
    4251             :     // Do we need to remap nodata ?
    4252          69 :     int bHasNoData = FALSE;
    4253          69 :     poDS->m_dfSrcNoData =
    4254          69 :         poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHasNoData);
    4255          69 :     poDS->m_bHasSrcNoData = CPL_TO_BOOL(bHasNoData);
    4256             : 
    4257          69 :     if (poDS->m_bUseSrcLabel)
    4258             :     {
    4259          61 :         CSLConstList papszMD_ISIS3 = poSrcDS->GetMetadata("json:ISIS3");
    4260          61 :         if (papszMD_ISIS3 != nullptr)
    4261             :         {
    4262          21 :             poDS->SetMetadata(papszMD_ISIS3, "json:ISIS3");
    4263             :         }
    4264             :     }
    4265             : 
    4266             :     // We don't need to initialize the imagery as we are going to copy it
    4267             :     // completely
    4268          69 :     poDS->m_bInitToNodata = false;
    4269          69 :     CPLErr eErr = GDALDatasetCopyWholeRaster(poSrcDS, poDS, nullptr,
    4270             :                                              pfnProgress, pProgressData);
    4271          69 :     poDS->FlushCache(false);
    4272          69 :     poDS->m_bHasSrcNoData = false;
    4273          69 :     if (eErr != CE_None)
    4274             :     {
    4275          11 :         delete poDS;
    4276          11 :         return nullptr;
    4277             :     }
    4278             : 
    4279          58 :     return poDS;
    4280             : }
    4281             : 
    4282             : /************************************************************************/
    4283             : /*                         GDALRegister_ISIS3()                         */
    4284             : /************************************************************************/
    4285             : 
    4286        2138 : void GDALRegister_ISIS3()
    4287             : 
    4288             : {
    4289        2138 :     if (GDALGetDriverByName(ISIS3_DRIVER_NAME) != nullptr)
    4290         263 :         return;
    4291             : 
    4292        1875 :     GDALDriver *poDriver = new GDALDriver();
    4293        1875 :     ISIS3DriverSetCommonMetadata(poDriver);
    4294             : 
    4295        1875 :     poDriver->pfnOpen = ISIS3Dataset::Open;
    4296        1875 :     poDriver->pfnCreate = ISIS3Dataset::Create;
    4297        1875 :     poDriver->pfnCreateCopy = ISIS3Dataset::CreateCopy;
    4298             : 
    4299        1875 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    4300             : }

Generated by: LCOV version 1.14