• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

OSGeo / gdal / 15885686134

25 Jun 2025 07:44PM UTC coverage: 71.084%. Remained the same
15885686134

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

92.17
/frmts/pds/isis3dataset.cpp
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 "gdal_frmts.h"
28
#include "gdal_proxy.h"
29
#include "nasakeywordhandler.h"
30
#include "ogrgeojsonreader.h"
31
#include "ogr_spatialref.h"
32
#include "rawdataset.h"
33
#include "vrtdataset.h"
34
#include "cpl_safemaths.hpp"
35
#include "pdsdrivercore.h"
36
#include "json_utils.h"
37

38
// For gethostname()
39
#ifdef _WIN32
40
#include <winsock2.h>
41
#else
42
#include <unistd.h>
43
#endif
44

45
#include <algorithm>
46
#include <map>
47
#include <utility>  // pair
48
#include <vector>
49

50
// Constants coming from ISIS3 source code
51
// in isis/src/base/objs/SpecialPixel/SpecialPixel.h
52

53
// There are several types of special pixels
54
//    *   Isis::Null Pixel has no data available
55
//    *   Isis::Lis Pixel was saturated on the instrument
56
//    *   Isis::His Pixel was saturated on the instrument
57
//    *   Isis::Lrs Pixel was saturated during a computation
58
//    *   Isis::Hrs Pixel was saturated during a computation
59

60
// 1-byte special pixel values
61
const unsigned char ISIS3_NULL1 = 0;
62
const unsigned char LOW_REPR_SAT1 = 0;
63
const unsigned char LOW_INSTR_SAT1 = 0;
64
const unsigned char HIGH_INSTR_SAT1 = 255;
65
const unsigned char HIGH_REPR_SAT1 = 255;
66

67
// 2-byte unsigned special pixel values
68
const unsigned short ISIS3_NULLU2 = 0;
69
const unsigned short LOW_REPR_SATU2 = 1;
70
const unsigned short LOW_INSTR_SATU2 = 2;
71
const unsigned short HIGH_INSTR_SATU2 = 65534;
72
const unsigned short HIGH_REPR_SATU2 = 65535;
73

74
// 2-byte signed special pixel values
75
const short ISIS3_NULL2 = -32768;
76
const short LOW_REPR_SAT2 = -32767;
77
const short LOW_INSTR_SAT2 = -32766;
78
const short HIGH_INSTR_SAT2 = -32765;
79
const short HIGH_REPR_SAT2 = -32764;
80

81
// Define 4-byte special pixel values for IEEE floating point
82
const float ISIS3_NULL4 = -3.4028226550889045e+38f;      // 0xFF7FFFFB;
83
const float LOW_REPR_SAT4 = -3.4028228579130005e+38f;    // 0xFF7FFFFC;
84
const float LOW_INSTR_SAT4 = -3.4028230607370965e+38f;   // 0xFF7FFFFD;
85
const float HIGH_INSTR_SAT4 = -3.4028232635611926e+38f;  // 0xFF7FFFFE;
86
const float HIGH_REPR_SAT4 = -3.4028234663852886e+38f;   // 0xFF7FFFFF;
87

88
// Must be large enough to hold an integer
89
static const char *const pszSTARTBYTE_PLACEHOLDER = "!*^STARTBYTE^*!";
90
// Must be large enough to hold an integer
91
static const char *const pszLABEL_BYTES_PLACEHOLDER = "!*^LABEL_BYTES^*!";
92
// Must be large enough to hold an integer
93
static const char *const pszHISTORY_STARTBYTE_PLACEHOLDER =
94
    "!*^HISTORY_STARTBYTE^*!";
95

96
/************************************************************************/
97
/* ==================================================================== */
98
/*                             ISISDataset                              */
99
/* ==================================================================== */
100
/************************************************************************/
101

102
class ISIS3Dataset final : public RawDataset
103
{
104
    friend class ISIS3RawRasterBand;
105
    friend class ISISTiledBand;
106
    friend class ISIS3WrapperRasterBand;
107

108
    class NonPixelSection
109
    {
110
      public:
111
        CPLString osSrcFilename{};
112
        CPLString osDstFilename{};  // empty for same file
113
        vsi_l_offset nSrcOffset{};
114
        vsi_l_offset nSize{};
115
        CPLString osPlaceHolder{};  // empty if not same file
116
    };
117

118
    VSILFILE *m_fpLabel{};               // label file (only used for writing)
119
    VSILFILE *m_fpImage{};               // image data file. May be == fpLabel
120
    GDALDataset *m_poExternalDS{};       // external dataset (GeoTIFF)
121
    bool m_bGeoTIFFAsRegularExternal{};  // creation only
122
    bool m_bGeoTIFFInitDone{true};       // creation only
123

124
    CPLString m_osExternalFilename{};
125
    bool m_bIsLabelWritten{true};  // creation only
126

127
    bool m_bIsTiled{};
128
    bool m_bInitToNodata{};  // creation only
129

130
    NASAKeywordHandler m_oKeywords{};
131

132
    bool m_bGotTransform{};
133
    GDALGeoTransform m_gt{};
134

135
    bool m_bHasSrcNoData{};  // creation only
136
    double m_dfSrcNoData{};  // creation only
137

138
    OGRSpatialReference m_oSRS{};
139

140
    // creation only variables
141
    CPLString m_osComment{};
142
    CPLString m_osLatitudeType{};
143
    CPLString m_osLongitudeDirection{};
144
    CPLString m_osTargetName{};
145
    bool m_bForce360{};
146
    bool m_bWriteBoundingDegrees{true};
147
    CPLString m_osBoundingDegrees{};
148

149
    CPLJSONObject m_oJSonLabel{};
150
    CPLString m_osHistory{};                              // creation only
151
    bool m_bUseSrcLabel{true};                            // creation only
152
    bool m_bUseSrcMapping{};                              // creation only
153
    bool m_bUseSrcHistory{true};                          // creation only
154
    bool m_bAddGDALHistory{true};                         // creation only
155
    CPLString m_osGDALHistory{};                          // creation only
156
    std::vector<NonPixelSection> m_aoNonPixelSections{};  // creation only
157
    CPLJSONObject m_oSrcJSonLabel{};                      // creation only
158
    CPLStringList m_aosISIS3MD{};
159
    CPLStringList m_aosAdditionalFiles{};
160
    CPLString m_osFromFilename{};  // creation only
161

162
    RawBinaryLayout m_sLayout{};
163

164
    const char *GetKeyword(const char *pszPath, const char *pszDefault = "");
165

166
    double FixLong(double dfLong);
167
    void BuildLabel();
168
    void BuildHistory();
169
    void WriteLabel();
170
    void InvalidateLabel();
171

172
    static CPLString SerializeAsPDL(const CPLJSONObject &oObj);
173
    static void SerializeAsPDL(VSILFILE *fp, const CPLJSONObject &oObj,
174
                               int nDepth = 0);
175

176
    CPL_DISALLOW_COPY_ASSIGN(ISIS3Dataset)
177

178
  protected:
179
    CPLErr Close() override;
180

181
  public:
182
    ISIS3Dataset();
183
    virtual ~ISIS3Dataset();
184

185
    virtual int CloseDependentDatasets() override;
186

187
    virtual CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
188
    virtual CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
189

190
    const OGRSpatialReference *GetSpatialRef() const override;
191
    CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
192

193
    virtual char **GetFileList() override;
194

195
    virtual char **GetMetadataDomainList() override;
196
    virtual char **GetMetadata(const char *pszDomain = "") override;
197
    virtual CPLErr SetMetadata(char **papszMD,
198
                               const char *pszDomain = "") override;
199

200
    bool GetRawBinaryLayout(GDALDataset::RawBinaryLayout &) override;
201

202
    static GDALDataset *Open(GDALOpenInfo *);
203
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
204
                               int nBandsIn, GDALDataType eType,
205
                               char **papszOptions);
206
    static GDALDataset *CreateCopy(const char *pszFilename,
207
                                   GDALDataset *poSrcDS, int bStrict,
208
                                   char **papszOptions,
209
                                   GDALProgressFunc pfnProgress,
210
                                   void *pProgressData);
211
};
212

213
/************************************************************************/
214
/* ==================================================================== */
215
/*                             ISISTiledBand                            */
216
/* ==================================================================== */
217
/************************************************************************/
218

219
class ISISTiledBand final : public GDALPamRasterBand
220
{
221
    friend class ISIS3Dataset;
222

223
    VSILFILE *const m_fpVSIL{};
224
    GIntBig m_nFirstTileOffset{};
225
    GIntBig m_nXTileOffset{};
226
    GIntBig m_nYTileOffset{};
227
    const bool m_bNativeOrder{};
228
    bool m_bHasOffset{};
229
    bool m_bHasScale{};
230
    double m_dfOffset{};
231
    double m_dfScale{1.0};
232
    double m_dfNoData{};
233
    bool m_bValid = false;
234

235
    CPL_DISALLOW_COPY_ASSIGN(ISISTiledBand)
236

237
  public:
238
    ISISTiledBand(GDALDataset *poDS, VSILFILE *fpVSIL, int nBand,
239
                  GDALDataType eDT, int nTileXSize, int nTileYSize,
240
                  GIntBig nFirstTileOffset, GIntBig nXTileOffset,
241
                  GIntBig nYTileOffset, int bNativeOrder);
242

243
    virtual ~ISISTiledBand()
102✔
244
    {
51✔
245
    }
102✔
246

247
    bool IsValid() const
41✔
248
    {
249
        return m_bValid;
41✔
250
    }
251

252
    virtual CPLErr IReadBlock(int, int, void *) override;
253
    virtual CPLErr IWriteBlock(int, int, void *) override;
254

255
    virtual double GetOffset(int *pbSuccess = nullptr) override;
256
    virtual double GetScale(int *pbSuccess = nullptr) override;
257
    virtual CPLErr SetOffset(double dfNewOffset) override;
258
    virtual CPLErr SetScale(double dfNewScale) override;
259
    virtual double GetNoDataValue(int *pbSuccess = nullptr) override;
260
    virtual CPLErr SetNoDataValue(double dfNewNoData) override;
261

262
    void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
263
};
264

265
/************************************************************************/
266
/* ==================================================================== */
267
/*                        ISIS3RawRasterBand                            */
268
/* ==================================================================== */
269
/************************************************************************/
270

271
class ISIS3RawRasterBand final : public RawRasterBand
272
{
273
    friend class ISIS3Dataset;
274

275
    bool m_bHasOffset;
276
    bool m_bHasScale;
277
    double m_dfOffset;
278
    double m_dfScale{1.0};
279
    double m_dfNoData;
280

281
  public:
282
    ISIS3RawRasterBand(GDALDataset *l_poDS, int l_nBand, VSILFILE *l_fpRaw,
283
                       vsi_l_offset l_nImgOffset, int l_nPixelOffset,
284
                       int l_nLineOffset, GDALDataType l_eDataType,
285
                       int l_bNativeOrder);
286

287
    virtual ~ISIS3RawRasterBand()
732✔
288
    {
366✔
289
    }
732✔
290

291
    virtual CPLErr IReadBlock(int, int, void *) override;
292
    virtual CPLErr IWriteBlock(int, int, void *) override;
293

294
    virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
295
                             GDALDataType, GSpacing nPixelSpace,
296
                             GSpacing nLineSpace,
297
                             GDALRasterIOExtraArg *psExtraArg) override;
298

299
    virtual double GetOffset(int *pbSuccess = nullptr) override;
300
    virtual double GetScale(int *pbSuccess = nullptr) override;
301
    virtual CPLErr SetOffset(double dfNewOffset) override;
302
    virtual CPLErr SetScale(double dfNewScale) override;
303
    virtual double GetNoDataValue(int *pbSuccess = nullptr) override;
304
    virtual CPLErr SetNoDataValue(double dfNewNoData) override;
305

306
    void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
307
};
308

309
/************************************************************************/
310
/* ==================================================================== */
311
/*                         ISIS3WrapperRasterBand                       */
312
/*                                                                      */
313
/*      proxy for bands stored in other formats.                        */
314
/* ==================================================================== */
315
/************************************************************************/
316
class ISIS3WrapperRasterBand final : public GDALProxyRasterBand
317
{
318
    friend class ISIS3Dataset;
319

320
    GDALRasterBand *m_poBaseBand{};
321
    bool m_bHasOffset{};
322
    bool m_bHasScale{};
323
    double m_dfOffset{};
324
    double m_dfScale{1.0};
325
    double m_dfNoData{};
326

327
    CPL_DISALLOW_COPY_ASSIGN(ISIS3WrapperRasterBand)
328

329
  protected:
330
    virtual GDALRasterBand *
331
    RefUnderlyingRasterBand(bool /* bForceOpen */) const override
127✔
332
    {
333
        return m_poBaseBand;
127✔
334
    }
335

336
  public:
337
    explicit ISIS3WrapperRasterBand(GDALRasterBand *poBaseBandIn);
338

339
    void InitFile();
340

341
    virtual CPLErr Fill(double dfRealValue,
342
                        double dfImaginaryValue = 0) override;
343
    virtual CPLErr IWriteBlock(int, int, void *) override;
344

345
    virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
346
                             GDALDataType, GSpacing nPixelSpace,
347
                             GSpacing nLineSpace,
348
                             GDALRasterIOExtraArg *psExtraArg) override;
349

350
    virtual double GetOffset(int *pbSuccess = nullptr) override;
351
    virtual double GetScale(int *pbSuccess = nullptr) override;
352
    virtual CPLErr SetOffset(double dfNewOffset) override;
353
    virtual CPLErr SetScale(double dfNewScale) override;
354
    virtual double GetNoDataValue(int *pbSuccess = nullptr) override;
355
    virtual CPLErr SetNoDataValue(double dfNewNoData) override;
356

357
    int GetMaskFlags() override
4✔
358
    {
359
        return nMaskFlags;
4✔
360
    }
361

362
    GDALRasterBand *GetMaskBand() override
4✔
363
    {
364
        return poMask;
4✔
365
    }
366

367
    void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
368
};
369

370
/************************************************************************/
371
/* ==================================================================== */
372
/*                             ISISMaskBand                             */
373
/* ==================================================================== */
374

375
class ISISMaskBand final : public GDALRasterBand
376
{
377
    GDALRasterBand *m_poBaseBand{};
378
    void *m_pBuffer{};
379

380
    CPL_DISALLOW_COPY_ASSIGN(ISISMaskBand)
381

382
  public:
383
    explicit ISISMaskBand(GDALRasterBand *poBaseBand);
384
    ~ISISMaskBand();
385

386
    virtual CPLErr IReadBlock(int, int, void *) override;
387
};
388

389
/************************************************************************/
390
/*                           ISISTiledBand()                            */
391
/************************************************************************/
392

393
ISISTiledBand::ISISTiledBand(GDALDataset *poDSIn, VSILFILE *fpVSILIn,
51✔
394
                             int nBandIn, GDALDataType eDT, int nTileXSize,
395
                             int nTileYSize, GIntBig nFirstTileOffsetIn,
396
                             GIntBig nXTileOffsetIn, GIntBig nYTileOffsetIn,
397
                             int bNativeOrderIn)
51✔
398
    : m_fpVSIL(fpVSILIn), m_nXTileOffset(nXTileOffsetIn),
399
      m_nYTileOffset(nYTileOffsetIn), m_bNativeOrder(bNativeOrderIn)
51✔
400
{
401
    poDS = poDSIn;
51✔
402
    nBand = nBandIn;
51✔
403
    eDataType = eDT;
51✔
404
    nBlockXSize = nTileXSize;
51✔
405
    nBlockYSize = nTileYSize;
51✔
406
    nRasterXSize = poDSIn->GetRasterXSize();
51✔
407
    nRasterYSize = poDSIn->GetRasterYSize();
51✔
408

409
    const int l_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
51✔
410
    const int l_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
51✔
411

412
    if (m_nXTileOffset == 0 && m_nYTileOffset == 0)
51✔
413
    {
414
        m_nXTileOffset =
51✔
415
            static_cast<GIntBig>(GDALGetDataTypeSizeBytes(eDT)) * nTileXSize;
51✔
416
        if (m_nXTileOffset > GINTBIG_MAX / nTileYSize)
51✔
417
        {
418
            CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
×
419
            return;
×
420
        }
421
        m_nXTileOffset *= nTileYSize;
51✔
422

423
        if (m_nXTileOffset > GINTBIG_MAX / l_nBlocksPerRow)
51✔
424
        {
425
            CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
×
426
            return;
×
427
        }
428
        m_nYTileOffset = m_nXTileOffset * l_nBlocksPerRow;
51✔
429
    }
430

431
    m_nFirstTileOffset = nFirstTileOffsetIn;
51✔
432
    if (nBand > 1)
51✔
433
    {
434
        if (m_nYTileOffset > GINTBIG_MAX / (nBand - 1) ||
19✔
435
            (nBand - 1) * m_nYTileOffset > GINTBIG_MAX / l_nBlocksPerColumn ||
19✔
436
            m_nFirstTileOffset >
19✔
437
                GINTBIG_MAX - (nBand - 1) * m_nYTileOffset * l_nBlocksPerColumn)
19✔
438
        {
439
            CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
×
440
            return;
×
441
        }
442
        m_nFirstTileOffset += (nBand - 1) * m_nYTileOffset * l_nBlocksPerColumn;
19✔
443
    }
444
    m_bValid = true;
51✔
445
}
446

447
/************************************************************************/
448
/*                             IReadBlock()                             */
449
/************************************************************************/
450

451
CPLErr ISISTiledBand::IReadBlock(int nXBlock, int nYBlock, void *pImage)
476✔
452

453
{
454
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
476✔
455
    if (poGDS->m_osExternalFilename.empty())
476✔
456
    {
457
        if (!poGDS->m_bIsLabelWritten)
232✔
458
            poGDS->WriteLabel();
2✔
459
    }
460

461
    const GIntBig nOffset = m_nFirstTileOffset + nXBlock * m_nXTileOffset +
476✔
462
                            nYBlock * m_nYTileOffset;
476✔
463
    const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
476✔
464
    const size_t nBlockSize =
476✔
465
        static_cast<size_t>(nDTSize) * nBlockXSize * nBlockYSize;
476✔
466

467
    if (VSIFSeekL(m_fpVSIL, nOffset, SEEK_SET) != 0)
476✔
468
    {
469
        CPLError(CE_Failure, CPLE_FileIO,
×
470
                 "Failed to seek to offset %d to read tile %d,%d.",
471
                 static_cast<int>(nOffset), nXBlock, nYBlock);
472
        return CE_Failure;
×
473
    }
474

475
    if (VSIFReadL(pImage, 1, nBlockSize, m_fpVSIL) != nBlockSize)
476✔
476
    {
477
        CPLError(CE_Failure, CPLE_FileIO,
×
478
                 "Failed to read %d bytes for tile %d,%d.",
479
                 static_cast<int>(nBlockSize), nXBlock, nYBlock);
480
        return CE_Failure;
×
481
    }
482

483
    if (!m_bNativeOrder && eDataType != GDT_Byte)
476✔
484
        GDALSwapWords(pImage, nDTSize, nBlockXSize * nBlockYSize, nDTSize);
×
485

486
    return CE_None;
476✔
487
}
488

489
/************************************************************************/
490
/*                           RemapNoDataT()                             */
491
/************************************************************************/
492

493
template <class T>
494
static void RemapNoDataT(T *pBuffer, int nItems, T srcNoData, T dstNoData)
6✔
495
{
496
    for (int i = 0; i < nItems; i++)
67,542✔
497
    {
498
        if (pBuffer[i] == srcNoData)
67,536✔
499
            pBuffer[i] = dstNoData;
6✔
500
    }
501
}
6✔
502

503
/************************************************************************/
504
/*                            RemapNoData()                             */
505
/************************************************************************/
506

507
static void RemapNoData(GDALDataType eDataType, void *pBuffer, int nItems,
6✔
508
                        double dfSrcNoData, double dfDstNoData)
509
{
510
    if (eDataType == GDT_Byte)
6✔
511
    {
512
        RemapNoDataT(reinterpret_cast<GByte *>(pBuffer), nItems,
3✔
513
                     static_cast<GByte>(dfSrcNoData),
3✔
514
                     static_cast<GByte>(dfDstNoData));
3✔
515
    }
516
    else if (eDataType == GDT_UInt16)
3✔
517
    {
518
        RemapNoDataT(reinterpret_cast<GUInt16 *>(pBuffer), nItems,
1✔
519
                     static_cast<GUInt16>(dfSrcNoData),
1✔
520
                     static_cast<GUInt16>(dfDstNoData));
1✔
521
    }
522
    else if (eDataType == GDT_Int16)
2✔
523
    {
524
        RemapNoDataT(reinterpret_cast<GInt16 *>(pBuffer), nItems,
1✔
525
                     static_cast<GInt16>(dfSrcNoData),
1✔
526
                     static_cast<GInt16>(dfDstNoData));
1✔
527
    }
528
    else
529
    {
530
        CPLAssert(eDataType == GDT_Float32);
1✔
531
        RemapNoDataT(reinterpret_cast<float *>(pBuffer), nItems,
1✔
532
                     static_cast<float>(dfSrcNoData),
533
                     static_cast<float>(dfDstNoData));
534
    }
535
}
6✔
536

537
/************************************************************************/
538
/*                             IReadBlock()                             */
539
/************************************************************************/
540

541
CPLErr ISISTiledBand::IWriteBlock(int nXBlock, int nYBlock, void *pImage)
225✔
542

543
{
544
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
225✔
545
    if (poGDS->m_osExternalFilename.empty())
225✔
546
    {
547
        if (!poGDS->m_bIsLabelWritten)
223✔
548
            poGDS->WriteLabel();
2✔
549
    }
550

551
    if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData != m_dfNoData)
225✔
552
    {
553
        RemapNoData(eDataType, pImage, nBlockXSize * nBlockYSize,
1✔
554
                    poGDS->m_dfSrcNoData, m_dfNoData);
555
    }
556

557
    const GIntBig nOffset = m_nFirstTileOffset + nXBlock * m_nXTileOffset +
225✔
558
                            nYBlock * m_nYTileOffset;
225✔
559
    const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
225✔
560
    const size_t nBlockSize =
225✔
561
        static_cast<size_t>(nDTSize) * nBlockXSize * nBlockYSize;
225✔
562

563
    const int l_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
225✔
564
    const int l_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
225✔
565

566
    // Pad partial blocks to nodata value
567
    if (nXBlock == l_nBlocksPerRow - 1 && (nRasterXSize % nBlockXSize) != 0)
225✔
568
    {
569
        GByte *pabyImage = static_cast<GByte *>(pImage);
24✔
570
        int nXStart = nRasterXSize % nBlockXSize;
24✔
571
        for (int iY = 0; iY < nBlockYSize; iY++)
1,688✔
572
        {
573
            GDALCopyWords(&m_dfNoData, GDT_Float64, 0,
1,664✔
574
                          pabyImage + (iY * nBlockXSize + nXStart) * nDTSize,
1,664✔
575
                          eDataType, nDTSize, nBlockXSize - nXStart);
1,664✔
576
        }
577
    }
578
    if (nYBlock == l_nBlocksPerColumn - 1 && (nRasterYSize % nBlockYSize) != 0)
225✔
579
    {
580
        GByte *pabyImage = static_cast<GByte *>(pImage);
49✔
581
        for (int iY = nRasterYSize % nBlockYSize; iY < nBlockYSize; iY++)
1,685✔
582
        {
583
            GDALCopyWords(&m_dfNoData, GDT_Float64, 0,
1,636✔
584
                          pabyImage + iY * nBlockXSize * nDTSize, eDataType,
1,636✔
585
                          nDTSize, nBlockXSize);
586
        }
587
    }
588

589
    if (VSIFSeekL(m_fpVSIL, nOffset, SEEK_SET) != 0)
225✔
590
    {
591
        CPLError(CE_Failure, CPLE_FileIO,
×
592
                 "Failed to seek to offset %d to read tile %d,%d.",
593
                 static_cast<int>(nOffset), nXBlock, nYBlock);
594
        return CE_Failure;
×
595
    }
596

597
    if (!m_bNativeOrder && eDataType != GDT_Byte)
225✔
598
        GDALSwapWords(pImage, nDTSize, nBlockXSize * nBlockYSize, nDTSize);
×
599

600
    if (VSIFWriteL(pImage, 1, nBlockSize, m_fpVSIL) != nBlockSize)
225✔
601
    {
602
        CPLError(CE_Failure, CPLE_FileIO,
×
603
                 "Failed to write %d bytes for tile %d,%d.",
604
                 static_cast<int>(nBlockSize), nXBlock, nYBlock);
605
        return CE_Failure;
×
606
    }
607

608
    if (!m_bNativeOrder && eDataType != GDT_Byte)
225✔
609
        GDALSwapWords(pImage, nDTSize, nBlockXSize * nBlockYSize, nDTSize);
×
610

611
    return CE_None;
225✔
612
}
613

614
/************************************************************************/
615
/*                             SetMaskBand()                            */
616
/************************************************************************/
617

618
void ISISTiledBand::SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand)
41✔
619
{
620
    poMask.reset(std::move(poMaskBand));
41✔
621
    nMaskFlags = 0;
41✔
622
}
41✔
623

624
/************************************************************************/
625
/*                              GetOffset()                             */
626
/************************************************************************/
627

628
double ISISTiledBand::GetOffset(int *pbSuccess)
14✔
629
{
630
    if (pbSuccess)
14✔
631
        *pbSuccess = m_bHasOffset;
4✔
632
    return m_dfOffset;
14✔
633
}
634

635
/************************************************************************/
636
/*                              GetScale()                              */
637
/************************************************************************/
638

639
double ISISTiledBand::GetScale(int *pbSuccess)
14✔
640
{
641
    if (pbSuccess)
14✔
642
        *pbSuccess = m_bHasScale;
4✔
643
    return m_dfScale;
14✔
644
}
645

646
/************************************************************************/
647
/*                              SetOffset()                             */
648
/************************************************************************/
649

650
CPLErr ISISTiledBand::SetOffset(double dfNewOffset)
14✔
651
{
652
    m_dfOffset = dfNewOffset;
14✔
653
    m_bHasOffset = true;
14✔
654
    return CE_None;
14✔
655
}
656

657
/************************************************************************/
658
/*                              SetScale()                              */
659
/************************************************************************/
660

661
CPLErr ISISTiledBand::SetScale(double dfNewScale)
14✔
662
{
663
    m_dfScale = dfNewScale;
14✔
664
    m_bHasScale = true;
14✔
665
    return CE_None;
14✔
666
}
667

668
/************************************************************************/
669
/*                           GetNoDataValue()                           */
670
/************************************************************************/
671

672
double ISISTiledBand::GetNoDataValue(int *pbSuccess)
12✔
673
{
674
    if (pbSuccess)
12✔
675
        *pbSuccess = true;
8✔
676
    return m_dfNoData;
12✔
677
}
678

679
/************************************************************************/
680
/*                           SetNoDataValue()                           */
681
/************************************************************************/
682

683
CPLErr ISISTiledBand::SetNoDataValue(double dfNewNoData)
51✔
684
{
685
    m_dfNoData = dfNewNoData;
51✔
686
    return CE_None;
51✔
687
}
688

689
/************************************************************************/
690
/*                       ISIS3RawRasterBand()                           */
691
/************************************************************************/
692

693
ISIS3RawRasterBand::ISIS3RawRasterBand(GDALDataset *l_poDS, int l_nBand,
366✔
694
                                       VSILFILE *l_fpRaw,
695
                                       vsi_l_offset l_nImgOffset,
696
                                       int l_nPixelOffset, int l_nLineOffset,
697
                                       GDALDataType l_eDataType,
698
                                       int l_bNativeOrder)
366✔
699
    : RawRasterBand(l_poDS, l_nBand, l_fpRaw, l_nImgOffset, l_nPixelOffset,
700
                    l_nLineOffset, l_eDataType, l_bNativeOrder,
701
                    RawRasterBand::OwnFP::NO),
702
      m_bHasOffset(false), m_bHasScale(false), m_dfOffset(0.0), m_dfScale(1.0),
703
      m_dfNoData(0.0)
366✔
704
{
705
}
366✔
706

707
/************************************************************************/
708
/*                             IReadBlock()                             */
709
/************************************************************************/
710

711
CPLErr ISIS3RawRasterBand::IReadBlock(int nXBlock, int nYBlock, void *pImage)
2,071✔
712

713
{
714
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
2,071✔
715
    if (poGDS->m_osExternalFilename.empty())
2,071✔
716
    {
717
        if (!poGDS->m_bIsLabelWritten)
930✔
718
            poGDS->WriteLabel();
×
719
    }
720
    return RawRasterBand::IReadBlock(nXBlock, nYBlock, pImage);
2,071✔
721
}
722

723
/************************************************************************/
724
/*                            IWriteBlock()                             */
725
/************************************************************************/
726

727
CPLErr ISIS3RawRasterBand::IWriteBlock(int nXBlock, int nYBlock, void *pImage)
856✔
728

729
{
730
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
856✔
731
    if (poGDS->m_osExternalFilename.empty())
856✔
732
    {
733
        if (!poGDS->m_bIsLabelWritten)
796✔
734
            poGDS->WriteLabel();
×
735
    }
736

737
    if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData != m_dfNoData)
856✔
738
    {
739
        RemapNoData(eDataType, pImage, nBlockXSize * nBlockYSize,
×
740
                    poGDS->m_dfSrcNoData, m_dfNoData);
741
    }
742

743
    return RawRasterBand::IWriteBlock(nXBlock, nYBlock, pImage);
856✔
744
}
745

746
/************************************************************************/
747
/*                             IRasterIO()                              */
748
/************************************************************************/
749

750
CPLErr ISIS3RawRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
1,939✔
751
                                     int nXSize, int nYSize, void *pData,
752
                                     int nBufXSize, int nBufYSize,
753
                                     GDALDataType eBufType,
754
                                     GSpacing nPixelSpace, GSpacing nLineSpace,
755
                                     GDALRasterIOExtraArg *psExtraArg)
756

757
{
758
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
1,939✔
759
    if (poGDS->m_osExternalFilename.empty())
1,939✔
760
    {
761
        if (!poGDS->m_bIsLabelWritten)
1,136✔
762
            poGDS->WriteLabel();
49✔
763
    }
764
    if (eRWFlag == GF_Write && poGDS->m_bHasSrcNoData &&
1,939✔
765
        poGDS->m_dfSrcNoData != m_dfNoData)
27✔
766
    {
767
        const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
4✔
768
        if (eBufType == eDataType && nPixelSpace == nDTSize &&
4✔
769
            nLineSpace == nPixelSpace * nBufXSize)
4✔
770
        {
771
            RemapNoData(eDataType, pData, nBufXSize * nBufYSize,
4✔
772
                        poGDS->m_dfSrcNoData, m_dfNoData);
773
        }
774
        else
775
        {
776
            const GByte *pabySrc = reinterpret_cast<GByte *>(pData);
×
777
            GByte *pabyTemp = reinterpret_cast<GByte *>(
778
                VSI_MALLOC3_VERBOSE(nDTSize, nBufXSize, nBufYSize));
×
779
            for (int i = 0; i < nBufYSize; i++)
×
780
            {
781
                GDALCopyWords(pabySrc + i * nLineSpace, eBufType,
×
782
                              static_cast<int>(nPixelSpace),
783
                              pabyTemp + i * nBufXSize * nDTSize, eDataType,
×
784
                              nDTSize, nBufXSize);
785
            }
786
            RemapNoData(eDataType, pabyTemp, nBufXSize * nBufYSize,
×
787
                        poGDS->m_dfSrcNoData, m_dfNoData);
788
            CPLErr eErr = RawRasterBand::IRasterIO(
×
789
                eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nBufXSize,
790
                nBufYSize, eDataType, nDTSize,
791
                static_cast<GSpacing>(nDTSize) * nBufXSize, psExtraArg);
×
792
            VSIFree(pabyTemp);
×
793
            return eErr;
×
794
        }
795
    }
796
    return RawRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
1,939✔
797
                                    pData, nBufXSize, nBufYSize, eBufType,
798
                                    nPixelSpace, nLineSpace, psExtraArg);
1,939✔
799
}
800

801
/************************************************************************/
802
/*                             SetMaskBand()                            */
803
/************************************************************************/
804

805
void ISIS3RawRasterBand::SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand)
234✔
806
{
807
    poMask.reset(std::move(poMaskBand));
234✔
808
    nMaskFlags = 0;
234✔
809
}
234✔
810

811
/************************************************************************/
812
/*                              GetOffset()                             */
813
/************************************************************************/
814

815
double ISIS3RawRasterBand::GetOffset(int *pbSuccess)
157✔
816
{
817
    if (pbSuccess)
157✔
818
        *pbSuccess = m_bHasOffset;
24✔
819
    return m_dfOffset;
157✔
820
}
821

822
/************************************************************************/
823
/*                              GetScale()                              */
824
/************************************************************************/
825

826
double ISIS3RawRasterBand::GetScale(int *pbSuccess)
157✔
827
{
828
    if (pbSuccess)
157✔
829
        *pbSuccess = m_bHasScale;
24✔
830
    return m_dfScale;
157✔
831
}
832

833
/************************************************************************/
834
/*                              SetOffset()                             */
835
/************************************************************************/
836

837
CPLErr ISIS3RawRasterBand::SetOffset(double dfNewOffset)
51✔
838
{
839
    m_dfOffset = dfNewOffset;
51✔
840
    m_bHasOffset = true;
51✔
841
    return CE_None;
51✔
842
}
843

844
/************************************************************************/
845
/*                              SetScale()                              */
846
/************************************************************************/
847

848
CPLErr ISIS3RawRasterBand::SetScale(double dfNewScale)
51✔
849
{
850
    m_dfScale = dfNewScale;
51✔
851
    m_bHasScale = true;
51✔
852
    return CE_None;
51✔
853
}
854

855
/************************************************************************/
856
/*                           GetNoDataValue()                           */
857
/************************************************************************/
858

859
double ISIS3RawRasterBand::GetNoDataValue(int *pbSuccess)
138✔
860
{
861
    if (pbSuccess)
138✔
862
        *pbSuccess = true;
87✔
863
    return m_dfNoData;
138✔
864
}
865

866
/************************************************************************/
867
/*                           SetNoDataValue()                           */
868
/************************************************************************/
869

870
CPLErr ISIS3RawRasterBand::SetNoDataValue(double dfNewNoData)
367✔
871
{
872
    m_dfNoData = dfNewNoData;
367✔
873
    return CE_None;
367✔
874
}
875

876
/************************************************************************/
877
/*                        ISIS3WrapperRasterBand()                      */
878
/************************************************************************/
879

880
ISIS3WrapperRasterBand::ISIS3WrapperRasterBand(GDALRasterBand *poBaseBandIn)
45✔
881
    : m_poBaseBand(poBaseBandIn)
45✔
882
{
883
    eDataType = m_poBaseBand->GetRasterDataType();
45✔
884
    m_poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
45✔
885
}
45✔
886

887
/************************************************************************/
888
/*                             SetMaskBand()                            */
889
/************************************************************************/
890

891
void ISIS3WrapperRasterBand::SetMaskBand(
21✔
892
    std::unique_ptr<GDALRasterBand> poMaskBand)
893
{
894
    poMask.reset(std::move(poMaskBand));
21✔
895
    nMaskFlags = 0;
21✔
896
}
21✔
897

898
/************************************************************************/
899
/*                              GetOffset()                             */
900
/************************************************************************/
901

902
double ISIS3WrapperRasterBand::GetOffset(int *pbSuccess)
22✔
903
{
904
    if (pbSuccess)
22✔
905
        *pbSuccess = m_bHasOffset;
4✔
906
    return m_dfOffset;
22✔
907
}
908

909
/************************************************************************/
910
/*                              GetScale()                              */
911
/************************************************************************/
912

913
double ISIS3WrapperRasterBand::GetScale(int *pbSuccess)
22✔
914
{
915
    if (pbSuccess)
22✔
916
        *pbSuccess = m_bHasScale;
4✔
917
    return m_dfScale;
22✔
918
}
919

920
/************************************************************************/
921
/*                              SetOffset()                             */
922
/************************************************************************/
923

924
CPLErr ISIS3WrapperRasterBand::SetOffset(double dfNewOffset)
12✔
925
{
926
    m_dfOffset = dfNewOffset;
12✔
927
    m_bHasOffset = true;
12✔
928

929
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
12✔
930
    if (poGDS->m_poExternalDS && eAccess == GA_Update)
12✔
931
        poGDS->m_poExternalDS->GetRasterBand(nBand)->SetOffset(dfNewOffset);
4✔
932

933
    return CE_None;
12✔
934
}
935

936
/************************************************************************/
937
/*                              SetScale()                              */
938
/************************************************************************/
939

940
CPLErr ISIS3WrapperRasterBand::SetScale(double dfNewScale)
12✔
941
{
942
    m_dfScale = dfNewScale;
12✔
943
    m_bHasScale = true;
12✔
944

945
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
12✔
946
    if (poGDS->m_poExternalDS && eAccess == GA_Update)
12✔
947
        poGDS->m_poExternalDS->GetRasterBand(nBand)->SetScale(dfNewScale);
4✔
948

949
    return CE_None;
12✔
950
}
951

952
/************************************************************************/
953
/*                           GetNoDataValue()                           */
954
/************************************************************************/
955

956
double ISIS3WrapperRasterBand::GetNoDataValue(int *pbSuccess)
4✔
957
{
958
    if (pbSuccess)
4✔
959
        *pbSuccess = true;
4✔
960
    return m_dfNoData;
4✔
961
}
962

963
/************************************************************************/
964
/*                           SetNoDataValue()                           */
965
/************************************************************************/
966

967
CPLErr ISIS3WrapperRasterBand::SetNoDataValue(double dfNewNoData)
45✔
968
{
969
    m_dfNoData = dfNewNoData;
45✔
970

971
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
45✔
972
    if (poGDS->m_poExternalDS && eAccess == GA_Update)
45✔
973
        poGDS->m_poExternalDS->GetRasterBand(nBand)->SetNoDataValue(
25✔
974
            dfNewNoData);
25✔
975

976
    return CE_None;
45✔
977
}
978

979
/************************************************************************/
980
/*                              InitFile()                              */
981
/************************************************************************/
982

983
void ISIS3WrapperRasterBand::InitFile()
8✔
984
{
985
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
8✔
986
    if (poGDS->m_bGeoTIFFAsRegularExternal && !poGDS->m_bGeoTIFFInitDone)
8✔
987
    {
988
        poGDS->m_bGeoTIFFInitDone = true;
8✔
989

990
        const int nBands = poGDS->GetRasterCount();
8✔
991
        // We need to make sure that blocks are written in the right order
992
        for (int i = 0; i < nBands; i++)
22✔
993
        {
994
            poGDS->m_poExternalDS->GetRasterBand(i + 1)->Fill(m_dfNoData);
14✔
995
        }
996
        poGDS->m_poExternalDS->FlushCache(false);
8✔
997

998
        // Check that blocks are effectively written in expected order.
999
        const int nBlockSizeBytes =
1000
            nBlockXSize * nBlockYSize * GDALGetDataTypeSizeBytes(eDataType);
8✔
1001

1002
        GIntBig nLastOffset = 0;
8✔
1003
        bool bGoOn = true;
8✔
1004
        const int l_nBlocksPerRow = DIV_ROUND_UP(nRasterXSize, nBlockXSize);
8✔
1005
        const int l_nBlocksPerColumn = DIV_ROUND_UP(nRasterYSize, nBlockYSize);
8✔
1006
        for (int i = 0; i < nBands && bGoOn; i++)
22✔
1007
        {
1008
            for (int y = 0; y < l_nBlocksPerColumn && bGoOn; y++)
641✔
1009
            {
1010
                for (int x = 0; x < l_nBlocksPerRow && bGoOn; x++)
1,473✔
1011
                {
1012
                    const char *pszBlockOffset =
1013
                        poGDS->m_poExternalDS->GetRasterBand(i + 1)
846✔
1014
                            ->GetMetadataItem(
846✔
1015
                                CPLSPrintf("BLOCK_OFFSET_%d_%d", x, y), "TIFF");
846✔
1016
                    if (pszBlockOffset)
846✔
1017
                    {
1018
                        GIntBig nOffset = CPLAtoGIntBig(pszBlockOffset);
846✔
1019
                        if (i != 0 || x != 0 || y != 0)
846✔
1020
                        {
1021
                            if (nOffset != nLastOffset + nBlockSizeBytes)
838✔
1022
                            {
1023
                                CPLError(CE_Warning, CPLE_AppDefined,
×
1024
                                         "Block %d,%d band %d not at expected "
1025
                                         "offset",
1026
                                         x, y, i + 1);
1027
                                bGoOn = false;
×
1028
                                poGDS->m_bGeoTIFFAsRegularExternal = false;
×
1029
                            }
1030
                        }
1031
                        nLastOffset = nOffset;
846✔
1032
                    }
1033
                    else
1034
                    {
1035
                        CPLError(CE_Warning, CPLE_AppDefined,
×
1036
                                 "Block %d,%d band %d not at expected "
1037
                                 "offset",
1038
                                 x, y, i + 1);
1039
                        bGoOn = false;
×
1040
                        poGDS->m_bGeoTIFFAsRegularExternal = false;
×
1041
                    }
1042
                }
1043
            }
1044
        }
1045
    }
1046
}
8✔
1047

1048
/************************************************************************/
1049
/*                               Fill()                                 */
1050
/************************************************************************/
1051

1052
CPLErr ISIS3WrapperRasterBand::Fill(double dfRealValue, double dfImaginaryValue)
4✔
1053
{
1054
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
4✔
1055
    if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData == dfRealValue)
4✔
1056
    {
1057
        dfRealValue = m_dfNoData;
×
1058
    }
1059
    if (poGDS->m_bGeoTIFFAsRegularExternal && !poGDS->m_bGeoTIFFInitDone)
4✔
1060
    {
1061
        InitFile();
1✔
1062
    }
1063

1064
    return GDALProxyRasterBand::Fill(dfRealValue, dfImaginaryValue);
4✔
1065
}
1066

1067
/************************************************************************/
1068
/*                             IWriteBlock()                             */
1069
/************************************************************************/
1070

1071
CPLErr ISIS3WrapperRasterBand::IWriteBlock(int nXBlock, int nYBlock,
×
1072
                                           void *pImage)
1073

1074
{
1075
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
×
1076
    if (poGDS->m_bHasSrcNoData && poGDS->m_dfSrcNoData != m_dfNoData)
×
1077
    {
1078
        RemapNoData(eDataType, pImage, nBlockXSize * nBlockYSize,
×
1079
                    poGDS->m_dfSrcNoData, m_dfNoData);
1080
    }
1081
    if (poGDS->m_bGeoTIFFAsRegularExternal && !poGDS->m_bGeoTIFFInitDone)
×
1082
    {
1083
        InitFile();
×
1084
    }
1085

1086
    return GDALProxyRasterBand::IWriteBlock(nXBlock, nYBlock, pImage);
×
1087
}
1088

1089
/************************************************************************/
1090
/*                             IRasterIO()                              */
1091
/************************************************************************/
1092

1093
CPLErr ISIS3WrapperRasterBand::IRasterIO(
46✔
1094
    GDALRWFlag eRWFlag, int nXOff, int nYOff, int nXSize, int nYSize,
1095
    void *pData, int nBufXSize, int nBufYSize, GDALDataType eBufType,
1096
    GSpacing nPixelSpace, GSpacing nLineSpace, GDALRasterIOExtraArg *psExtraArg)
1097

1098
{
1099
    ISIS3Dataset *poGDS = reinterpret_cast<ISIS3Dataset *>(poDS);
46✔
1100
    if (eRWFlag == GF_Write && poGDS->m_bGeoTIFFAsRegularExternal &&
46✔
1101
        !poGDS->m_bGeoTIFFInitDone)
17✔
1102
    {
1103
        InitFile();
6✔
1104
    }
1105
    if (eRWFlag == GF_Write && poGDS->m_bHasSrcNoData &&
46✔
1106
        poGDS->m_dfSrcNoData != m_dfNoData)
9✔
1107
    {
1108
        const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
1✔
1109
        if (eBufType == eDataType && nPixelSpace == nDTSize &&
1✔
1110
            nLineSpace == nPixelSpace * nBufXSize)
1✔
1111
        {
1112
            RemapNoData(eDataType, pData, nBufXSize * nBufYSize,
1✔
1113
                        poGDS->m_dfSrcNoData, m_dfNoData);
1114
        }
1115
        else
1116
        {
1117
            const GByte *pabySrc = reinterpret_cast<GByte *>(pData);
×
1118
            GByte *pabyTemp = reinterpret_cast<GByte *>(
1119
                VSI_MALLOC3_VERBOSE(nDTSize, nBufXSize, nBufYSize));
×
1120
            for (int i = 0; i < nBufYSize; i++)
×
1121
            {
1122
                GDALCopyWords(pabySrc + i * nLineSpace, eBufType,
×
1123
                              static_cast<int>(nPixelSpace),
1124
                              pabyTemp + i * nBufXSize * nDTSize, eDataType,
×
1125
                              nDTSize, nBufXSize);
1126
            }
1127
            RemapNoData(eDataType, pabyTemp, nBufXSize * nBufYSize,
×
1128
                        poGDS->m_dfSrcNoData, m_dfNoData);
1129
            CPLErr eErr = GDALProxyRasterBand::IRasterIO(
×
1130
                eRWFlag, nXOff, nYOff, nXSize, nYSize, pabyTemp, nBufXSize,
1131
                nBufYSize, eDataType, nDTSize,
1132
                static_cast<GSpacing>(nDTSize) * nBufXSize, psExtraArg);
×
1133
            VSIFree(pabyTemp);
×
1134
            return eErr;
×
1135
        }
1136
    }
1137
    return GDALProxyRasterBand::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
46✔
1138
                                          pData, nBufXSize, nBufYSize, eBufType,
1139
                                          nPixelSpace, nLineSpace, psExtraArg);
46✔
1140
}
1141

1142
/************************************************************************/
1143
/*                            ISISMaskBand()                            */
1144
/************************************************************************/
1145

1146
ISISMaskBand::ISISMaskBand(GDALRasterBand *poBaseBand)
296✔
1147
    : m_poBaseBand(poBaseBand), m_pBuffer(nullptr)
296✔
1148
{
1149
    eDataType = GDT_Byte;
296✔
1150
    poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
296✔
1151
    nRasterXSize = poBaseBand->GetXSize();
296✔
1152
    nRasterYSize = poBaseBand->GetYSize();
296✔
1153
}
296✔
1154

1155
/************************************************************************/
1156
/*                           ~ISISMaskBand()                            */
1157
/************************************************************************/
1158

1159
ISISMaskBand::~ISISMaskBand()
592✔
1160
{
1161
    VSIFree(m_pBuffer);
296✔
1162
}
592✔
1163

1164
/************************************************************************/
1165
/*                             FillMask()                               */
1166
/************************************************************************/
1167

1168
template <class T>
1169
static void FillMask(void *pvBuffer, GByte *pabyDst, int nReqXSize,
68✔
1170
                     int nReqYSize, int nBlockXSize, T NULL_VAL, T LOW_REPR_SAT,
1171
                     T LOW_INSTR_SAT, T HIGH_INSTR_SAT, T HIGH_REPR_SAT)
1172
{
1173
    const T *pSrc = static_cast<T *>(pvBuffer);
68✔
1174
    for (int y = 0; y < nReqYSize; y++)
140✔
1175
    {
1176
        for (int x = 0; x < nReqXSize; x++)
9,624✔
1177
        {
1178
            const T nSrc = pSrc[y * nBlockXSize + x];
9,552✔
1179
            if (nSrc == NULL_VAL || nSrc == LOW_REPR_SAT ||
9,552✔
1180
                nSrc == LOW_INSTR_SAT || nSrc == HIGH_INSTR_SAT ||
6,338✔
1181
                nSrc == HIGH_REPR_SAT)
1182
            {
1183
                pabyDst[y * nBlockXSize + x] = 0;
3,214✔
1184
            }
1185
            else
1186
            {
1187
                pabyDst[y * nBlockXSize + x] = 255;
6,338✔
1188
            }
1189
        }
1190
    }
1191
}
68✔
1192

1193
/************************************************************************/
1194
/*                           IReadBlock()                               */
1195
/************************************************************************/
1196

1197
CPLErr ISISMaskBand::IReadBlock(int nXBlock, int nYBlock, void *pImage)
68✔
1198

1199
{
1200
    const GDALDataType eSrcDT = m_poBaseBand->GetRasterDataType();
68✔
1201
    const int nSrcDTSize = GDALGetDataTypeSizeBytes(eSrcDT);
68✔
1202
    if (m_pBuffer == nullptr)
68✔
1203
    {
1204
        m_pBuffer = VSI_MALLOC3_VERBOSE(nBlockXSize, nBlockYSize, nSrcDTSize);
23✔
1205
        if (m_pBuffer == nullptr)
23✔
1206
            return CE_Failure;
×
1207
    }
1208

1209
    int nXOff = nXBlock * nBlockXSize;
68✔
1210
    int nReqXSize = nBlockXSize;
68✔
1211
    if (nXOff + nReqXSize > nRasterXSize)
68✔
1212
        nReqXSize = nRasterXSize - nXOff;
4✔
1213
    int nYOff = nYBlock * nBlockYSize;
68✔
1214
    int nReqYSize = nBlockYSize;
68✔
1215
    if (nYOff + nReqYSize > nRasterYSize)
68✔
1216
        nReqYSize = nRasterYSize - nYOff;
4✔
1217

1218
    if (m_poBaseBand->RasterIO(GF_Read, nXOff, nYOff, nReqXSize, nReqYSize,
136✔
1219
                               m_pBuffer, nReqXSize, nReqYSize, eSrcDT,
1220
                               nSrcDTSize,
1221
                               static_cast<GSpacing>(nSrcDTSize) * nBlockXSize,
68✔
1222
                               nullptr) != CE_None)
68✔
1223
    {
1224
        return CE_Failure;
×
1225
    }
1226

1227
    GByte *pabyDst = static_cast<GByte *>(pImage);
68✔
1228
    if (eSrcDT == GDT_Byte)
68✔
1229
    {
1230
        FillMask<GByte>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
44✔
1231
                        ISIS3_NULL1, LOW_REPR_SAT1, LOW_INSTR_SAT1,
1232
                        HIGH_INSTR_SAT1, HIGH_REPR_SAT1);
1233
    }
1234
    else if (eSrcDT == GDT_UInt16)
24✔
1235
    {
1236
        FillMask<GUInt16>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
8✔
1237
                          ISIS3_NULLU2, LOW_REPR_SATU2, LOW_INSTR_SATU2,
1238
                          HIGH_INSTR_SATU2, HIGH_REPR_SATU2);
1239
    }
1240
    else if (eSrcDT == GDT_Int16)
16✔
1241
    {
1242
        FillMask<GInt16>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
8✔
1243
                         ISIS3_NULL2, LOW_REPR_SAT2, LOW_INSTR_SAT2,
1244
                         HIGH_INSTR_SAT2, HIGH_REPR_SAT2);
1245
    }
1246
    else
1247
    {
1248
        CPLAssert(eSrcDT == GDT_Float32);
8✔
1249
        FillMask<float>(m_pBuffer, pabyDst, nReqXSize, nReqYSize, nBlockXSize,
8✔
1250
                        ISIS3_NULL4, LOW_REPR_SAT4, LOW_INSTR_SAT4,
1251
                        HIGH_INSTR_SAT4, HIGH_REPR_SAT4);
1252
    }
1253

1254
    return CE_None;
68✔
1255
}
1256

1257
/************************************************************************/
1258
/*                            ISIS3Dataset()                            */
1259
/************************************************************************/
1260

1261
ISIS3Dataset::ISIS3Dataset()
385✔
1262
{
1263
    m_oKeywords.SetStripSurroundingQuotes(true);
385✔
1264

1265
    // Deinit JSON objects
1266
    m_oJSonLabel.Deinit();
385✔
1267
    m_oSrcJSonLabel.Deinit();
385✔
1268
}
385✔
1269

1270
/************************************************************************/
1271
/*                           ~ISIS3Dataset()                            */
1272
/************************************************************************/
1273

1274
ISIS3Dataset::~ISIS3Dataset()
770✔
1275

1276
{
1277
    ISIS3Dataset::Close();
385✔
1278
}
770✔
1279

1280
/************************************************************************/
1281
/*                              Close()                                 */
1282
/************************************************************************/
1283

1284
CPLErr ISIS3Dataset::Close()
745✔
1285
{
1286
    CPLErr eErr = CE_None;
745✔
1287
    if (nOpenFlags != OPEN_FLAGS_CLOSED)
745✔
1288
    {
1289
        if (!m_bIsLabelWritten)
385✔
1290
            WriteLabel();
72✔
1291
        if (m_poExternalDS && m_bGeoTIFFAsRegularExternal &&
385✔
1292
            !m_bGeoTIFFInitDone)
8✔
1293
        {
1294
            reinterpret_cast<ISIS3WrapperRasterBand *>(GetRasterBand(1))
×
1295
                ->InitFile();
×
1296
        }
1297
        if (ISIS3Dataset::FlushCache(true) != CE_None)
385✔
1298
            eErr = CE_Failure;
×
1299
        if (m_fpLabel)
385✔
1300
        {
1301
            if (VSIFCloseL(m_fpLabel) != 0)
125✔
1302
                eErr = CE_Failure;
×
1303
        }
1304
        if (m_fpImage && m_fpImage != m_fpLabel)
385✔
1305
        {
1306
            if (VSIFCloseL(m_fpImage) != 0)
238✔
1307
                eErr = CE_Failure;
×
1308
        }
1309

1310
        ISIS3Dataset::CloseDependentDatasets();
385✔
1311
        if (GDALPamDataset::Close() != CE_None)
385✔
1312
            eErr = CE_Failure;
×
1313
    }
1314
    return eErr;
745✔
1315
}
1316

1317
/************************************************************************/
1318
/*                        CloseDependentDatasets()                      */
1319
/************************************************************************/
1320

1321
int ISIS3Dataset::CloseDependentDatasets()
385✔
1322
{
1323
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
385✔
1324

1325
    if (m_poExternalDS)
385✔
1326
    {
1327
        bHasDroppedRef = FALSE;
43✔
1328
        delete m_poExternalDS;
43✔
1329
        m_poExternalDS = nullptr;
43✔
1330
    }
1331

1332
    for (int iBand = 0; iBand < nBands; iBand++)
847✔
1333
    {
1334
        delete papoBands[iBand];
462✔
1335
    }
1336
    nBands = 0;
385✔
1337

1338
    return bHasDroppedRef;
385✔
1339
}
1340

1341
/************************************************************************/
1342
/*                            GetFileList()                             */
1343
/************************************************************************/
1344

1345
char **ISIS3Dataset::GetFileList()
97✔
1346

1347
{
1348
    char **papszFileList = GDALPamDataset::GetFileList();
97✔
1349

1350
    if (!m_osExternalFilename.empty())
97✔
1351
        papszFileList = CSLAddString(papszFileList, m_osExternalFilename);
31✔
1352
    for (int i = 0; i < m_aosAdditionalFiles.Count(); ++i)
127✔
1353
    {
1354
        if (CSLFindString(papszFileList, m_aosAdditionalFiles[i]) < 0)
30✔
1355
        {
1356
            papszFileList =
1357
                CSLAddString(papszFileList, m_aosAdditionalFiles[i]);
30✔
1358
        }
1359
    }
1360

1361
    return papszFileList;
97✔
1362
}
1363

1364
/************************************************************************/
1365
/*                           GetSpatialRef()                            */
1366
/************************************************************************/
1367

1368
const OGRSpatialReference *ISIS3Dataset::GetSpatialRef() const
59✔
1369

1370
{
1371
    if (!m_oSRS.IsEmpty())
59✔
1372
        return &m_oSRS;
39✔
1373

1374
    return GDALPamDataset::GetSpatialRef();
20✔
1375
}
1376

1377
/************************************************************************/
1378
/*                           SetSpatialRef()                            */
1379
/************************************************************************/
1380

1381
CPLErr ISIS3Dataset::SetSpatialRef(const OGRSpatialReference *poSRS)
61✔
1382
{
1383
    if (eAccess == GA_ReadOnly)
61✔
1384
        return GDALPamDataset::SetSpatialRef(poSRS);
×
1385
    if (poSRS)
61✔
1386
        m_oSRS = *poSRS;
61✔
1387
    else
1388
        m_oSRS.Clear();
×
1389
    if (m_poExternalDS)
61✔
1390
        m_poExternalDS->SetSpatialRef(poSRS);
6✔
1391
    InvalidateLabel();
61✔
1392
    return CE_None;
61✔
1393
}
1394

1395
/************************************************************************/
1396
/*                          GetGeoTransform()                           */
1397
/************************************************************************/
1398

1399
CPLErr ISIS3Dataset::GetGeoTransform(GDALGeoTransform &gt) const
78✔
1400

1401
{
1402
    if (m_bGotTransform)
78✔
1403
    {
1404
        gt = m_gt;
59✔
1405
        return CE_None;
59✔
1406
    }
1407

1408
    return GDALPamDataset::GetGeoTransform(gt);
19✔
1409
}
1410

1411
/************************************************************************/
1412
/*                          SetGeoTransform()                           */
1413
/************************************************************************/
1414

1415
CPLErr ISIS3Dataset::SetGeoTransform(const GDALGeoTransform &gt)
53✔
1416

1417
{
1418
    if (eAccess == GA_ReadOnly)
53✔
1419
        return GDALPamDataset::SetGeoTransform(gt);
×
1420
    if (gt[1] <= 0.0 || gt[1] != -gt[5] || gt[2] != 0.0 || gt[4] != 0.0)
53✔
1421
    {
1422
        CPLError(CE_Failure, CPLE_NotSupported,
×
1423
                 "Only north-up geotransform with square pixels supported");
1424
        return CE_Failure;
×
1425
    }
1426
    m_bGotTransform = true;
53✔
1427
    m_gt = gt;
53✔
1428
    if (m_poExternalDS)
53✔
1429
        m_poExternalDS->SetGeoTransform(m_gt);
6✔
1430
    InvalidateLabel();
53✔
1431
    return CE_None;
53✔
1432
}
1433

1434
/************************************************************************/
1435
/*                      GetMetadataDomainList()                         */
1436
/************************************************************************/
1437

1438
char **ISIS3Dataset::GetMetadataDomainList()
1✔
1439
{
1440
    return BuildMetadataDomainList(nullptr, FALSE, "", "json:ISIS3", nullptr);
1✔
1441
}
1442

1443
/************************************************************************/
1444
/*                             GetMetadata()                            */
1445
/************************************************************************/
1446

1447
char **ISIS3Dataset::GetMetadata(const char *pszDomain)
105✔
1448
{
1449
    if (pszDomain != nullptr && EQUAL(pszDomain, "json:ISIS3"))
105✔
1450
    {
1451
        if (m_aosISIS3MD.empty())
53✔
1452
        {
1453
            if (eAccess == GA_Update && !m_oJSonLabel.IsValid())
48✔
1454
            {
1455
                BuildLabel();
1✔
1456
            }
1457
            CPLAssert(m_oJSonLabel.IsValid());
48✔
1458
            const CPLString osJson =
1459
                m_oJSonLabel.Format(CPLJSONObject::PrettyFormat::Pretty);
96✔
1460
            m_aosISIS3MD.InsertString(0, osJson.c_str());
48✔
1461
        }
1462
        return m_aosISIS3MD.List();
53✔
1463
    }
1464
    return GDALPamDataset::GetMetadata(pszDomain);
52✔
1465
}
1466

1467
/************************************************************************/
1468
/*                           InvalidateLabel()                          */
1469
/************************************************************************/
1470

1471
void ISIS3Dataset::InvalidateLabel()
139✔
1472
{
1473
    m_oJSonLabel.Deinit();
139✔
1474
    m_aosISIS3MD.Clear();
139✔
1475
}
139✔
1476

1477
/************************************************************************/
1478
/*                             SetMetadata()                            */
1479
/************************************************************************/
1480

1481
CPLErr ISIS3Dataset::SetMetadata(char **papszMD, const char *pszDomain)
25✔
1482
{
1483
    if (m_bUseSrcLabel && eAccess == GA_Update && pszDomain != nullptr &&
25✔
1484
        EQUAL(pszDomain, "json:ISIS3"))
25✔
1485
    {
1486
        m_oSrcJSonLabel.Deinit();
25✔
1487
        InvalidateLabel();
25✔
1488
        if (papszMD != nullptr && papszMD[0] != nullptr)
25✔
1489
        {
1490
            CPLJSONDocument oJSONDocument;
25✔
1491
            const GByte *pabyData = reinterpret_cast<const GByte *>(papszMD[0]);
25✔
1492
            if (!oJSONDocument.LoadMemory(pabyData))
25✔
1493
            {
1494
                return CE_Failure;
1✔
1495
            }
1496

1497
            m_oSrcJSonLabel = oJSONDocument.GetRoot();
24✔
1498
            if (!m_oSrcJSonLabel.IsValid())
24✔
1499
            {
1500
                return CE_Failure;
×
1501
            }
1502
        }
1503
        return CE_None;
24✔
1504
    }
1505
    return GDALPamDataset::SetMetadata(papszMD, pszDomain);
×
1506
}
1507

1508
/************************************************************************/
1509
/*                        GetRawBinaryLayout()                          */
1510
/************************************************************************/
1511

1512
bool ISIS3Dataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
2✔
1513
{
1514
    if (m_sLayout.osRawFilename.empty())
2✔
1515
        return false;
×
1516
    sLayout = m_sLayout;
2✔
1517
    return true;
2✔
1518
}
1519

1520
/************************************************************************/
1521
/*                           GetValueAndUnits()                         */
1522
/************************************************************************/
1523

1524
static void GetValueAndUnits(const CPLJSONObject &obj,
50✔
1525
                             std::vector<double> &adfValues,
1526
                             std::vector<std::string> &aosUnits,
1527
                             int nExpectedVals)
1528
{
1529
    if (obj.GetType() == CPLJSONObject::Type::Integer ||
100✔
1530
        obj.GetType() == CPLJSONObject::Type::Double)
50✔
1531
    {
1532
        adfValues.push_back(obj.ToDouble());
32✔
1533
    }
1534
    else if (obj.GetType() == CPLJSONObject::Type::Object)
18✔
1535
    {
1536
        auto oValue = obj.GetObj("value");
24✔
1537
        auto oUnit = obj.GetObj("unit");
24✔
1538
        if (oValue.IsValid() &&
8✔
1539
            (oValue.GetType() == CPLJSONObject::Type::Integer ||
8✔
1540
             oValue.GetType() == CPLJSONObject::Type::Double ||
2✔
1541
             oValue.GetType() == CPLJSONObject::Type::Array) &&
2✔
1542
            oUnit.IsValid() && oUnit.GetType() == CPLJSONObject::Type::String)
16✔
1543
        {
1544
            if (oValue.GetType() == CPLJSONObject::Type::Array)
8✔
1545
            {
1546
                GetValueAndUnits(oValue, adfValues, aosUnits, nExpectedVals);
2✔
1547
            }
1548
            else
1549
            {
1550
                adfValues.push_back(oValue.ToDouble());
6✔
1551
            }
1552
            aosUnits.push_back(oUnit.ToString());
8✔
1553
        }
1554
    }
1555
    else if (obj.GetType() == CPLJSONObject::Type::Array)
10✔
1556
    {
1557
        auto oArray = obj.ToArray();
10✔
1558
        if (oArray.Size() == nExpectedVals)
10✔
1559
        {
1560
            for (int i = 0; i < nExpectedVals; i++)
34✔
1561
            {
1562
                if (oArray[i].GetType() == CPLJSONObject::Type::Integer ||
60✔
1563
                    oArray[i].GetType() == CPLJSONObject::Type::Double)
36✔
1564
                {
1565
                    adfValues.push_back(oArray[i].ToDouble());
24✔
1566
                }
1567
                else
1568
                {
1569
                    adfValues.clear();
×
1570
                    return;
×
1571
                }
1572
            }
1573
        }
1574
    }
1575
}
1576

1577
/************************************************************************/
1578
/*                                Open()                                */
1579
/************************************************************************/
1580

1581
GDALDataset *ISIS3Dataset::Open(GDALOpenInfo *poOpenInfo)
260✔
1582

1583
{
1584
    /* -------------------------------------------------------------------- */
1585
    /*      Does this look like a CUBE dataset?                             */
1586
    /* -------------------------------------------------------------------- */
1587
    if (!ISIS3DriverIdentify(poOpenInfo))
260✔
1588
        return nullptr;
×
1589

1590
    /* -------------------------------------------------------------------- */
1591
    /*      Open the file using the large file API.                         */
1592
    /* -------------------------------------------------------------------- */
1593
    auto poDS = std::make_unique<ISIS3Dataset>();
520✔
1594

1595
    if (!poDS->m_oKeywords.Ingest(poOpenInfo->fpL, 0))
260✔
1596
    {
1597
        VSIFCloseL(poOpenInfo->fpL);
1✔
1598
        poOpenInfo->fpL = nullptr;
1✔
1599
        return nullptr;
1✔
1600
    }
1601
    poDS->m_oJSonLabel = poDS->m_oKeywords.GetJsonObject();
259✔
1602
    poDS->m_oJSonLabel.Add("_filename", poOpenInfo->pszFilename);
259✔
1603

1604
    // Find additional files from the label
1605
    for (const CPLJSONObject &oObj : poDS->m_oJSonLabel.GetChildren())
1,292✔
1606
    {
1607
        if (oObj.GetType() == CPLJSONObject::Type::Object)
1,033✔
1608
        {
1609
            CPLString osContainerName = oObj.GetName();
1,548✔
1610
            CPLJSONObject oContainerName = oObj.GetObj("_container_name");
2,322✔
1611
            if (oContainerName.GetType() == CPLJSONObject::Type::String)
774✔
1612
            {
1613
                osContainerName = oContainerName.ToString();
24✔
1614
            }
1615

1616
            CPLJSONObject oFilename = oObj.GetObj("^" + osContainerName);
1,548✔
1617
            if (oFilename.GetType() == CPLJSONObject::Type::String)
774✔
1618
            {
1619
                VSIStatBufL sStat;
1620
                const CPLString osFilename(CPLFormFilenameSafe(
357✔
1621
                    CPLGetPathSafe(poOpenInfo->pszFilename).c_str(),
238✔
1622
                    oFilename.ToString().c_str(), nullptr));
476✔
1623
                if (VSIStatL(osFilename, &sStat) == 0)
119✔
1624
                {
1625
                    poDS->m_aosAdditionalFiles.AddString(osFilename);
81✔
1626
                }
1627
                else
1628
                {
1629
                    CPLDebug("ISIS3", "File %s referenced but not foud",
38✔
1630
                             osFilename.c_str());
1631
                }
1632
            }
1633
        }
1634
    }
1635

1636
    VSIFCloseL(poOpenInfo->fpL);
259✔
1637
    poOpenInfo->fpL = nullptr;
259✔
1638

1639
    /* -------------------------------------------------------------------- */
1640
    /* Assume user is pointing to label (i.e. .lbl) file for detached option */
1641
    /* -------------------------------------------------------------------- */
1642
    //  Image can be inline or detached and point to an image name
1643
    //  the Format can be Tiled or Raw
1644
    //  Object = Core
1645
    //      StartByte   = 65537
1646
    //      Format      = Tile
1647
    //      TileSamples = 128
1648
    //      TileLines   = 128
1649
    // OR-----
1650
    //  Object = Core
1651
    //      StartByte = 1
1652
    //      ^Core     = r0200357_detatched.cub
1653
    //      Format    = BandSequential
1654
    // OR-----
1655
    //  Object = Core
1656
    //      StartByte = 1
1657
    //      ^Core     = r0200357_detached_tiled.cub
1658
    //      Format      = Tile
1659
    //      TileSamples = 128
1660
    //      TileLines   = 128
1661
    // OR-----
1662
    //  Object = Core
1663
    //      StartByte = 1
1664
    //      ^Core     = some.tif
1665
    //      Format    = GeoTIFF
1666

1667
    /* -------------------------------------------------------------------- */
1668
    /*      What file contains the actual data?                             */
1669
    /* -------------------------------------------------------------------- */
1670
    const char *pszCore = poDS->GetKeyword("IsisCube.Core.^Core");
259✔
1671
    CPLString osQubeFile(
1672
        EQUAL(pszCore, "")
259✔
1673
            ? CPLString(poOpenInfo->pszFilename)
356✔
1674
            : CPLFormFilenameSafe(
1675
                  CPLGetPathSafe(poOpenInfo->pszFilename).c_str(), pszCore,
453✔
1676
                  nullptr));
518✔
1677
    if (!EQUAL(pszCore, ""))
259✔
1678
    {
1679
        poDS->m_osExternalFilename = osQubeFile;
97✔
1680
    }
1681

1682
    /* -------------------------------------------------------------------- */
1683
    /*      Check if file an ISIS3 header file?  Read a few lines of text   */
1684
    /*      searching for something starting with nrows or ncols.           */
1685
    /* -------------------------------------------------------------------- */
1686

1687
    /*************   Skipbytes     *****************************/
1688
    int nSkipBytes = atoi(poDS->GetKeyword("IsisCube.Core.StartByte", "1"));
259✔
1689
    if (nSkipBytes <= 1)
259✔
1690
        nSkipBytes = 0;
91✔
1691
    else
1692
        nSkipBytes -= 1;
168✔
1693

1694
    /*******   Grab format type (BandSequential, Tiled)  *******/
1695
    CPLString osFormat = poDS->GetKeyword("IsisCube.Core.Format");
518✔
1696

1697
    int tileSizeX = 0;
259✔
1698
    int tileSizeY = 0;
259✔
1699

1700
    if (EQUAL(osFormat, "Tile"))
259✔
1701
    {
1702
        poDS->m_bIsTiled = true;
26✔
1703
        /******* Get Tile Sizes *********/
1704
        tileSizeX = atoi(poDS->GetKeyword("IsisCube.Core.TileSamples"));
26✔
1705
        tileSizeY = atoi(poDS->GetKeyword("IsisCube.Core.TileLines"));
26✔
1706
        if (tileSizeX <= 0 || tileSizeY <= 0)
26✔
1707
        {
1708
            CPLError(CE_Failure, CPLE_OpenFailed,
1✔
1709
                     "Wrong tile dimensions : %d x %d", tileSizeX, tileSizeY);
1710
            return nullptr;
1✔
1711
        }
1712
    }
1713
    else if (!EQUAL(osFormat, "BandSequential") && !EQUAL(osFormat, "GeoTIFF"))
233✔
1714
    {
1715
        CPLError(CE_Failure, CPLE_OpenFailed, "%s format not supported.",
1✔
1716
                 osFormat.c_str());
1717
        return nullptr;
1✔
1718
    }
1719

1720
    /***********   Grab samples lines band ************/
1721
    const int nCols =
1722
        atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Samples"));
257✔
1723
    const int nRows = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Lines"));
257✔
1724
    const int nBands = atoi(poDS->GetKeyword("IsisCube.Core.Dimensions.Bands"));
257✔
1725

1726
    /****** Grab format type - ISIS3 only supports 8,U16,S16,32 *****/
1727
    GDALDataType eDataType = GDT_Byte;
257✔
1728
    double dfNoData = 0.0;
257✔
1729

1730
    const char *itype = poDS->GetKeyword("IsisCube.Core.Pixels.Type");
257✔
1731
    if (EQUAL(itype, "UnsignedByte"))
257✔
1732
    {
1733
        eDataType = GDT_Byte;
209✔
1734
        dfNoData = ISIS3_NULL1;
209✔
1735
    }
1736
    else if (EQUAL(itype, "UnsignedWord"))
48✔
1737
    {
1738
        eDataType = GDT_UInt16;
14✔
1739
        dfNoData = ISIS3_NULLU2;
14✔
1740
    }
1741
    else if (EQUAL(itype, "SignedWord"))
34✔
1742
    {
1743
        eDataType = GDT_Int16;
14✔
1744
        dfNoData = ISIS3_NULL2;
14✔
1745
    }
1746
    else if (EQUAL(itype, "Real") || EQUAL(itype, ""))
20✔
1747
    {
1748
        eDataType = GDT_Float32;
19✔
1749
        dfNoData = ISIS3_NULL4;
19✔
1750
    }
1751
    else
1752
    {
1753
        CPLError(CE_Failure, CPLE_OpenFailed, "%s pixel type not supported.",
1✔
1754
                 itype);
1755
        return nullptr;
1✔
1756
    }
1757

1758
    /***********   Grab samples lines band ************/
1759

1760
    // default to MSB
1761
    const bool bIsLSB =
1762
        EQUAL(poDS->GetKeyword("IsisCube.Core.Pixels.ByteOrder"), "Lsb");
256✔
1763

1764
    /***********   Grab Cellsize ************/
1765
    double dfXDim = 1.0;
256✔
1766
    double dfYDim = 1.0;
256✔
1767

1768
    const char *pszRes = poDS->GetKeyword("IsisCube.Mapping.PixelResolution");
256✔
1769
    if (strlen(pszRes) > 0)
256✔
1770
    {
1771
        dfXDim = CPLAtof(pszRes); /* values are in meters */
97✔
1772
        dfYDim = -CPLAtof(pszRes);
97✔
1773
    }
1774

1775
    /***********   Grab UpperLeftCornerY ************/
1776
    double dfULYMap = 0.5;
256✔
1777

1778
    const char *pszULY = poDS->GetKeyword("IsisCube.Mapping.UpperLeftCornerY");
256✔
1779
    if (strlen(pszULY) > 0)
256✔
1780
    {
1781
        dfULYMap = CPLAtof(pszULY);
97✔
1782
    }
1783

1784
    /***********   Grab UpperLeftCornerX ************/
1785
    double dfULXMap = 0.5;
256✔
1786

1787
    const char *pszULX = poDS->GetKeyword("IsisCube.Mapping.UpperLeftCornerX");
256✔
1788
    if (strlen(pszULX) > 0)
256✔
1789
    {
1790
        dfULXMap = CPLAtof(pszULX);
97✔
1791
    }
1792

1793
    /***********  Grab TARGET_NAME  ************/
1794
    /**** This is the planets name i.e. Mars ***/
1795
    const char *target_name = poDS->GetKeyword("IsisCube.Mapping.TargetName");
256✔
1796

1797
#ifdef notdef
1798
    const double dfLongitudeMulFactor =
1799
        EQUAL(poDS->GetKeyword("IsisCube.Mapping.LongitudeDirection",
1800
                               "PositiveEast"),
1801
              "PositiveEast")
1802
            ? 1
1803
            : -1;
1804
#else
1805
    const double dfLongitudeMulFactor = 1;
256✔
1806
#endif
1807

1808
    /***********   Grab MAP_PROJECTION_TYPE ************/
1809
    const char *map_proj_name =
1810
        poDS->GetKeyword("IsisCube.Mapping.ProjectionName");
256✔
1811

1812
    /***********   Grab SEMI-MAJOR ************/
1813
    const double semi_major =
1814
        CPLAtof(poDS->GetKeyword("IsisCube.Mapping.EquatorialRadius"));
256✔
1815

1816
    /***********   Grab semi-minor ************/
1817
    const double semi_minor =
1818
        CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PolarRadius"));
256✔
1819

1820
    /***********   Grab CENTER_LAT ************/
1821
    const double center_lat =
1822
        CPLAtof(poDS->GetKeyword("IsisCube.Mapping.CenterLatitude"));
256✔
1823

1824
    /***********   Grab CENTER_LON ************/
1825
    const double center_lon =
1826
        CPLAtof(poDS->GetKeyword("IsisCube.Mapping.CenterLongitude")) *
256✔
1827
        dfLongitudeMulFactor;
1828

1829
    /***********   Grab 1st std parallel ************/
1830
    const double first_std_parallel =
1831
        CPLAtof(poDS->GetKeyword("IsisCube.Mapping.FirstStandardParallel"));
256✔
1832

1833
    /***********   Grab 2nd std parallel ************/
1834
    const double second_std_parallel =
1835
        CPLAtof(poDS->GetKeyword("IsisCube.Mapping.SecondStandardParallel"));
256✔
1836

1837
    /***********   Grab scaleFactor ************/
1838
    const double scaleFactor =
1839
        CPLAtof(poDS->GetKeyword("IsisCube.Mapping.scaleFactor", "1.0"));
256✔
1840

1841
    /*** grab      LatitudeType = Planetographic ****/
1842
    // Need to further study how ocentric/ographic will effect the gdal library
1843
    // So far we will use this fact to define a sphere or ellipse for some
1844
    // projections
1845

1846
    // Frank - may need to talk this over
1847
    bool bIsGeographic = true;
256✔
1848
    if (EQUAL(poDS->GetKeyword("IsisCube.Mapping.LatitudeType"),
256✔
1849
              "Planetocentric"))
1850
        bIsGeographic = false;
87✔
1851

1852
        // Set oSRS projection and parameters
1853
        // ############################################################
1854
        // ISIS3 Projection types
1855
        //   Equirectangular
1856
        //   LambertConformal
1857
        //   Mercator
1858
        //   ObliqueCylindrical
1859
        //   Orthographic
1860
        //   PolarStereographic
1861
        //   SimpleCylindrical
1862
        //   Sinusoidal
1863
        //   TransverseMercator
1864

1865
#ifdef DEBUG
1866
    CPLDebug("ISIS3", "using projection %s", map_proj_name);
256✔
1867
#endif
1868

1869
    OGRSpatialReference oSRS;
512✔
1870
    bool bProjectionSet = true;
256✔
1871

1872
    if ((EQUAL(map_proj_name, "Equirectangular")) ||
256✔
1873
        (EQUAL(map_proj_name, "SimpleCylindrical")))
189✔
1874
    {
1875
        oSRS.SetEquirectangular2(0.0, center_lon, center_lat, 0, 0);
82✔
1876
    }
1877
    else if (EQUAL(map_proj_name, "Orthographic"))
174✔
1878
    {
1879
        oSRS.SetOrthographic(center_lat, center_lon, 0, 0);
2✔
1880
    }
1881
    else if (EQUAL(map_proj_name, "Sinusoidal"))
172✔
1882
    {
1883
        oSRS.SetSinusoidal(center_lon, 0, 0);
2✔
1884
    }
1885
    else if (EQUAL(map_proj_name, "Mercator"))
170✔
1886
    {
1887
        oSRS.SetMercator(center_lat, center_lon, scaleFactor, 0, 0);
2✔
1888
    }
1889
    else if (EQUAL(map_proj_name, "PolarStereographic"))
168✔
1890
    {
1891
        oSRS.SetPS(center_lat, center_lon, scaleFactor, 0, 0);
2✔
1892
    }
1893
    else if (EQUAL(map_proj_name, "TransverseMercator"))
166✔
1894
    {
1895
        oSRS.SetTM(center_lat, center_lon, scaleFactor, 0, 0);
16✔
1896
    }
1897
    else if (EQUAL(map_proj_name, "LambertConformal"))
150✔
1898
    {
1899
        oSRS.SetLCC(first_std_parallel, second_std_parallel, center_lat,
2✔
1900
                    center_lon, 0, 0);
1901
    }
1902
    else if (EQUAL(map_proj_name, "PointPerspective"))
148✔
1903
    {
1904
        // Distance parameter is the distance to the center of the body, and is
1905
        // given in km
1906
        const double distance =
1907
            CPLAtof(poDS->GetKeyword("IsisCube.Mapping.Distance")) * 1000.0;
1✔
1908
        const double height_above_ground = distance - semi_major;
1✔
1909
        oSRS.SetVerticalPerspective(center_lat, center_lon, 0,
1✔
1910
                                    height_above_ground, 0, 0);
1911
    }
1912
    else if (EQUAL(map_proj_name, "ObliqueCylindrical"))
147✔
1913
    {
1914
        const double poleLatitude =
1915
            CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PoleLatitude"));
4✔
1916
        const double poleLongitude =
1917
            CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PoleLongitude")) *
4✔
1918
            dfLongitudeMulFactor;
1919
        const double poleRotation =
1920
            CPLAtof(poDS->GetKeyword("IsisCube.Mapping.PoleRotation"));
4✔
1921
        CPLString oProj4String;
8✔
1922
        // ISIS3 rotated pole doesn't use the same conventions than PROJ ob_tran
1923
        // Compare the sign difference in
1924
        // https://github.com/USGS-Astrogeology/ISIS3/blob/3.8.0/isis/src/base/objs/ObliqueCylindrical/ObliqueCylindrical.cpp#L244
1925
        // and
1926
        // https://github.com/OSGeo/PROJ/blob/6.2/src/projections/ob_tran.cpp#L34
1927
        // They can be compensated by modifying the poleLatitude to
1928
        // 180-poleLatitude There's also a sign difference for the poleRotation
1929
        // parameter The existence of those different conventions is
1930
        // acknowledged in
1931
        // https://pds-imaging.jpl.nasa.gov/documentation/Cassini_BIDRSIS.PDF in
1932
        // the middle of page 10
1933
        oProj4String.Printf("+proj=ob_tran +o_proj=eqc +o_lon_p=%.17g "
1934
                            "+o_lat_p=%.17g +lon_0=%.17g",
1935
                            -poleRotation, 180 - poleLatitude, poleLongitude);
4✔
1936
        oSRS.SetFromUserInput(oProj4String);
4✔
1937
    }
1938
    else
1939
    {
1940
        CPLDebug("ISIS3",
143✔
1941
                 "Dataset projection %s is not supported. Continuing...",
1942
                 map_proj_name);
1943
        bProjectionSet = false;
143✔
1944
    }
1945

1946
    if (bProjectionSet)
256✔
1947
    {
1948
        // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
1949
        CPLString osProjTargetName(map_proj_name);
226✔
1950
        osProjTargetName += " ";
113✔
1951
        osProjTargetName += target_name;
113✔
1952
        oSRS.SetProjCS(osProjTargetName);  // set ProjCS keyword
113✔
1953

1954
        // The geographic/geocentric name will be the same basic name as the
1955
        // body name 'GCS' = Geographic/Geocentric Coordinate System
1956
        CPLString osGeogName("GCS_");
226✔
1957
        osGeogName += target_name;
113✔
1958

1959
        // The datum name will be the same basic name as the planet
1960
        CPLString osDatumName("D_");
226✔
1961
        osDatumName += target_name;
113✔
1962

1963
        CPLString osSphereName(target_name);
226✔
1964
        // strcat(osSphereName, "_IAU_IAG");  //Might not be IAU defined so
1965
        // don't add
1966

1967
        // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
1968
        double iflattening = 0.0;
113✔
1969
        if ((semi_major - semi_minor) < 0.0000001)
113✔
1970
            iflattening = 0;
38✔
1971
        else
1972
            iflattening = semi_major / (semi_major - semi_minor);
75✔
1973

1974
        // Set the body size but take into consideration which proj is being
1975
        // used to help w/ proj4 compatibility The use of a Sphere, polar radius
1976
        // or ellipse here is based on how ISIS does it internally
1977
        if (((EQUAL(map_proj_name, "Stereographic") &&
113✔
1978
              (fabs(center_lat) == 90))) ||
×
1979
            (EQUAL(map_proj_name, "PolarStereographic")))
113✔
1980
        {
1981
            if (bIsGeographic)
2✔
1982
            {
1983
                // Geograpraphic, so set an ellipse
1984
                oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
×
1985
                               semi_major, iflattening, "Reference_Meridian",
1986
                               0.0);
1987
            }
1988
            else
1989
            {
1990
                // Geocentric, so force a sphere using the semi-minor axis. I
1991
                // hope...
1992
                osSphereName += "_polarRadius";
2✔
1993
                oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
2✔
1994
                               semi_minor, 0.0, "Reference_Meridian", 0.0);
1995
            }
1996
        }
1997
        else if ((EQUAL(map_proj_name, "SimpleCylindrical")) ||
111✔
1998
                 (EQUAL(map_proj_name, "Orthographic")) ||
96✔
1999
                 (EQUAL(map_proj_name, "Stereographic")) ||
94✔
2000
                 (EQUAL(map_proj_name, "Sinusoidal")) ||
94✔
2001
                 (EQUAL(map_proj_name, "PointPerspective")))
92✔
2002
        {
2003
            // ISIS uses the spherical equation for these projections
2004
            // so force a sphere.
2005
            oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, semi_major,
20✔
2006
                           0.0, "Reference_Meridian", 0.0);
2007
        }
2008
        else if (EQUAL(map_proj_name, "Equirectangular"))
91✔
2009
        {
2010
            // Calculate localRadius using ISIS3 simple elliptical method
2011
            //   not the more standard Radius of Curvature method
2012
            // PI = 4 * atan(1);
2013
            const double radLat = center_lat * M_PI / 180;  // in radians
67✔
2014
            const double meanRadius = sqrt(pow(semi_minor * cos(radLat), 2) +
67✔
2015
                                           pow(semi_major * sin(radLat), 2));
67✔
2016
            const double localRadius =
67✔
2017
                (meanRadius == 0.0) ? 0.0
67✔
2018
                                    : semi_major * semi_minor / meanRadius;
67✔
2019
            osSphereName += "_localRadius";
67✔
2020
            oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName, localRadius,
67✔
2021
                           0.0, "Reference_Meridian", 0.0);
2022
        }
2023
        else
2024
        {
2025
            // All other projections: Mercator, Transverse Mercator, Lambert
2026
            // Conformal, etc. Geographic, so set an ellipse
2027
            if (bIsGeographic)
24✔
2028
            {
2029
                oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
×
2030
                               semi_major, iflattening, "Reference_Meridian",
2031
                               0.0);
2032
            }
2033
            else
2034
            {
2035
                // Geocentric, so force a sphere. I hope...
2036
                oSRS.SetGeogCS(osGeogName, osDatumName, osSphereName,
24✔
2037
                               semi_major, 0.0, "Reference_Meridian", 0.0);
2038
            }
2039
        }
2040

2041
        // translate back into a projection string.
2042
        poDS->m_oSRS = std::move(oSRS);
113✔
2043
        poDS->m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
113✔
2044
    }
2045

2046
    /* END ISIS3 Label Read */
2047
    /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
2048

2049
    /* -------------------------------------------------------------------- */
2050
    /*      Did we get the required keywords?  If not we return with        */
2051
    /*      this never having been considered to be a match. This isn't     */
2052
    /*      an error!                                                       */
2053
    /* -------------------------------------------------------------------- */
2054
    if (!GDALCheckDatasetDimensions(nCols, nRows) ||
511✔
2055
        !GDALCheckBandCount(nBands, false))
255✔
2056
    {
2057
        return nullptr;
2✔
2058
    }
2059

2060
    /* -------------------------------------------------------------------- */
2061
    /*      Capture some information from the file that is of interest.     */
2062
    /* -------------------------------------------------------------------- */
2063
    poDS->nRasterXSize = nCols;
254✔
2064
    poDS->nRasterYSize = nRows;
254✔
2065

2066
    /* -------------------------------------------------------------------- */
2067
    /*      Open target binary file.                                        */
2068
    /* -------------------------------------------------------------------- */
2069
    if (EQUAL(osFormat, "GeoTIFF"))
254✔
2070
    {
2071
        if (nSkipBytes != 0)
27✔
2072
        {
2073
            CPLError(CE_Warning, CPLE_NotSupported,
2✔
2074
                     "Ignoring StartByte=%d for format=GeoTIFF",
2075
                     1 + nSkipBytes);
2076
        }
2077
        if (osQubeFile == poOpenInfo->pszFilename)
27✔
2078
        {
2079
            CPLError(CE_Failure, CPLE_AppDefined, "A ^Core file must be set");
×
2080
            return nullptr;
×
2081
        }
2082
        poDS->m_poExternalDS =
27✔
2083
            GDALDataset::FromHandle(GDALOpen(osQubeFile, poOpenInfo->eAccess));
27✔
2084
        if (poDS->m_poExternalDS == nullptr)
27✔
2085
        {
2086
            return nullptr;
2✔
2087
        }
2088
        if (poDS->m_poExternalDS->GetRasterXSize() != poDS->nRasterXSize ||
25✔
2089
            poDS->m_poExternalDS->GetRasterYSize() != poDS->nRasterYSize ||
24✔
2090
            poDS->m_poExternalDS->GetRasterCount() != nBands ||
71✔
2091
            poDS->m_poExternalDS->GetRasterBand(1)->GetRasterDataType() !=
22✔
2092
                eDataType)
2093
        {
2094
            CPLError(CE_Failure, CPLE_AppDefined,
4✔
2095
                     "%s has incompatible characteristics with the ones "
2096
                     "declared in the label.",
2097
                     osQubeFile.c_str());
2098
            return nullptr;
4✔
2099
        }
2100
    }
2101
    else
2102
    {
2103
        if (poOpenInfo->eAccess == GA_ReadOnly)
227✔
2104
            poDS->m_fpImage = VSIFOpenL(osQubeFile, "r");
225✔
2105
        else
2106
            poDS->m_fpImage = VSIFOpenL(osQubeFile, "r+");
2✔
2107

2108
        if (poDS->m_fpImage == nullptr)
227✔
2109
        {
2110
            CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s: %s.",
2✔
2111
                     osQubeFile.c_str(), VSIStrerror(errno));
2✔
2112
            return nullptr;
2✔
2113
        }
2114

2115
        // Sanity checks in case the external raw file appears to be a
2116
        // TIFF file
2117
        if (EQUAL(CPLGetExtensionSafe(osQubeFile).c_str(), "tif"))
225✔
2118
        {
2119
            GDALDataset *poTIF_DS =
2120
                GDALDataset::FromHandle(GDALOpen(osQubeFile, GA_ReadOnly));
23✔
2121
            if (poTIF_DS)
23✔
2122
            {
2123
                bool bWarned = false;
23✔
2124
                if (poTIF_DS->GetRasterXSize() != poDS->nRasterXSize ||
23✔
2125
                    poTIF_DS->GetRasterYSize() != poDS->nRasterYSize ||
22✔
2126
                    poTIF_DS->GetRasterCount() != nBands ||
21✔
2127
                    poTIF_DS->GetRasterBand(1)->GetRasterDataType() !=
20✔
2128
                        eDataType ||
45✔
2129
                    poTIF_DS->GetMetadataItem("COMPRESSION",
19✔
2130
                                              "IMAGE_STRUCTURE") != nullptr)
19✔
2131
                {
2132
                    bWarned = true;
5✔
2133
                    CPLError(
5✔
2134
                        CE_Warning, CPLE_AppDefined,
2135
                        "%s has incompatible characteristics with the ones "
2136
                        "declared in the label.",
2137
                        osQubeFile.c_str());
2138
                }
2139
                int nBlockXSize = 1, nBlockYSize = 1;
23✔
2140
                poTIF_DS->GetRasterBand(1)->GetBlockSize(&nBlockXSize,
23✔
2141
                                                         &nBlockYSize);
2142
                if ((poDS->m_bIsTiled &&
23✔
2143
                     (nBlockXSize != tileSizeX || nBlockYSize != tileSizeY)) ||
46✔
2144
                    (!poDS->m_bIsTiled && (nBlockXSize != nCols ||
23✔
2145
                                           (nBands > 1 && nBlockYSize != 1))))
2✔
2146
                {
2147
                    if (!bWarned)
2✔
2148
                    {
2149
                        bWarned = true;
1✔
2150
                        CPLError(
1✔
2151
                            CE_Warning, CPLE_AppDefined,
2152
                            "%s has incompatible characteristics with the ones "
2153
                            "declared in the label.",
2154
                            osQubeFile.c_str());
2155
                    }
2156
                }
2157
                // to please Clang Static Analyzer
2158
                nBlockXSize = std::max(1, nBlockXSize);
23✔
2159
                nBlockYSize = std::max(1, nBlockYSize);
23✔
2160

2161
                // Check that blocks are effectively written in expected order.
2162
                const int nBlockSizeBytes = nBlockXSize * nBlockYSize *
23✔
2163
                                            GDALGetDataTypeSizeBytes(eDataType);
23✔
2164
                bool bGoOn = !bWarned;
23✔
2165
                const int l_nBlocksPerRow = DIV_ROUND_UP(nCols, nBlockXSize);
23✔
2166
                const int l_nBlocksPerColumn = DIV_ROUND_UP(nRows, nBlockYSize);
23✔
2167
                int nBlockNo = 0;
23✔
2168
                for (int i = 0; i < nBands && bGoOn; i++)
52✔
2169
                {
2170
                    for (int y = 0; y < l_nBlocksPerColumn && bGoOn; y++)
1,285✔
2171
                    {
2172
                        for (int x = 0; x < l_nBlocksPerRow && bGoOn; x++)
2,950✔
2173
                        {
2174
                            const char *pszBlockOffset =
2175
                                poTIF_DS->GetRasterBand(i + 1)->GetMetadataItem(
3,388✔
2176
                                    CPLSPrintf("BLOCK_OFFSET_%d_%d", x, y),
2177
                                    "TIFF");
1,694✔
2178
                            if (pszBlockOffset)
1,694✔
2179
                            {
2180
                                GIntBig nOffset = CPLAtoGIntBig(pszBlockOffset);
1,694✔
2181
                                if (nOffset !=
1,694✔
2182
                                    nSkipBytes + nBlockNo * nBlockSizeBytes)
1,694✔
2183
                                {
2184
                                    // bWarned = true;
2185
                                    CPLError(CE_Warning, CPLE_AppDefined,
2✔
2186
                                             "%s has incompatible "
2187
                                             "characteristics with the ones "
2188
                                             "declared in the label.",
2189
                                             osQubeFile.c_str());
2190
                                    bGoOn = false;
2✔
2191
                                }
2192
                            }
2193
                            nBlockNo++;
1,694✔
2194
                        }
2195
                    }
2196
                }
2197

2198
                delete poTIF_DS;
23✔
2199
            }
2200
        }
2201
    }
2202

2203
    poDS->eAccess = poOpenInfo->eAccess;
246✔
2204

2205
    /* -------------------------------------------------------------------- */
2206
    /*      Compute the line offset.                                        */
2207
    /* -------------------------------------------------------------------- */
2208
    int nLineOffset = 0;
246✔
2209
    int nPixelOffset = 0;
246✔
2210
    vsi_l_offset nBandOffset = 0;
246✔
2211

2212
    if (EQUAL(osFormat, "BandSequential"))
246✔
2213
    {
2214
        const int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
200✔
2215
        nPixelOffset = nItemSize;
200✔
2216
        try
2217
        {
2218
            nLineOffset = (CPLSM(nPixelOffset) * CPLSM(nCols)).v();
200✔
2219
        }
2220
        catch (const CPLSafeIntOverflow &)
×
2221
        {
2222
            return nullptr;
×
2223
        }
2224
        nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
200✔
2225

2226
        poDS->m_sLayout.osRawFilename = std::move(osQubeFile);
200✔
2227
        if (nBands > 1)
200✔
2228
            poDS->m_sLayout.eInterleaving = RawBinaryLayout::Interleaving::BSQ;
16✔
2229
        poDS->m_sLayout.eDataType = eDataType;
200✔
2230
        poDS->m_sLayout.bLittleEndianOrder = bIsLSB;
200✔
2231
        poDS->m_sLayout.nImageOffset = nSkipBytes;
200✔
2232
        poDS->m_sLayout.nPixelOffset = nPixelOffset;
200✔
2233
        poDS->m_sLayout.nLineOffset = nLineOffset;
200✔
2234
        poDS->m_sLayout.nBandOffset = static_cast<GIntBig>(nBandOffset);
200✔
2235
    }
2236
    /* else Tiled or external */
2237

2238
    /* -------------------------------------------------------------------- */
2239
    /*      Extract BandBin info.                                           */
2240
    /* -------------------------------------------------------------------- */
2241
    std::vector<std::string> aosBandNames;
492✔
2242
    std::vector<std::string> aosBandUnits;
492✔
2243
    std::vector<double> adfWavelengths;
492✔
2244
    std::vector<std::string> aosWavelengthsUnit;
492✔
2245
    std::vector<double> adfBandwidth;
492✔
2246
    std::vector<std::string> aosBandwidthUnit;
492✔
2247
    const auto oBandBin = poDS->m_oJSonLabel.GetObj("IsisCube/BandBin");
738✔
2248
    if (oBandBin.IsValid() && oBandBin.GetType() == CPLJSONObject::Type::Object)
246✔
2249
    {
2250
        for (const auto &child : oBandBin.GetChildren())
184✔
2251
        {
2252
            if (CPLString(child.GetName()).ifind("name") != std::string::npos)
143✔
2253
            {
2254
                // Use "name" in priority
2255
                if (EQUAL(child.GetName().c_str(), "name"))
12✔
2256
                {
2257
                    aosBandNames.clear();
5✔
2258
                }
2259
                else if (!aosBandNames.empty())
7✔
2260
                {
2261
                    continue;
×
2262
                }
2263

2264
                if (child.GetType() == CPLJSONObject::Type::String &&
12✔
2265
                    nBands == 1)
2266
                {
2267
                    aosBandNames.push_back(child.ToString());
4✔
2268
                }
2269
                else if (child.GetType() == CPLJSONObject::Type::Array)
8✔
2270
                {
2271
                    auto oArray = child.ToArray();
16✔
2272
                    if (oArray.Size() == nBands)
8✔
2273
                    {
2274
                        for (int i = 0; i < nBands; i++)
28✔
2275
                        {
2276
                            if (oArray[i].GetType() ==
20✔
2277
                                CPLJSONObject::Type::String)
2278
                            {
2279
                                aosBandNames.push_back(oArray[i].ToString());
20✔
2280
                            }
2281
                            else
2282
                            {
2283
                                aosBandNames.clear();
×
2284
                                break;
×
2285
                            }
2286
                        }
2287
                    }
2288
                }
2289
            }
2290
            else if (EQUAL(child.GetName().c_str(), "BandSuffixUnit") &&
135✔
2291
                     child.GetType() == CPLJSONObject::Type::Array)
4✔
2292
            {
2293
                auto oArray = child.ToArray();
8✔
2294
                if (oArray.Size() == nBands)
4✔
2295
                {
2296
                    for (int i = 0; i < nBands; i++)
12✔
2297
                    {
2298
                        if (oArray[i].GetType() == CPLJSONObject::Type::String)
8✔
2299
                        {
2300
                            aosBandUnits.push_back(oArray[i].ToString());
8✔
2301
                        }
2302
                        else
2303
                        {
2304
                            aosBandUnits.clear();
×
2305
                            break;
×
2306
                        }
2307
                    }
2308
                }
2309
            }
2310
            else if (EQUAL(child.GetName().c_str(), "BandBinCenter") ||
377✔
2311
                     EQUAL(child.GetName().c_str(), "Center"))
250✔
2312
            {
2313
                GetValueAndUnits(child, adfWavelengths, aosWavelengthsUnit,
41✔
2314
                                 nBands);
2315
            }
2316
            else if (EQUAL(child.GetName().c_str(), "BandBinUnit") &&
90✔
2317
                     child.GetType() == CPLJSONObject::Type::String)
4✔
2318
            {
2319
                CPLString unit(child.ToString());
12✔
2320
                if (STARTS_WITH_CI(unit, "micromet") || EQUAL(unit, "um") ||
4✔
2321
                    STARTS_WITH_CI(unit, "nanomet") || EQUAL(unit, "nm"))
4✔
2322
                {
2323
                    aosWavelengthsUnit.push_back(child.ToString());
4✔
2324
                }
2325
            }
2326
            else if (EQUAL(child.GetName().c_str(), "Width"))
82✔
2327
            {
2328
                GetValueAndUnits(child, adfBandwidth, aosBandwidthUnit, nBands);
7✔
2329
            }
2330
        }
2331

2332
        if (!adfWavelengths.empty() && aosWavelengthsUnit.size() == 1)
41✔
2333
        {
2334
            for (int i = 1; i < nBands; i++)
11✔
2335
            {
2336
                aosWavelengthsUnit.push_back(aosWavelengthsUnit[0]);
4✔
2337
            }
2338
        }
2339
        if (!adfBandwidth.empty() && aosBandwidthUnit.size() == 1)
41✔
2340
        {
2341
            for (int i = 1; i < nBands; i++)
7✔
2342
            {
2343
                aosBandwidthUnit.push_back(aosBandwidthUnit[0]);
2✔
2344
            }
2345
        }
2346
    }
2347

2348
/* -------------------------------------------------------------------- */
2349
/*      Create band information objects.                                */
2350
/* -------------------------------------------------------------------- */
2351
#ifdef CPL_LSB
2352
    const bool bNativeOrder = bIsLSB;
246✔
2353
#else
2354
    const bool bNativeOrder = !bIsLSB;
2355
#endif
2356

2357
    for (int i = 0; i < nBands; i++)
542✔
2358
    {
2359
        GDALRasterBand *poBand;
2360

2361
        if (poDS->m_poExternalDS != nullptr)
296✔
2362
        {
2363
            auto poISISBand = std::make_unique<ISIS3WrapperRasterBand>(
2364
                poDS->m_poExternalDS->GetRasterBand(i + 1));
21✔
2365
            poISISBand->SetMaskBand(
42✔
2366
                std::make_unique<ISISMaskBand>(poISISBand.get()));
42✔
2367
            poDS->SetBand(i + 1, std::move(poISISBand));
21✔
2368
            poBand = poDS->GetRasterBand(i + 1);
21✔
2369
        }
2370
        else if (poDS->m_bIsTiled)
275✔
2371
        {
2372
            auto poISISBand = std::make_unique<ISISTiledBand>(
2373
                poDS.get(), poDS->m_fpImage, i + 1, eDataType, tileSizeX,
41✔
2374
                tileSizeY, nSkipBytes, 0, 0, bNativeOrder);
82✔
2375
            if (!poISISBand->IsValid())
41✔
2376
            {
2377
                return nullptr;
×
2378
            }
2379
            poISISBand->SetMaskBand(
82✔
2380
                std::make_unique<ISISMaskBand>(poISISBand.get()));
82✔
2381
            poDS->SetBand(i + 1, std::move(poISISBand));
41✔
2382
            poBand = poDS->GetRasterBand(i + 1);
41✔
2383
        }
2384
        else
2385
        {
2386
            auto poISISBand = std::make_unique<ISIS3RawRasterBand>(
2387
                poDS.get(), i + 1, poDS->m_fpImage,
234✔
2388
                nSkipBytes + nBandOffset * i, nPixelOffset, nLineOffset,
234✔
2389
                eDataType, bNativeOrder);
234✔
2390
            if (!poISISBand->IsValid())
234✔
2391
            {
2392
                return nullptr;
×
2393
            }
2394
            poISISBand->SetMaskBand(
468✔
2395
                std::make_unique<ISISMaskBand>(poISISBand.get()));
468✔
2396
            poDS->SetBand(i + 1, std::move(poISISBand));
234✔
2397
            poBand = poDS->GetRasterBand(i + 1);
234✔
2398
        }
2399

2400
        if (i < static_cast<int>(aosBandNames.size()))
296✔
2401
        {
2402
            poBand->SetDescription(aosBandNames[i].c_str());
17✔
2403
        }
2404
        if (i < static_cast<int>(adfWavelengths.size()) &&
345✔
2405
            i < static_cast<int>(aosWavelengthsUnit.size()))
49✔
2406
        {
2407
            poBand->SetMetadataItem("WAVELENGTH",
11✔
2408
                                    CPLSPrintf("%f", adfWavelengths[i]));
11✔
2409
            poBand->SetMetadataItem("WAVELENGTH_UNIT",
11✔
2410
                                    aosWavelengthsUnit[i].c_str());
11✔
2411
            if (i < static_cast<int>(adfBandwidth.size()) &&
18✔
2412
                i < static_cast<int>(aosBandwidthUnit.size()))
7✔
2413
            {
2414
                poBand->SetMetadataItem("BANDWIDTH",
7✔
2415
                                        CPLSPrintf("%f", adfBandwidth[i]));
7✔
2416
                poBand->SetMetadataItem("BANDWIDTH_UNIT",
7✔
2417
                                        aosBandwidthUnit[i].c_str());
7✔
2418
            }
2419
        }
2420
        if (i < static_cast<int>(aosBandUnits.size()))
296✔
2421
        {
2422
            poBand->SetUnitType(aosBandUnits[i].c_str());
8✔
2423
        }
2424

2425
        poBand->SetNoDataValue(dfNoData);
296✔
2426

2427
        // Set offset/scale values.
2428
        const double dfOffset =
2429
            CPLAtofM(poDS->GetKeyword("IsisCube.Core.Pixels.Base", "0.0"));
296✔
2430
        const double dfScale = CPLAtofM(
296✔
2431
            poDS->GetKeyword("IsisCube.Core.Pixels.Multiplier", "1.0"));
2432
        if (dfOffset != 0.0 || dfScale != 1.0)
296✔
2433
        {
2434
            poBand->SetOffset(dfOffset);
52✔
2435
            poBand->SetScale(dfScale);
52✔
2436
        }
2437
    }
2438

2439
    /* -------------------------------------------------------------------- */
2440
    /*      Check for a .prj file. For ISIS3 I would like to keep this in   */
2441
    /* -------------------------------------------------------------------- */
2442
    const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
492✔
2443
    const CPLString osName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
492✔
2444
    const std::string osPrjFile = CPLFormCIFilenameSafe(osPath, osName, "prj");
492✔
2445

2446
    VSILFILE *fp = VSIFOpenL(osPrjFile.c_str(), "r");
246✔
2447
    if (fp != nullptr)
246✔
2448
    {
2449
        VSIFCloseL(fp);
×
2450

2451
        char **papszLines = CSLLoad(osPrjFile.c_str());
×
2452

2453
        OGRSpatialReference oSRS2;
×
2454
        if (oSRS2.importFromESRI(papszLines) == OGRERR_NONE)
×
2455
        {
2456
            poDS->m_aosAdditionalFiles.AddString(osPrjFile.c_str());
×
2457
            poDS->m_oSRS = std::move(oSRS2);
×
2458
            poDS->m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
×
2459
        }
2460

2461
        CSLDestroy(papszLines);
×
2462
    }
2463

2464
    if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0)
246✔
2465
    {
2466
        poDS->m_bGotTransform = true;
94✔
2467
        poDS->m_gt[0] = dfULXMap;
94✔
2468
        poDS->m_gt[1] = dfXDim;
94✔
2469
        poDS->m_gt[2] = 0.0;
94✔
2470
        poDS->m_gt[3] = dfULYMap;
94✔
2471
        poDS->m_gt[4] = 0.0;
94✔
2472
        poDS->m_gt[5] = dfYDim;
94✔
2473
    }
2474

2475
    if (!poDS->m_bGotTransform)
246✔
2476
    {
2477
        poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
304✔
2478
            poOpenInfo->pszFilename, "cbw", poDS->m_gt.data()));
304✔
2479
        if (poDS->m_bGotTransform)
152✔
2480
        {
2481
            poDS->m_aosAdditionalFiles.AddString(
×
2482
                CPLResetExtensionSafe(poOpenInfo->pszFilename, "cbw").c_str());
×
2483
        }
2484
    }
2485

2486
    if (!poDS->m_bGotTransform)
246✔
2487
    {
2488
        poDS->m_bGotTransform = CPL_TO_BOOL(GDALReadWorldFile(
304✔
2489
            poOpenInfo->pszFilename, "wld", poDS->m_gt.data()));
304✔
2490
        if (poDS->m_bGotTransform)
152✔
2491
        {
2492
            poDS->m_aosAdditionalFiles.AddString(
×
2493
                CPLResetExtensionSafe(poOpenInfo->pszFilename, "wld").c_str());
×
2494
        }
2495
    }
2496

2497
    /* -------------------------------------------------------------------- */
2498
    /*      Initialize any PAM information.                                 */
2499
    /* -------------------------------------------------------------------- */
2500
    poDS->SetDescription(poOpenInfo->pszFilename);
246✔
2501
    poDS->TryLoadXML();
246✔
2502

2503
    /* -------------------------------------------------------------------- */
2504
    /*      Check for overviews.                                            */
2505
    /* -------------------------------------------------------------------- */
2506
    poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
246✔
2507

2508
    return poDS.release();
246✔
2509
}
2510

2511
/************************************************************************/
2512
/*                             GetKeyword()                             */
2513
/************************************************************************/
2514

2515
const char *ISIS3Dataset::GetKeyword(const char *pszPath,
6,046✔
2516
                                     const char *pszDefault)
2517

2518
{
2519
    return m_oKeywords.GetKeyword(pszPath, pszDefault);
6,046✔
2520
}
2521

2522
/************************************************************************/
2523
/*                              FixLong()                               */
2524
/************************************************************************/
2525

2526
double ISIS3Dataset::FixLong(double dfLong)
232✔
2527
{
2528
    if (m_osLongitudeDirection == "PositiveWest")
232✔
2529
        dfLong = -dfLong;
4✔
2530
    if (m_bForce360 && dfLong < 0)
232✔
2531
        dfLong += 360.0;
2✔
2532
    return dfLong;
232✔
2533
}
2534

2535
/************************************************************************/
2536
/*                           BuildLabel()                               */
2537
/************************************************************************/
2538

2539
void ISIS3Dataset::BuildLabel()
126✔
2540
{
2541
    CPLJSONObject oLabel = m_oSrcJSonLabel;
252✔
2542
    if (!oLabel.IsValid())
126✔
2543
    {
2544
        oLabel = CPLJSONObject();
102✔
2545
    }
2546
    // If we have a source label, then edit it directly
2547
    CPLJSONObject oIsisCube = GetOrCreateJSONObject(oLabel, "IsisCube");
378✔
2548
    oIsisCube.Set("_type", "object");
126✔
2549

2550
    if (!m_osComment.empty())
126✔
2551
        oIsisCube.Set("_comment", m_osComment);
1✔
2552

2553
    CPLJSONObject oCore = GetOrCreateJSONObject(oIsisCube, "Core");
378✔
2554
    if (oCore.GetType() != CPLJSONObject::Type::Object)
126✔
2555
    {
2556
        oIsisCube.Delete("Core");
×
2557
        oCore = CPLJSONObject();
×
2558
        oIsisCube.Add("Core", oCore);
×
2559
    }
2560
    oCore.Set("_type", "object");
126✔
2561

2562
    if (!m_osExternalFilename.empty())
126✔
2563
    {
2564
        if (m_poExternalDS && m_bGeoTIFFAsRegularExternal)
31✔
2565
        {
2566
            if (!m_bGeoTIFFInitDone)
8✔
2567
            {
2568
                reinterpret_cast<ISIS3WrapperRasterBand *>(GetRasterBand(1))
1✔
2569
                    ->InitFile();
1✔
2570
            }
2571

2572
            const char *pszOffset =
2573
                m_poExternalDS->GetRasterBand(1)->GetMetadataItem(
8✔
2574
                    "BLOCK_OFFSET_0_0", "TIFF");
8✔
2575
            if (pszOffset)
8✔
2576
            {
2577
                oCore.Set("StartByte", 1 + atoi(pszOffset));
8✔
2578
            }
2579
            else
2580
            {
2581
                // Shouldn't happen normally
2582
                CPLError(CE_Warning, CPLE_AppDefined,
×
2583
                         "Missing BLOCK_OFFSET_0_0");
2584
                m_bGeoTIFFAsRegularExternal = false;
×
2585
                oCore.Set("StartByte", 1);
×
2586
            }
8✔
2587
        }
2588
        else
2589
        {
2590
            oCore.Set("StartByte", 1);
23✔
2591
        }
2592
        if (!m_osExternalFilename.empty())
31✔
2593
        {
2594
            const CPLString osExternalFilename =
2595
                CPLGetFilename(m_osExternalFilename);
31✔
2596
            oCore.Set("^Core", osExternalFilename);
31✔
2597
        }
2598
    }
2599
    else
2600
    {
2601
        oCore.Set("StartByte", pszSTARTBYTE_PLACEHOLDER);
95✔
2602
        oCore.Delete("^Core");
95✔
2603
    }
2604

2605
    if (m_poExternalDS && !m_bGeoTIFFAsRegularExternal)
126✔
2606
    {
2607
        oCore.Set("Format", "GeoTIFF");
10✔
2608
        oCore.Delete("TileSamples");
10✔
2609
        oCore.Delete("TileLines");
10✔
2610
    }
2611
    else if (m_bIsTiled)
116✔
2612
    {
2613
        oCore.Set("Format", "Tile");
9✔
2614
        int nBlockXSize = 1, nBlockYSize = 1;
9✔
2615
        GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
9✔
2616
        oCore.Set("TileSamples", nBlockXSize);
9✔
2617
        oCore.Set("TileLines", nBlockYSize);
9✔
2618
    }
2619
    else
2620
    {
2621
        oCore.Set("Format", "BandSequential");
107✔
2622
        oCore.Delete("TileSamples");
107✔
2623
        oCore.Delete("TileLines");
107✔
2624
    }
2625

2626
    CPLJSONObject oDimensions = GetOrCreateJSONObject(oCore, "Dimensions");
378✔
2627
    oDimensions.Set("_type", "group");
126✔
2628
    oDimensions.Set("Samples", nRasterXSize);
126✔
2629
    oDimensions.Set("Lines", nRasterYSize);
126✔
2630
    oDimensions.Set("Bands", nBands);
126✔
2631

2632
    CPLJSONObject oPixels = GetOrCreateJSONObject(oCore, "Pixels");
378✔
2633
    oPixels.Set("_type", "group");
126✔
2634
    const GDALDataType eDT = GetRasterBand(1)->GetRasterDataType();
126✔
2635
    oPixels.Set("Type", (eDT == GDT_Byte)     ? "UnsignedByte"
152✔
2636
                        : (eDT == GDT_UInt16) ? "UnsignedWord"
43✔
2637
                        : (eDT == GDT_Int16)  ? "SignedWord"
17✔
2638
                                              : "Real");
2639

2640
    oPixels.Set("ByteOrder", "Lsb");
126✔
2641
    oPixels.Set("Base", GetRasterBand(1)->GetOffset());
126✔
2642
    oPixels.Set("Multiplier", GetRasterBand(1)->GetScale());
126✔
2643

2644
    const OGRSpatialReference &oSRS = m_oSRS;
126✔
2645

2646
    if (!m_bUseSrcMapping)
126✔
2647
    {
2648
        oIsisCube.Delete("Mapping");
124✔
2649
    }
2650

2651
    CPLJSONObject oMapping = GetOrCreateJSONObject(oIsisCube, "Mapping");
378✔
2652
    if (m_bUseSrcMapping && oMapping.IsValid() &&
128✔
2653
        oMapping.GetType() == CPLJSONObject::Type::Object)
2✔
2654
    {
2655
        if (!m_osTargetName.empty())
2✔
2656
            oMapping.Set("TargetName", m_osTargetName);
1✔
2657
        if (!m_osLatitudeType.empty())
2✔
2658
            oMapping.Set("LatitudeType", m_osLatitudeType);
1✔
2659
        if (!m_osLongitudeDirection.empty())
2✔
2660
            oMapping.Set("LongitudeDirection", m_osLongitudeDirection);
1✔
2661
    }
2662
    else if (!m_bUseSrcMapping && !m_oSRS.IsEmpty())
124✔
2663
    {
2664
        oMapping.Add("_type", "group");
59✔
2665

2666
        if (oSRS.IsProjected() || oSRS.IsGeographic())
59✔
2667
        {
2668
            const char *pszDatum = oSRS.GetAttrValue("DATUM");
59✔
2669
            CPLString osTargetName(m_osTargetName);
118✔
2670
            if (osTargetName.empty())
59✔
2671
            {
2672
                if (pszDatum && STARTS_WITH(pszDatum, "D_"))
58✔
2673
                {
2674
                    osTargetName = pszDatum + 2;
24✔
2675
                }
2676
                else if (pszDatum)
34✔
2677
                {
2678
                    osTargetName = pszDatum;
34✔
2679
                }
2680
            }
2681
            if (!osTargetName.empty())
59✔
2682
                oMapping.Add("TargetName", osTargetName);
59✔
2683

2684
            oMapping.Add("EquatorialRadius/value", oSRS.GetSemiMajor());
59✔
2685
            oMapping.Add("EquatorialRadius/unit", "meters");
59✔
2686
            oMapping.Add("PolarRadius/value", oSRS.GetSemiMinor());
59✔
2687
            oMapping.Add("PolarRadius/unit", "meters");
59✔
2688

2689
            if (!m_osLatitudeType.empty())
59✔
2690
                oMapping.Add("LatitudeType", m_osLatitudeType);
1✔
2691
            else
2692
                oMapping.Add("LatitudeType", "Planetocentric");
58✔
2693

2694
            if (!m_osLongitudeDirection.empty())
59✔
2695
                oMapping.Add("LongitudeDirection", m_osLongitudeDirection);
1✔
2696
            else
2697
                oMapping.Add("LongitudeDirection", "PositiveEast");
58✔
2698

2699
            double adfX[4] = {0};
59✔
2700
            double adfY[4] = {0};
59✔
2701
            bool bLongLatCorners = false;
59✔
2702
            if (m_bGotTransform)
59✔
2703
            {
2704
                for (int i = 0; i < 4; i++)
255✔
2705
                {
2706
                    adfX[i] = m_gt[0] + (i % 2) * nRasterXSize * m_gt[1];
204✔
2707
                    adfY[i] = m_gt[3] + ((i == 0 || i == 3) ? 0 : 1) *
204✔
2708
                                            nRasterYSize * m_gt[5];
204✔
2709
                }
2710
                if (oSRS.IsGeographic())
51✔
2711
                {
2712
                    bLongLatCorners = true;
31✔
2713
                }
2714
                else
2715
                {
2716
                    OGRSpatialReference *poSRSLongLat = oSRS.CloneGeogCS();
20✔
2717
                    if (poSRSLongLat)
20✔
2718
                    {
2719
                        poSRSLongLat->SetAxisMappingStrategy(
20✔
2720
                            OAMS_TRADITIONAL_GIS_ORDER);
2721
                        OGRCoordinateTransformation *poCT =
2722
                            OGRCreateCoordinateTransformation(&oSRS,
20✔
2723
                                                              poSRSLongLat);
2724
                        if (poCT)
20✔
2725
                        {
2726
                            if (poCT->Transform(4, adfX, adfY))
20✔
2727
                            {
2728
                                bLongLatCorners = true;
20✔
2729
                            }
2730
                            delete poCT;
20✔
2731
                        }
2732
                        delete poSRSLongLat;
20✔
2733
                    }
2734
                }
2735
            }
2736
            if (bLongLatCorners)
59✔
2737
            {
2738
                for (int i = 0; i < 4; i++)
255✔
2739
                {
2740
                    adfX[i] = FixLong(adfX[i]);
204✔
2741
                }
2742
            }
2743

2744
            if (bLongLatCorners &&
59✔
2745
                (m_bForce360 || adfX[0] < -180.0 || adfX[3] > 180.0))
51✔
2746
            {
2747
                oMapping.Add("LongitudeDomain", 360);
1✔
2748
            }
2749
            else
2750
            {
2751
                oMapping.Add("LongitudeDomain", 180);
58✔
2752
            }
2753

2754
            if (m_bWriteBoundingDegrees && !m_osBoundingDegrees.empty())
59✔
2755
            {
2756
                char **papszTokens =
2757
                    CSLTokenizeString2(m_osBoundingDegrees, ",", 0);
1✔
2758
                if (CSLCount(papszTokens) == 4)
1✔
2759
                {
2760
                    oMapping.Add("MinimumLatitude", CPLAtof(papszTokens[1]));
1✔
2761
                    oMapping.Add("MinimumLongitude", CPLAtof(papszTokens[0]));
1✔
2762
                    oMapping.Add("MaximumLatitude", CPLAtof(papszTokens[3]));
1✔
2763
                    oMapping.Add("MaximumLongitude", CPLAtof(papszTokens[2]));
1✔
2764
                }
2765
                CSLDestroy(papszTokens);
1✔
2766
            }
2767
            else if (m_bWriteBoundingDegrees && bLongLatCorners)
58✔
2768
            {
2769
                oMapping.Add("MinimumLatitude",
49✔
2770
                             std::min(std::min(adfY[0], adfY[1]),
2771
                                      std::min(adfY[2], adfY[3])));
49✔
2772
                oMapping.Add("MinimumLongitude",
49✔
2773
                             std::min(std::min(adfX[0], adfX[1]),
2774
                                      std::min(adfX[2], adfX[3])));
49✔
2775
                oMapping.Add("MaximumLatitude",
49✔
2776
                             std::max(std::max(adfY[0], adfY[1]),
2777
                                      std::max(adfY[2], adfY[3])));
49✔
2778
                oMapping.Add("MaximumLongitude",
49✔
2779
                             std::max(std::max(adfX[0], adfX[1]),
2780
                                      std::max(adfX[2], adfX[3])));
49✔
2781
            }
2782

2783
            const char *pszProjection = oSRS.GetAttrValue("PROJECTION");
59✔
2784
            if (pszProjection == nullptr)
59✔
2785
            {
2786
                oMapping.Add("ProjectionName", "SimpleCylindrical");
31✔
2787
                oMapping.Add("CenterLongitude", 0.0);
31✔
2788
                oMapping.Add("CenterLatitude", 0.0);
31✔
2789
                oMapping.Add("CenterLatitudeRadius", oSRS.GetSemiMajor());
31✔
2790
            }
2791
            else if (EQUAL(pszProjection, SRS_PT_EQUIRECTANGULAR))
28✔
2792
            {
2793
                oMapping.Add("ProjectionName", "Equirectangular");
14✔
2794
                if (oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
14✔
2795
                {
2796
                    CPLError(CE_Warning, CPLE_NotSupported,
1✔
2797
                             "Ignoring %s. Only 0 value supported",
2798
                             SRS_PP_LATITUDE_OF_ORIGIN);
2799
                }
2800
                oMapping.Add("CenterLongitude",
14✔
2801
                             FixLong(oSRS.GetNormProjParm(
2802
                                 SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2803
                const double dfCenterLat =
2804
                    oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0);
14✔
2805
                oMapping.Add("CenterLatitude", dfCenterLat);
14✔
2806

2807
                // in radians
2808
                const double radLat = dfCenterLat * M_PI / 180;
14✔
2809
                const double semi_major = oSRS.GetSemiMajor();
14✔
2810
                const double semi_minor = oSRS.GetSemiMinor();
14✔
2811
                const double localRadius =
2812
                    semi_major * semi_minor /
14✔
2813
                    sqrt(pow(semi_minor * cos(radLat), 2) +
14✔
2814
                         pow(semi_major * sin(radLat), 2));
14✔
2815
                oMapping.Add("CenterLatitudeRadius", localRadius);
14✔
2816
            }
2817

2818
            else if (EQUAL(pszProjection, SRS_PT_ORTHOGRAPHIC))
14✔
2819
            {
2820
                oMapping.Add("ProjectionName", "Orthographic");
1✔
2821
                oMapping.Add("CenterLongitude",
1✔
2822
                             FixLong(oSRS.GetNormProjParm(
2823
                                 SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2824
                oMapping.Add(
1✔
2825
                    "CenterLatitude",
2826
                    oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2827
            }
2828

2829
            else if (EQUAL(pszProjection, SRS_PT_SINUSOIDAL))
13✔
2830
            {
2831
                oMapping.Add("ProjectionName", "Sinusoidal");
1✔
2832
                oMapping.Add("CenterLongitude",
1✔
2833
                             FixLong(oSRS.GetNormProjParm(
2834
                                 SRS_PP_LONGITUDE_OF_CENTER, 0.0)));
2835
            }
2836

2837
            else if (EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
12✔
2838
            {
2839
                oMapping.Add("ProjectionName", "Mercator");
1✔
2840
                oMapping.Add("CenterLongitude",
1✔
2841
                             FixLong(oSRS.GetNormProjParm(
2842
                                 SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2843
                oMapping.Add(
1✔
2844
                    "CenterLatitude",
2845
                    oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2846
                oMapping.Add("scaleFactor",
1✔
2847
                             oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
2848
            }
2849

2850
            else if (EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC))
11✔
2851
            {
2852
                oMapping.Add("ProjectionName", "PolarStereographic");
1✔
2853
                oMapping.Add("CenterLongitude",
1✔
2854
                             FixLong(oSRS.GetNormProjParm(
2855
                                 SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2856
                oMapping.Add(
1✔
2857
                    "CenterLatitude",
2858
                    oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2859
                oMapping.Add("scaleFactor",
1✔
2860
                             oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
2861
            }
2862

2863
            else if (EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
10✔
2864
            {
2865
                oMapping.Add("ProjectionName", "TransverseMercator");
8✔
2866
                oMapping.Add("CenterLongitude",
8✔
2867
                             FixLong(oSRS.GetNormProjParm(
2868
                                 SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2869
                oMapping.Add(
8✔
2870
                    "CenterLatitude",
2871
                    oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2872
                oMapping.Add("scaleFactor",
8✔
2873
                             oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 1.0));
2874
            }
2875

2876
            else if (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
2✔
2877
            {
2878
                oMapping.Add("ProjectionName", "LambertConformal");
1✔
2879
                oMapping.Add("CenterLongitude",
1✔
2880
                             FixLong(oSRS.GetNormProjParm(
2881
                                 SRS_PP_CENTRAL_MERIDIAN, 0.0)));
2882
                oMapping.Add(
1✔
2883
                    "CenterLatitude",
2884
                    oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0));
2885
                oMapping.Add(
1✔
2886
                    "FirstStandardParallel",
2887
                    oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0));
2888
                oMapping.Add(
1✔
2889
                    "SecondStandardParallel",
2890
                    oSRS.GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0));
2891
            }
2892

2893
            else if (EQUAL(pszProjection,
1✔
2894
                           "Vertical Perspective"))  // PROJ 7 required
2895
            {
2896
                oMapping.Add("ProjectionName", "PointPerspective");
×
2897
                oMapping.Add("CenterLongitude",
×
2898
                             FixLong(oSRS.GetNormProjParm(
2899
                                 "Longitude of topocentric origin", 0.0)));
2900
                oMapping.Add("CenterLatitude",
×
2901
                             oSRS.GetNormProjParm(
2902
                                 "Latitude of topocentric origin", 0.0));
2903
                // ISIS3 value is the distance from center of ellipsoid, in km
2904
                oMapping.Add("Distance",
×
2905
                             (oSRS.GetNormProjParm("Viewpoint height", 0.0) +
×
2906
                              oSRS.GetSemiMajor()) /
×
2907
                                 1000.0);
2908
            }
2909

2910
            else if (EQUAL(pszProjection, "custom_proj4"))
1✔
2911
            {
2912
                const char *pszProj4 =
2913
                    oSRS.GetExtension("PROJCS", "PROJ4", nullptr);
1✔
2914
                if (pszProj4 && strstr(pszProj4, "+proj=ob_tran") &&
1✔
2915
                    strstr(pszProj4, "+o_proj=eqc"))
1✔
2916
                {
2917
                    const auto FetchParam =
2918
                        [](const char *pszProj4Str, const char *pszKey)
3✔
2919
                    {
2920
                        CPLString needle;
6✔
2921
                        needle.Printf("+%s=", pszKey);
3✔
2922
                        const char *pszVal =
2923
                            strstr(pszProj4Str, needle.c_str());
3✔
2924
                        if (pszVal)
3✔
2925
                            return CPLAtof(pszVal + needle.size());
3✔
2926
                        return 0.0;
×
2927
                    };
2928

2929
                    double dfLonP = FetchParam(pszProj4, "o_lon_p");
1✔
2930
                    double dfLatP = FetchParam(pszProj4, "o_lat_p");
1✔
2931
                    double dfLon0 = FetchParam(pszProj4, "lon_0");
1✔
2932
                    double dfPoleRotation = -dfLonP;
1✔
2933
                    double dfPoleLatitude = 180 - dfLatP;
1✔
2934
                    double dfPoleLongitude = dfLon0;
1✔
2935
                    oMapping.Add("ProjectionName", "ObliqueCylindrical");
1✔
2936
                    oMapping.Add("PoleLatitude", dfPoleLatitude);
1✔
2937
                    oMapping.Add("PoleLongitude", FixLong(dfPoleLongitude));
1✔
2938
                    oMapping.Add("PoleRotation", dfPoleRotation);
1✔
2939
                }
2940
                else
2941
                {
2942
                    CPLError(CE_Warning, CPLE_NotSupported,
×
2943
                             "Projection %s not supported", pszProjection);
2944
                }
2945
            }
2946
            else
2947
            {
2948
                CPLError(CE_Warning, CPLE_NotSupported,
×
2949
                         "Projection %s not supported", pszProjection);
2950
            }
2951

2952
            if (oMapping["ProjectionName"].IsValid())
59✔
2953
            {
2954
                if (oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0) != 0.0)
59✔
2955
                {
2956
                    CPLError(CE_Warning, CPLE_NotSupported,
6✔
2957
                             "Ignoring %s. Only 0 value supported",
2958
                             SRS_PP_FALSE_EASTING);
2959
                }
2960
                if (oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0) != 0.0)
59✔
2961
                {
2962
                    CPLError(CE_Warning, CPLE_AppDefined,
1✔
2963
                             "Ignoring %s. Only 0 value supported",
2964
                             SRS_PP_FALSE_NORTHING);
2965
                }
2966
            }
2967
        }
2968
        else
2969
        {
2970
            CPLError(CE_Warning, CPLE_NotSupported, "SRS not supported");
×
2971
        }
2972
    }
2973

2974
    if (!m_bUseSrcMapping && m_bGotTransform)
126✔
2975
    {
2976
        oMapping.Add("_type", "group");
51✔
2977

2978
        const double dfDegToMeter = oSRS.GetSemiMajor() * M_PI / 180.0;
51✔
2979
        if (!m_oSRS.IsEmpty() && oSRS.IsProjected())
51✔
2980
        {
2981
            const double dfLinearUnits = oSRS.GetLinearUnits();
20✔
2982
            // Maybe we should deal differently with non meter units ?
2983
            const double dfRes = m_gt[1] * dfLinearUnits;
20✔
2984
            const double dfScale = dfDegToMeter / dfRes;
20✔
2985
            oMapping.Add("UpperLeftCornerX", m_gt[0]);
20✔
2986
            oMapping.Add("UpperLeftCornerY", m_gt[3]);
20✔
2987
            oMapping.Add("PixelResolution/value", dfRes);
20✔
2988
            oMapping.Add("PixelResolution/unit", "meters/pixel");
20✔
2989
            oMapping.Add("Scale/value", dfScale);
20✔
2990
            oMapping.Add("Scale/unit", "pixels/degree");
20✔
2991
        }
2992
        else if (!m_oSRS.IsEmpty() && oSRS.IsGeographic())
31✔
2993
        {
2994
            const double dfScale = 1.0 / m_gt[1];
31✔
2995
            const double dfRes = m_gt[1] * dfDegToMeter;
31✔
2996
            oMapping.Add("UpperLeftCornerX", m_gt[0] * dfDegToMeter);
31✔
2997
            oMapping.Add("UpperLeftCornerY", m_gt[3] * dfDegToMeter);
31✔
2998
            oMapping.Add("PixelResolution/value", dfRes);
31✔
2999
            oMapping.Add("PixelResolution/unit", "meters/pixel");
31✔
3000
            oMapping.Add("Scale/value", dfScale);
31✔
3001
            oMapping.Add("Scale/unit", "pixels/degree");
31✔
3002
        }
3003
        else
3004
        {
3005
            oMapping.Add("UpperLeftCornerX", m_gt[0]);
×
3006
            oMapping.Add("UpperLeftCornerY", m_gt[3]);
×
3007
            oMapping.Add("PixelResolution", m_gt[1]);
×
3008
        }
3009
    }
3010

3011
    CPLJSONObject oLabelLabel = GetOrCreateJSONObject(oLabel, "Label");
378✔
3012
    oLabelLabel.Set("_type", "object");
126✔
3013
    oLabelLabel.Set("Bytes", pszLABEL_BYTES_PLACEHOLDER);
126✔
3014

3015
    // Deal with History object
3016
    BuildHistory();
126✔
3017

3018
    oLabel.Delete("History");
126✔
3019
    if (!m_osHistory.empty())
126✔
3020
    {
3021
        CPLJSONObject oHistory;
124✔
3022
        oHistory.Add("_type", "object");
124✔
3023
        oHistory.Add("Name", "IsisCube");
124✔
3024
        if (m_osExternalFilename.empty())
124✔
3025
            oHistory.Add("StartByte", pszHISTORY_STARTBYTE_PLACEHOLDER);
94✔
3026
        else
3027
            oHistory.Add("StartByte", 1);
30✔
3028
        oHistory.Add("Bytes", static_cast<GIntBig>(m_osHistory.size()));
124✔
3029
        if (!m_osExternalFilename.empty())
124✔
3030
        {
3031
            CPLString osFilename(CPLGetBasenameSafe(GetDescription()));
30✔
3032
            osFilename += ".History.IsisCube";
30✔
3033
            oHistory.Add("^History", osFilename);
30✔
3034
        }
3035
        oLabel.Add("History", oHistory);
124✔
3036
    }
3037

3038
    // Deal with other objects that have StartByte & Bytes
3039
    m_aoNonPixelSections.clear();
126✔
3040
    if (m_oSrcJSonLabel.IsValid())
126✔
3041
    {
3042
        CPLString osLabelSrcFilename;
48✔
3043
        CPLJSONObject oFilename = oLabel["_filename"];
72✔
3044
        if (oFilename.GetType() == CPLJSONObject::Type::String)
24✔
3045
        {
3046
            osLabelSrcFilename = oFilename.ToString();
19✔
3047
        }
3048

3049
        for (CPLJSONObject &oObj : oLabel.GetChildren())
128✔
3050
        {
3051
            CPLString osKey = oObj.GetName();
104✔
3052
            if (osKey == "History")
104✔
3053
            {
3054
                continue;
22✔
3055
            }
3056

3057
            CPLJSONObject oBytes = oObj.GetObj("Bytes");
164✔
3058
            if (oBytes.GetType() != CPLJSONObject::Type::Integer ||
92✔
3059
                oBytes.ToInteger() <= 0)
10✔
3060
            {
3061
                continue;
72✔
3062
            }
3063

3064
            CPLJSONObject oStartByte = oObj.GetObj("StartByte");
20✔
3065
            if (oStartByte.GetType() != CPLJSONObject::Type::Integer ||
20✔
3066
                oStartByte.ToInteger() <= 0)
10✔
3067
            {
3068
                continue;
×
3069
            }
3070

3071
            if (osLabelSrcFilename.empty())
10✔
3072
            {
3073
                CPLError(CE_Warning, CPLE_AppDefined,
×
3074
                         "Cannot find _filename attribute in "
3075
                         "source ISIS3 metadata. Removing object "
3076
                         "%s from the label.",
3077
                         osKey.c_str());
3078
                oLabel.Delete(osKey);
×
3079
                continue;
×
3080
            }
3081

3082
            NonPixelSection oSection;
10✔
3083
            oSection.osSrcFilename = osLabelSrcFilename;
10✔
3084
            oSection.nSrcOffset =
10✔
3085
                static_cast<vsi_l_offset>(oObj.GetInteger("StartByte")) - 1U;
10✔
3086
            oSection.nSize =
10✔
3087
                static_cast<vsi_l_offset>(oObj.GetInteger("Bytes"));
10✔
3088

3089
            CPLString osName;
10✔
3090
            CPLJSONObject oName = oObj.GetObj("Name");
20✔
3091
            if (oName.GetType() == CPLJSONObject::Type::String)
10✔
3092
            {
3093
                osName = oName.ToString();
10✔
3094
            }
3095

3096
            CPLString osContainerName(osKey);
10✔
3097
            CPLJSONObject oContainerName = oObj.GetObj("_container_name");
20✔
3098
            if (oContainerName.GetType() == CPLJSONObject::Type::String)
10✔
3099
            {
3100
                osContainerName = oContainerName.ToString();
5✔
3101
            }
3102

3103
            const CPLString osKeyFilename("^" + osContainerName);
10✔
3104
            CPLJSONObject oFilenameCap = oObj.GetObj(osKeyFilename);
10✔
3105
            if (oFilenameCap.GetType() == CPLJSONObject::Type::String)
10✔
3106
            {
3107
                VSIStatBufL sStat;
3108
                CPLString osSrcFilename(CPLFormFilenameSafe(
24✔
3109
                    CPLGetPathSafe(osLabelSrcFilename).c_str(),
16✔
3110
                    oFilenameCap.ToString().c_str(), nullptr));
24✔
3111
                if (VSIStatL(osSrcFilename, &sStat) == 0)
8✔
3112
                {
3113
                    oSection.osSrcFilename = std::move(osSrcFilename);
3✔
3114
                }
3115
                else
3116
                {
3117
                    CPLError(CE_Warning, CPLE_AppDefined,
5✔
3118
                             "Object %s points to %s, which does "
3119
                             "not exist. Removing this section "
3120
                             "from the label",
3121
                             osKey.c_str(), osSrcFilename.c_str());
3122
                    oLabel.Delete(osKey);
5✔
3123
                    continue;
5✔
3124
                }
3125
            }
3126

3127
            if (!m_osExternalFilename.empty())
5✔
3128
            {
3129
                oObj.Set("StartByte", 1);
2✔
3130
            }
3131
            else
3132
            {
3133
                CPLString osPlaceHolder;
6✔
3134
                osPlaceHolder.Printf(
3135
                    "!*^PLACEHOLDER_%d_STARTBYTE^*!",
3136
                    static_cast<int>(m_aoNonPixelSections.size()) + 1);
3✔
3137
                oObj.Set("StartByte", osPlaceHolder);
3✔
3138
                oSection.osPlaceHolder = std::move(osPlaceHolder);
3✔
3139
            }
3140

3141
            if (!m_osExternalFilename.empty())
5✔
3142
            {
3143
                CPLString osDstFilename(CPLGetBasenameSafe(GetDescription()));
4✔
3144
                osDstFilename += ".";
2✔
3145
                osDstFilename += osContainerName;
2✔
3146
                if (!osName.empty())
2✔
3147
                {
3148
                    osDstFilename += ".";
2✔
3149
                    osDstFilename += osName;
2✔
3150
                }
3151

3152
                oSection.osDstFilename = CPLFormFilenameSafe(
4✔
3153
                    CPLGetPathSafe(GetDescription()).c_str(), osDstFilename,
4✔
3154
                    nullptr);
2✔
3155

3156
                oObj.Set(osKeyFilename, osDstFilename);
2✔
3157
            }
3158
            else
3159
            {
3160
                oObj.Delete(osKeyFilename);
3✔
3161
            }
3162

3163
            m_aoNonPixelSections.push_back(std::move(oSection));
5✔
3164
        }
3165
    }
3166
    m_oJSonLabel = std::move(oLabel);
126✔
3167
}
126✔
3168

3169
/************************************************************************/
3170
/*                         BuildHistory()                               */
3171
/************************************************************************/
3172

3173
void ISIS3Dataset::BuildHistory()
126✔
3174
{
3175
    CPLString osHistory;
252✔
3176

3177
    if (m_oSrcJSonLabel.IsValid() && m_bUseSrcHistory)
126✔
3178
    {
3179
        vsi_l_offset nHistoryOffset = 0;
22✔
3180
        int nHistorySize = 0;
22✔
3181
        CPLString osSrcFilename;
44✔
3182

3183
        CPLJSONObject oFilename = m_oSrcJSonLabel["_filename"];
66✔
3184
        if (oFilename.GetType() == CPLJSONObject::Type::String)
22✔
3185
        {
3186
            osSrcFilename = oFilename.ToString();
17✔
3187
        }
3188
        CPLString osHistoryFilename(osSrcFilename);
44✔
3189
        CPLJSONObject oHistory = m_oSrcJSonLabel["History"];
66✔
3190
        if (oHistory.GetType() == CPLJSONObject::Type::Object)
22✔
3191
        {
3192
            CPLJSONObject oHistoryFilename = oHistory["^History"];
33✔
3193
            if (oHistoryFilename.GetType() == CPLJSONObject::Type::String)
11✔
3194
            {
3195
                osHistoryFilename = CPLFormFilenameSafe(
14✔
3196
                    CPLGetPathSafe(osSrcFilename).c_str(),
14✔
3197
                    oHistoryFilename.ToString().c_str(), nullptr);
21✔
3198
            }
3199

3200
            CPLJSONObject oStartByte = oHistory["StartByte"];
33✔
3201
            if (oStartByte.GetType() == CPLJSONObject::Type::Integer)
11✔
3202
            {
3203
                if (oStartByte.ToInteger() > 0)
11✔
3204
                {
3205
                    nHistoryOffset =
11✔
3206
                        static_cast<vsi_l_offset>(oStartByte.ToInteger()) - 1U;
11✔
3207
                }
3208
            }
3209

3210
            CPLJSONObject oBytes = oHistory["Bytes"];
33✔
3211
            if (oBytes.GetType() == CPLJSONObject::Type::Integer)
11✔
3212
            {
3213
                nHistorySize = static_cast<int>(oBytes.ToInteger());
11✔
3214
            }
3215
        }
3216

3217
        if (osHistoryFilename.empty())
22✔
3218
        {
3219
            CPLDebug("ISIS3", "Cannot find filename for source history");
5✔
3220
        }
3221
        else if (nHistorySize <= 0 || nHistorySize > 1000000)
17✔
3222
        {
3223
            CPLDebug("ISIS3", "Invalid or missing value for History.Bytes "
6✔
3224
                              "for source history");
3225
        }
3226
        else
3227
        {
3228
            VSILFILE *fpHistory = VSIFOpenL(osHistoryFilename, "rb");
11✔
3229
            if (fpHistory != nullptr)
11✔
3230
            {
3231
                VSIFSeekL(fpHistory, nHistoryOffset, SEEK_SET);
6✔
3232
                osHistory.resize(nHistorySize);
6✔
3233
                if (VSIFReadL(&osHistory[0], nHistorySize, 1, fpHistory) != 1)
6✔
3234
                {
3235
                    CPLError(CE_Warning, CPLE_FileIO,
×
3236
                             "Cannot read %d bytes at offset " CPL_FRMT_GUIB
3237
                             "of %s: history will not be preserved",
3238
                             nHistorySize, nHistoryOffset,
3239
                             osHistoryFilename.c_str());
3240
                    osHistory.clear();
×
3241
                }
3242
                VSIFCloseL(fpHistory);
6✔
3243
            }
3244
            else
3245
            {
3246
                CPLError(CE_Warning, CPLE_FileIO,
5✔
3247
                         "Cannot open %s: history will not be preserved",
3248
                         osHistoryFilename.c_str());
3249
            }
3250
        }
3251
    }
3252

3253
    if (m_bAddGDALHistory && !m_osGDALHistory.empty())
126✔
3254
    {
3255
        if (!osHistory.empty())
1✔
3256
            osHistory += "\n";
×
3257
        osHistory += m_osGDALHistory;
1✔
3258
    }
3259
    else if (m_bAddGDALHistory)
125✔
3260
    {
3261
        if (!osHistory.empty())
123✔
3262
            osHistory += "\n";
6✔
3263

3264
        CPLJSONObject oHistoryObj;
246✔
3265
        char szFullFilename[2048] = {0};
123✔
3266
        if (!CPLGetExecPath(szFullFilename, sizeof(szFullFilename) - 1))
123✔
3267
            strcpy(szFullFilename, "unknown_program");
×
3268
        const CPLString osProgram(CPLGetBasenameSafe(szFullFilename));
246✔
3269
        const CPLString osPath(CPLGetPathSafe(szFullFilename));
246✔
3270

3271
        CPLJSONObject oObj;
246✔
3272
        oHistoryObj.Add(osProgram, oObj);
123✔
3273

3274
        oObj.Add("_type", "object");
123✔
3275
        oObj.Add("GdalVersion", GDALVersionInfo("RELEASE_NAME"));
123✔
3276
        if (osPath != ".")
123✔
3277
            oObj.Add("ProgramPath", osPath);
123✔
3278
        time_t nCurTime = time(nullptr);
123✔
3279
        if (nCurTime != -1)
123✔
3280
        {
3281
            struct tm mytm;
3282
            CPLUnixTimeToYMDHMS(nCurTime, &mytm);
123✔
3283
            oObj.Add("ExecutionDateTime",
123✔
3284
                     CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02d",
3285
                                mytm.tm_year + 1900, mytm.tm_mon + 1,
123✔
3286
                                mytm.tm_mday, mytm.tm_hour, mytm.tm_min,
3287
                                mytm.tm_sec));
3288
        }
3289
        char szHostname[256] = {0};
123✔
3290
        if (gethostname(szHostname, sizeof(szHostname) - 1) == 0)
123✔
3291
        {
3292
            oObj.Add("HostName", std::string(szHostname));
123✔
3293
        }
3294
        const char *pszUsername = CPLGetConfigOption("USERNAME", nullptr);
123✔
3295
        if (pszUsername == nullptr)
123✔
3296
            pszUsername = CPLGetConfigOption("USER", nullptr);
123✔
3297
        if (pszUsername != nullptr)
123✔
3298
        {
3299
            oObj.Add("UserName", pszUsername);
×
3300
        }
3301
        oObj.Add("Description", "GDAL conversion");
123✔
3302

3303
        CPLJSONObject oUserParameters;
123✔
3304
        oObj.Add("UserParameters", oUserParameters);
123✔
3305

3306
        oUserParameters.Add("_type", "group");
123✔
3307
        if (!m_osFromFilename.empty())
123✔
3308
        {
3309
            const CPLString osFromFilename = CPLGetFilename(m_osFromFilename);
32✔
3310
            oUserParameters.Add("FROM", osFromFilename);
32✔
3311
        }
3312
        if (nullptr != GetDescription())
123✔
3313
        {
3314
            const CPLString osToFileName = CPLGetFilename(GetDescription());
123✔
3315
            oUserParameters.Add("TO", osToFileName);
123✔
3316
        }
3317
        if (m_bForce360)
123✔
3318
            oUserParameters.Add("Force_360", "true");
1✔
3319

3320
        osHistory += SerializeAsPDL(oHistoryObj);
123✔
3321
    }
3322

3323
    m_osHistory = std::move(osHistory);
126✔
3324
}
126✔
3325

3326
/************************************************************************/
3327
/*                           WriteLabel()                               */
3328
/************************************************************************/
3329

3330
void ISIS3Dataset::WriteLabel()
125✔
3331
{
3332
    m_bIsLabelWritten = true;
125✔
3333

3334
    if (!m_oJSonLabel.IsValid())
125✔
3335
        BuildLabel();
125✔
3336

3337
    // Serialize label
3338
    CPLString osLabel(SerializeAsPDL(m_oJSonLabel));
250✔
3339
    osLabel += "End\n";
125✔
3340
    if (m_osExternalFilename.empty() && osLabel.size() < 65536)
125✔
3341
    {
3342
        // In-line labels have conventionally a minimize size of 65536 bytes
3343
        // See #2741
3344
        osLabel.resize(65536);
94✔
3345
    }
3346
    char *pszLabel = &osLabel[0];
125✔
3347
    const int nLabelSize = static_cast<int>(osLabel.size());
125✔
3348

3349
    // Hack back StartByte value
3350
    {
3351
        char *pszStartByte = strstr(pszLabel, pszSTARTBYTE_PLACEHOLDER);
125✔
3352
        if (pszStartByte != nullptr)
125✔
3353
        {
3354
            const char *pszOffset = CPLSPrintf("%d", 1 + nLabelSize);
94✔
3355
            memcpy(pszStartByte, pszOffset, strlen(pszOffset));
94✔
3356
            memset(pszStartByte + strlen(pszOffset), ' ',
94✔
3357
                   strlen(pszSTARTBYTE_PLACEHOLDER) - strlen(pszOffset));
94✔
3358
        }
3359
    }
3360

3361
    // Hack back Label.Bytes value
3362
    {
3363
        char *pszLabelBytes = strstr(pszLabel, pszLABEL_BYTES_PLACEHOLDER);
125✔
3364
        if (pszLabelBytes != nullptr)
125✔
3365
        {
3366
            const char *pszBytes = CPLSPrintf("%d", nLabelSize);
125✔
3367
            memcpy(pszLabelBytes, pszBytes, strlen(pszBytes));
125✔
3368
            memset(pszLabelBytes + strlen(pszBytes), ' ',
125✔
3369
                   strlen(pszLABEL_BYTES_PLACEHOLDER) - strlen(pszBytes));
125✔
3370
        }
3371
    }
3372

3373
    const GDALDataType eType = GetRasterBand(1)->GetRasterDataType();
125✔
3374
    const int nDTSize = GDALGetDataTypeSizeBytes(eType);
125✔
3375
    vsi_l_offset nImagePixels = 0;
125✔
3376
    if (m_poExternalDS == nullptr)
125✔
3377
    {
3378
        if (m_bIsTiled)
107✔
3379
        {
3380
            int nBlockXSize = 1, nBlockYSize = 1;
7✔
3381
            GetRasterBand(1)->GetBlockSize(&nBlockXSize, &nBlockYSize);
7✔
3382
            nImagePixels = static_cast<vsi_l_offset>(nBlockXSize) *
7✔
3383
                           nBlockYSize * nBands *
14✔
3384
                           DIV_ROUND_UP(nRasterXSize, nBlockXSize) *
7✔
3385
                           DIV_ROUND_UP(nRasterYSize, nBlockYSize);
7✔
3386
        }
3387
        else
3388
        {
3389
            nImagePixels =
100✔
3390
                static_cast<vsi_l_offset>(nRasterXSize) * nRasterYSize * nBands;
100✔
3391
        }
3392
    }
3393

3394
    // Hack back History.StartBytes value
3395
    char *pszHistoryStartBytes =
3396
        strstr(pszLabel, pszHISTORY_STARTBYTE_PLACEHOLDER);
125✔
3397

3398
    vsi_l_offset nHistoryOffset = 0;
125✔
3399
    vsi_l_offset nLastOffset = 0;
125✔
3400
    if (pszHistoryStartBytes != nullptr)
125✔
3401
    {
3402
        CPLAssert(m_osExternalFilename.empty());
93✔
3403
        nHistoryOffset = nLabelSize + nImagePixels * nDTSize;
93✔
3404
        nLastOffset = nHistoryOffset + m_osHistory.size();
93✔
3405
        const char *pszStartByte =
3406
            CPLSPrintf(CPL_FRMT_GUIB, nHistoryOffset + 1);
93✔
3407
        CPLAssert(strlen(pszStartByte) <
93✔
3408
                  strlen(pszHISTORY_STARTBYTE_PLACEHOLDER));
3409
        memcpy(pszHistoryStartBytes, pszStartByte, strlen(pszStartByte));
93✔
3410
        memset(pszHistoryStartBytes + strlen(pszStartByte), ' ',
93✔
3411
               strlen(pszHISTORY_STARTBYTE_PLACEHOLDER) - strlen(pszStartByte));
93✔
3412
    }
3413

3414
    // Replace placeholders in other sections
3415
    for (size_t i = 0; i < m_aoNonPixelSections.size(); ++i)
130✔
3416
    {
3417
        if (!m_aoNonPixelSections[i].osPlaceHolder.empty())
5✔
3418
        {
3419
            char *pszPlaceHolder =
3420
                strstr(pszLabel, m_aoNonPixelSections[i].osPlaceHolder.c_str());
3✔
3421
            CPLAssert(pszPlaceHolder != nullptr);
3✔
3422
            const char *pszStartByte =
3423
                CPLSPrintf(CPL_FRMT_GUIB, nLastOffset + 1);
3✔
3424
            nLastOffset += m_aoNonPixelSections[i].nSize;
3✔
3425
            CPLAssert(strlen(pszStartByte) <
3✔
3426
                      m_aoNonPixelSections[i].osPlaceHolder.size());
3427

3428
            memcpy(pszPlaceHolder, pszStartByte, strlen(pszStartByte));
3✔
3429
            memset(pszPlaceHolder + strlen(pszStartByte), ' ',
3✔
3430
                   m_aoNonPixelSections[i].osPlaceHolder.size() -
3✔
3431
                       strlen(pszStartByte));
3✔
3432
        }
3433
    }
3434

3435
    // Write to final file
3436
    VSIFSeekL(m_fpLabel, 0, SEEK_SET);
125✔
3437
    VSIFWriteL(pszLabel, 1, osLabel.size(), m_fpLabel);
125✔
3438

3439
    if (m_osExternalFilename.empty())
125✔
3440
    {
3441
        // Update image offset in bands
3442
        if (m_bIsTiled)
94✔
3443
        {
3444
            for (int i = 0; i < nBands; i++)
15✔
3445
            {
3446
                ISISTiledBand *poBand =
3447
                    reinterpret_cast<ISISTiledBand *>(GetRasterBand(i + 1));
9✔
3448
                poBand->m_nFirstTileOffset += nLabelSize;
9✔
3449
            }
3450
        }
3451
        else
3452
        {
3453
            for (int i = 0; i < nBands; i++)
206✔
3454
            {
3455
                ISIS3RawRasterBand *poBand =
3456
                    reinterpret_cast<ISIS3RawRasterBand *>(
3457
                        GetRasterBand(i + 1));
118✔
3458
                poBand->nImgOffset += nLabelSize;
118✔
3459
            }
3460
        }
3461
    }
3462

3463
    if (m_bInitToNodata)
125✔
3464
    {
3465
        // Initialize the image to nodata
3466
        const double dfNoData = GetRasterBand(1)->GetNoDataValue();
55✔
3467
        if (dfNoData == 0.0)
55✔
3468
        {
3469
            VSIFTruncateL(m_fpImage,
43✔
3470
                          VSIFTellL(m_fpImage) + nImagePixels * nDTSize);
43✔
3471
        }
3472
        else if (nDTSize != 0)  // to make Coverity not warn about div by 0
12✔
3473
        {
3474
            const int nPageSize = 4096;  // Must be multiple of 4 since
12✔
3475
                                         // Float32 is the largest type
3476
            CPLAssert((nPageSize % nDTSize) == 0);
12✔
3477
            const int nMaxPerPage = nPageSize / nDTSize;
12✔
3478
            GByte *pabyTemp = static_cast<GByte *>(CPLMalloc(nPageSize));
12✔
3479
            GDALCopyWords(&dfNoData, GDT_Float64, 0, pabyTemp, eType, nDTSize,
12✔
3480
                          nMaxPerPage);
3481
#ifdef CPL_MSB
3482
            GDALSwapWords(pabyTemp, nDTSize, nMaxPerPage, nDTSize);
3483
#endif
3484
            for (vsi_l_offset i = 0; i < nImagePixels; i += nMaxPerPage)
80✔
3485
            {
3486
                int n;
3487
                if (i + nMaxPerPage <= nImagePixels)
68✔
3488
                    n = nMaxPerPage;
56✔
3489
                else
3490
                    n = static_cast<int>(nImagePixels - i);
12✔
3491
                if (VSIFWriteL(pabyTemp, static_cast<size_t>(n) * nDTSize, 1,
68✔
3492
                               m_fpImage) != 1)
68✔
3493
                {
3494
                    CPLError(CE_Failure, CPLE_FileIO,
×
3495
                             "Cannot initialize imagery to null");
3496
                    break;
×
3497
                }
3498
            }
3499

3500
            CPLFree(pabyTemp);
12✔
3501
        }
3502
    }
3503

3504
    // Write history
3505
    if (!m_osHistory.empty())
125✔
3506
    {
3507
        if (m_osExternalFilename.empty())
123✔
3508
        {
3509
            VSIFSeekL(m_fpLabel, nHistoryOffset, SEEK_SET);
93✔
3510
            VSIFWriteL(m_osHistory.c_str(), 1, m_osHistory.size(), m_fpLabel);
93✔
3511
        }
3512
        else
3513
        {
3514
            CPLString osFilename(CPLGetBasenameSafe(GetDescription()));
60✔
3515
            osFilename += ".History.IsisCube";
30✔
3516
            osFilename = CPLFormFilenameSafe(
60✔
3517
                CPLGetPathSafe(GetDescription()).c_str(), osFilename, nullptr);
60✔
3518
            VSILFILE *fp = VSIFOpenL(osFilename, "wb");
30✔
3519
            if (fp)
30✔
3520
            {
3521
                m_aosAdditionalFiles.AddString(osFilename);
30✔
3522

3523
                VSIFWriteL(m_osHistory.c_str(), 1, m_osHistory.size(), fp);
30✔
3524
                VSIFCloseL(fp);
30✔
3525
            }
3526
            else
3527
            {
3528
                CPLError(CE_Warning, CPLE_FileIO, "Cannot write %s",
×
3529
                         osFilename.c_str());
3530
            }
3531
        }
3532
    }
3533

3534
    // Write other non pixel sections
3535
    for (size_t i = 0; i < m_aoNonPixelSections.size(); ++i)
130✔
3536
    {
3537
        VSILFILE *fpSrc =
3538
            VSIFOpenL(m_aoNonPixelSections[i].osSrcFilename, "rb");
5✔
3539
        if (fpSrc == nullptr)
5✔
3540
        {
3541
            CPLError(CE_Warning, CPLE_FileIO, "Cannot open %s",
×
3542
                     m_aoNonPixelSections[i].osSrcFilename.c_str());
×
3543
            continue;
×
3544
        }
3545

3546
        VSILFILE *fpDest = m_fpLabel;
5✔
3547
        if (!m_aoNonPixelSections[i].osDstFilename.empty())
5✔
3548
        {
3549
            fpDest = VSIFOpenL(m_aoNonPixelSections[i].osDstFilename, "wb");
2✔
3550
            if (fpDest == nullptr)
2✔
3551
            {
3552
                CPLError(CE_Warning, CPLE_FileIO, "Cannot create %s",
×
3553
                         m_aoNonPixelSections[i].osDstFilename.c_str());
×
3554
                VSIFCloseL(fpSrc);
×
3555
                continue;
×
3556
            }
3557

3558
            m_aosAdditionalFiles.AddString(
3559
                m_aoNonPixelSections[i].osDstFilename);
2✔
3560
        }
3561

3562
        VSIFSeekL(fpSrc, m_aoNonPixelSections[i].nSrcOffset, SEEK_SET);
5✔
3563
        GByte abyBuffer[4096];
3564
        vsi_l_offset nRemaining = m_aoNonPixelSections[i].nSize;
5✔
3565
        while (nRemaining)
10✔
3566
        {
3567
            size_t nToRead = 4096;
5✔
3568
            if (nRemaining < nToRead)
5✔
3569
                nToRead = static_cast<size_t>(nRemaining);
5✔
3570
            size_t nRead = VSIFReadL(abyBuffer, 1, nToRead, fpSrc);
5✔
3571
            if (nRead != nToRead)
5✔
3572
            {
3573
                CPLError(CE_Warning, CPLE_FileIO,
×
3574
                         "Could not read " CPL_FRMT_GUIB " bytes from %s",
3575
                         m_aoNonPixelSections[i].nSize,
×
3576
                         m_aoNonPixelSections[i].osSrcFilename.c_str());
×
3577
                break;
×
3578
            }
3579
            VSIFWriteL(abyBuffer, 1, nRead, fpDest);
5✔
3580
            nRemaining -= nRead;
5✔
3581
        }
3582

3583
        VSIFCloseL(fpSrc);
5✔
3584
        if (fpDest != m_fpLabel)
5✔
3585
            VSIFCloseL(fpDest);
2✔
3586
    }
3587
}
125✔
3588

3589
/************************************************************************/
3590
/*                      SerializeAsPDL()                                */
3591
/************************************************************************/
3592

3593
CPLString ISIS3Dataset::SerializeAsPDL(const CPLJSONObject &oObj)
248✔
3594
{
3595
    const CPLString osTmpFile(VSIMemGenerateHiddenFilename("isis3_pdl"));
496✔
3596
    VSILFILE *fpTmp = VSIFOpenL(osTmpFile, "wb+");
248✔
3597
    SerializeAsPDL(fpTmp, oObj);
248✔
3598
    VSIFCloseL(fpTmp);
248✔
3599
    CPLString osContent(reinterpret_cast<char *>(
3600
        VSIGetMemFileBuffer(osTmpFile, nullptr, FALSE)));
248✔
3601
    VSIUnlink(osTmpFile);
248✔
3602
    return osContent;
496✔
3603
}
3604

3605
/************************************************************************/
3606
/*                      SerializeAsPDL()                                */
3607
/************************************************************************/
3608

3609
void ISIS3Dataset::SerializeAsPDL(VSILFILE *fp, const CPLJSONObject &oObj,
1,329✔
3610
                                  int nDepth)
3611
{
3612
    CPLString osIndentation;
2,658✔
3613
    for (int i = 0; i < nDepth; i++)
3,235✔
3614
        osIndentation += "  ";
1,906✔
3615
    const size_t WIDTH = 79;
1,329✔
3616

3617
    std::vector<CPLJSONObject> aoChildren = oObj.GetChildren();
2,658✔
3618
    size_t nMaxKeyLength = 0;
1,329✔
3619
    for (const CPLJSONObject &oChild : aoChildren)
7,175✔
3620
    {
3621
        const CPLString osKey = oChild.GetName();
5,846✔
3622
        if (EQUAL(osKey, "_type") || EQUAL(osKey, "_container_name") ||
10,602✔
3623
            EQUAL(osKey, "_filename"))
4,756✔
3624
        {
3625
            continue;
1,109✔
3626
        }
3627

3628
        const auto eType = oChild.GetType();
4,737✔
3629
        if (eType == CPLJSONObject::Type::String ||
4,737✔
3630
            eType == CPLJSONObject::Type::Integer ||
2,140✔
3631
            eType == CPLJSONObject::Type::Double ||
1,385✔
3632
            eType == CPLJSONObject::Type::Array)
3633
        {
3634
            if (osKey.size() > nMaxKeyLength)
3,361✔
3635
            {
3636
                nMaxKeyLength = osKey.size();
1,597✔
3637
            }
3638
        }
3639
        else if (eType == CPLJSONObject::Type::Object)
1,376✔
3640
        {
3641
            CPLJSONObject oValue = oChild.GetObj("value");
4,128✔
3642
            CPLJSONObject oUnit = oChild.GetObj("unit");
4,128✔
3643
            if (oValue.IsValid() &&
1,607✔
3644
                oUnit.GetType() == CPLJSONObject::Type::String)
231✔
3645
            {
3646
                if (osKey.size() > nMaxKeyLength)
231✔
3647
                {
3648
                    nMaxKeyLength = osKey.size();
61✔
3649
                }
3650
            }
3651
        }
3652
    }
3653

3654
    for (const CPLJSONObject &oChild : aoChildren)
7,175✔
3655
    {
3656
        const CPLString osKey = oChild.GetName();
5,846✔
3657
        if (EQUAL(osKey, "_type") || EQUAL(osKey, "_container_name") ||
10,602✔
3658
            EQUAL(osKey, "_filename"))
4,756✔
3659
        {
3660
            continue;
1,109✔
3661
        }
3662
        if (STARTS_WITH(osKey, "_comment"))
4,737✔
3663
        {
3664
            if (oChild.GetType() == CPLJSONObject::Type::String)
1✔
3665
            {
3666
                VSIFPrintfL(fp, "#%s\n", oChild.ToString().c_str());
1✔
3667
            }
3668
            continue;
1✔
3669
        }
3670
        CPLString osPadding;
9,472✔
3671
        size_t nLen = osKey.size();
4,736✔
3672
        if (nLen < nMaxKeyLength)
4,736✔
3673
        {
3674
            osPadding.append(nMaxKeyLength - nLen, ' ');
2,894✔
3675
        }
3676

3677
        const auto eType = oChild.GetType();
4,736✔
3678
        if (eType == CPLJSONObject::Type::Object)
4,736✔
3679
        {
3680
            CPLJSONObject oType = oChild.GetObj("_type");
4,128✔
3681
            CPLJSONObject oContainerName = oChild.GetObj("_container_name");
4,128✔
3682
            CPLString osContainerName = osKey;
2,752✔
3683
            if (oContainerName.GetType() == CPLJSONObject::Type::String)
1,376✔
3684
            {
3685
                osContainerName = oContainerName.ToString();
9✔
3686
            }
3687
            if (oType.GetType() == CPLJSONObject::Type::String)
1,376✔
3688
            {
3689
                const CPLString osType = oType.ToString();
3,243✔
3690
                if (EQUAL(osType, "Object"))
1,081✔
3691
                {
3692
                    if (nDepth == 0 && VSIFTellL(fp) != 0)
631✔
3693
                        VSIFPrintfL(fp, "\n");
258✔
3694
                    VSIFPrintfL(fp, "%sObject = %s\n", osIndentation.c_str(),
631✔
3695
                                osContainerName.c_str());
3696
                    SerializeAsPDL(fp, oChild, nDepth + 1);
631✔
3697
                    VSIFPrintfL(fp, "%sEnd_Object\n", osIndentation.c_str());
631✔
3698
                }
3699
                else if (EQUAL(osType, "Group"))
450✔
3700
                {
3701
                    VSIFPrintfL(fp, "\n");
450✔
3702
                    VSIFPrintfL(fp, "%sGroup = %s\n", osIndentation.c_str(),
450✔
3703
                                osContainerName.c_str());
3704
                    SerializeAsPDL(fp, oChild, nDepth + 1);
450✔
3705
                    VSIFPrintfL(fp, "%sEnd_Group\n", osIndentation.c_str());
450✔
3706
                }
3707
            }
3708
            else
3709
            {
3710
                CPLJSONObject oValue = oChild.GetObj("value");
885✔
3711
                CPLJSONObject oUnit = oChild.GetObj("unit");
885✔
3712
                if (oValue.IsValid() &&
526✔
3713
                    oUnit.GetType() == CPLJSONObject::Type::String)
231✔
3714
                {
3715
                    const CPLString osUnit = oUnit.ToString();
693✔
3716
                    const auto eValueType = oValue.GetType();
231✔
3717
                    if (eValueType == CPLJSONObject::Type::Integer)
231✔
3718
                    {
3719
                        VSIFPrintfL(fp, "%s%s%s = %d <%s>\n",
1✔
3720
                                    osIndentation.c_str(), osKey.c_str(),
3721
                                    osPadding.c_str(), oValue.ToInteger(),
3722
                                    osUnit.c_str());
3723
                    }
3724
                    else if (eValueType == CPLJSONObject::Type::Double)
230✔
3725
                    {
3726
                        const double dfVal = oValue.ToDouble();
229✔
3727
                        if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
229✔
3728
                            static_cast<int>(dfVal) == dfVal)
229✔
3729
                        {
3730
                            VSIFPrintfL(fp, "%s%s%s = %d.0 <%s>\n",
87✔
3731
                                        osIndentation.c_str(), osKey.c_str(),
3732
                                        osPadding.c_str(),
3733
                                        static_cast<int>(dfVal),
3734
                                        osUnit.c_str());
3735
                        }
3736
                        else
3737
                        {
3738
                            VSIFPrintfL(fp, "%s%s%s = %.17g <%s>\n",
142✔
3739
                                        osIndentation.c_str(), osKey.c_str(),
3740
                                        osPadding.c_str(), dfVal,
3741
                                        osUnit.c_str());
3742
                        }
3743
                    }
3744
                }
3745
            }
3746
        }
3747
        else if (eType == CPLJSONObject::Type::String)
3,360✔
3748
        {
3749
            CPLString osVal = oChild.ToString();
5,811✔
3750
            const char *pszVal = osVal.c_str();
1,937✔
3751
            if (pszVal[0] == '\0' || strchr(pszVal, ' ') ||
1,937✔
3752
                strstr(pszVal, "\\n") || strstr(pszVal, "\\r"))
1,809✔
3753
            {
3754
                osVal.replaceAll("\\n", "\n");
128✔
3755
                osVal.replaceAll("\\r", "\r");
128✔
3756
                VSIFPrintfL(fp, "%s%s%s = \"%s\"\n", osIndentation.c_str(),
128✔
3757
                            osKey.c_str(), osPadding.c_str(), osVal.c_str());
3758
            }
3759
            else
3760
            {
3761
                if (osIndentation.size() + osKey.size() + osPadding.size() +
1,809✔
3762
                            strlen(" = ") + strlen(pszVal) >
1,809✔
3763
                        WIDTH &&
1,810✔
3764
                    osIndentation.size() + osKey.size() + osPadding.size() +
1✔
3765
                            strlen(" = ") <
3766
                        WIDTH)
3767
                {
3768
                    size_t nFirstPos = osIndentation.size() + osKey.size() +
1✔
3769
                                       osPadding.size() + strlen(" = ");
1✔
3770
                    VSIFPrintfL(fp, "%s%s%s = ", osIndentation.c_str(),
1✔
3771
                                osKey.c_str(), osPadding.c_str());
3772
                    size_t nCurPos = nFirstPos;
1✔
3773
                    for (int j = 0; pszVal[j] != '\0'; j++)
96✔
3774
                    {
3775
                        nCurPos++;
95✔
3776
                        if (nCurPos == WIDTH && pszVal[j + 1] != '\0')
95✔
3777
                        {
3778
                            VSIFPrintfL(fp, "-\n");
1✔
3779
                            for (size_t k = 0; k < nFirstPos; k++)
15✔
3780
                            {
3781
                                const char chSpace = ' ';
14✔
3782
                                VSIFWriteL(&chSpace, 1, 1, fp);
14✔
3783
                            }
3784
                            nCurPos = nFirstPos + 1;
1✔
3785
                        }
3786
                        VSIFWriteL(&pszVal[j], 1, 1, fp);
95✔
3787
                    }
3788
                    VSIFPrintfL(fp, "\n");
1✔
3789
                }
3790
                else
3791
                {
3792
                    VSIFPrintfL(fp, "%s%s%s = %s\n", osIndentation.c_str(),
1,808✔
3793
                                osKey.c_str(), osPadding.c_str(), pszVal);
3794
                }
3795
            }
3796
        }
3797
        else if (eType == CPLJSONObject::Type::Integer)
1,423✔
3798
        {
3799
            const int nVal = oChild.ToInteger();
659✔
3800
            VSIFPrintfL(fp, "%s%s%s = %d\n", osIndentation.c_str(),
659✔
3801
                        osKey.c_str(), osPadding.c_str(), nVal);
3802
        }
3803
        else if (eType == CPLJSONObject::Type::Double)
764✔
3804
        {
3805
            const double dfVal = oChild.ToDouble();
755✔
3806
            if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
755✔
3807
                static_cast<int>(dfVal) == dfVal)
755✔
3808
            {
3809
                VSIFPrintfL(fp, "%s%s%s = %d.0\n", osIndentation.c_str(),
526✔
3810
                            osKey.c_str(), osPadding.c_str(),
3811
                            static_cast<int>(dfVal));
3812
            }
3813
            else
3814
            {
3815
                VSIFPrintfL(fp, "%s%s%s = %.17g\n", osIndentation.c_str(),
229✔
3816
                            osKey.c_str(), osPadding.c_str(), dfVal);
3817
            }
3818
        }
3819
        else if (eType == CPLJSONObject::Type::Array)
9✔
3820
        {
3821
            CPLJSONArray oArrayItem(oChild);
18✔
3822
            const int nLength = oArrayItem.Size();
9✔
3823
            size_t nFirstPos = osIndentation.size() + osKey.size() +
9✔
3824
                               osPadding.size() + strlen(" = (");
9✔
3825
            VSIFPrintfL(fp, "%s%s%s = (", osIndentation.c_str(), osKey.c_str(),
9✔
3826
                        osPadding.c_str());
3827
            size_t nCurPos = nFirstPos;
9✔
3828
            for (int idx = 0; idx < nLength; idx++)
46✔
3829
            {
3830
                CPLJSONObject oItem = oArrayItem[idx];
74✔
3831
                const auto eArrayItemType = oItem.GetType();
37✔
3832
                if (eArrayItemType == CPLJSONObject::Type::String)
37✔
3833
                {
3834
                    CPLString osVal = oItem.ToString();
36✔
3835
                    const char *pszVal = osVal.c_str();
12✔
3836
                    if (pszVal[0] == '\0' || strchr(pszVal, ' ') ||
12✔
3837
                        strstr(pszVal, "\\n") || strstr(pszVal, "\\r"))
7✔
3838
                    {
3839
                        osVal.replaceAll("\\n", "\n");
5✔
3840
                        osVal.replaceAll("\\r", "\r");
5✔
3841
                        VSIFPrintfL(fp, "\"%s\"", osVal.c_str());
5✔
3842
                    }
3843
                    else if (nFirstPos < WIDTH &&
7✔
3844
                             nCurPos + strlen(pszVal) > WIDTH)
7✔
3845
                    {
3846
                        if (idx > 0)
1✔
3847
                        {
3848
                            VSIFPrintfL(fp, "\n");
1✔
3849
                            for (size_t j = 0; j < nFirstPos; j++)
16✔
3850
                            {
3851
                                const char chSpace = ' ';
15✔
3852
                                VSIFWriteL(&chSpace, 1, 1, fp);
15✔
3853
                            }
3854
                            nCurPos = nFirstPos;
1✔
3855
                        }
3856

3857
                        for (int j = 0; pszVal[j] != '\0'; j++)
102✔
3858
                        {
3859
                            nCurPos++;
101✔
3860
                            if (nCurPos == WIDTH && pszVal[j + 1] != '\0')
101✔
3861
                            {
3862
                                VSIFPrintfL(fp, "-\n");
1✔
3863
                                for (size_t k = 0; k < nFirstPos; k++)
16✔
3864
                                {
3865
                                    const char chSpace = ' ';
15✔
3866
                                    VSIFWriteL(&chSpace, 1, 1, fp);
15✔
3867
                                }
3868
                                nCurPos = nFirstPos + 1;
1✔
3869
                            }
3870
                            VSIFWriteL(&pszVal[j], 1, 1, fp);
101✔
3871
                        }
1✔
3872
                    }
3873
                    else
3874
                    {
3875
                        VSIFPrintfL(fp, "%s", pszVal);
6✔
3876
                        nCurPos += strlen(pszVal);
6✔
3877
                    }
3878
                }
3879
                else if (eArrayItemType == CPLJSONObject::Type::Integer)
25✔
3880
                {
3881
                    const int nVal = oItem.ToInteger();
16✔
3882
                    const char *pszVal = CPLSPrintf("%d", nVal);
16✔
3883
                    const size_t nValLen = strlen(pszVal);
16✔
3884
                    if (nFirstPos < WIDTH && idx > 0 &&
16✔
3885
                        nCurPos + nValLen > WIDTH)
12✔
3886
                    {
3887
                        VSIFPrintfL(fp, "\n");
1✔
3888
                        for (size_t j = 0; j < nFirstPos; j++)
16✔
3889
                        {
3890
                            const char chSpace = ' ';
15✔
3891
                            VSIFWriteL(&chSpace, 1, 1, fp);
15✔
3892
                        }
3893
                        nCurPos = nFirstPos;
1✔
3894
                    }
3895
                    VSIFPrintfL(fp, "%d", nVal);
16✔
3896
                    nCurPos += nValLen;
16✔
3897
                }
3898
                else if (eArrayItemType == CPLJSONObject::Type::Double)
9✔
3899
                {
3900
                    const double dfVal = oItem.ToDouble();
9✔
3901
                    CPLString osVal;
9✔
3902
                    if (dfVal >= INT_MIN && dfVal <= INT_MAX &&
9✔
3903
                        static_cast<int>(dfVal) == dfVal)
9✔
3904
                    {
3905
                        osVal = CPLSPrintf("%d.0", static_cast<int>(dfVal));
8✔
3906
                    }
3907
                    else
3908
                    {
3909
                        osVal = CPLSPrintf("%.17g", dfVal);
1✔
3910
                    }
3911
                    const size_t nValLen = osVal.size();
9✔
3912
                    if (nFirstPos < WIDTH && idx > 0 &&
9✔
3913
                        nCurPos + nValLen > WIDTH)
8✔
3914
                    {
3915
                        VSIFPrintfL(fp, "\n");
1✔
3916
                        for (size_t j = 0; j < nFirstPos; j++)
16✔
3917
                        {
3918
                            const char chSpace = ' ';
15✔
3919
                            VSIFWriteL(&chSpace, 1, 1, fp);
15✔
3920
                        }
3921
                        nCurPos = nFirstPos;
1✔
3922
                    }
3923
                    VSIFPrintfL(fp, "%s", osVal.c_str());
9✔
3924
                    nCurPos += nValLen;
9✔
3925
                }
3926
                if (idx < nLength - 1)
37✔
3927
                {
3928
                    VSIFPrintfL(fp, ", ");
28✔
3929
                    nCurPos += 2;
28✔
3930
                }
3931
            }
3932
            VSIFPrintfL(fp, ")\n");
9✔
3933
        }
3934
    }
3935
}
1,329✔
3936

3937
/************************************************************************/
3938
/*                           Create()                                   */
3939
/************************************************************************/
3940

3941
GDALDataset *ISIS3Dataset::Create(const char *pszFilename, int nXSize,
159✔
3942
                                  int nYSize, int nBandsIn, GDALDataType eType,
3943
                                  char **papszOptions)
3944
{
3945
    if (eType != GDT_Byte && eType != GDT_UInt16 && eType != GDT_Int16 &&
159✔
3946
        eType != GDT_Float32)
3947
    {
3948
        CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type");
27✔
3949
        return nullptr;
27✔
3950
    }
3951
    if (nBandsIn == 0 || nBandsIn > 32767)
132✔
3952
    {
3953
        CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
1✔
3954
        return nullptr;
1✔
3955
    }
3956

3957
    const char *pszDataLocation =
3958
        CSLFetchNameValueDef(papszOptions, "DATA_LOCATION", "LABEL");
131✔
3959
    const bool bIsTiled = CPLFetchBool(papszOptions, "TILED", false);
131✔
3960
    const int nBlockXSize = std::max(
3961
        1, atoi(CSLFetchNameValueDef(papszOptions, "BLOCKXSIZE", "256")));
131✔
3962
    const int nBlockYSize = std::max(
3963
        1, atoi(CSLFetchNameValueDef(papszOptions, "BLOCKYSIZE", "256")));
131✔
3964
    if (!EQUAL(pszDataLocation, "LABEL") &&
165✔
3965
        !EQUAL(CPLGetExtensionSafe(pszFilename).c_str(), "LBL"))
165✔
3966
    {
3967
        CPLError(CE_Failure, CPLE_NotSupported,
1✔
3968
                 "For DATA_LOCATION=%s, "
3969
                 "the main filename should have a .lbl extension",
3970
                 pszDataLocation);
3971
        return nullptr;
1✔
3972
    }
3973

3974
    const char *pszPermission =
3975
        VSISupportsRandomWrite(pszFilename, true) ? "wb+" : "wb";
130✔
3976

3977
    VSILFILE *fp = VSIFOpenExL(pszFilename, pszPermission, true);
130✔
3978
    if (fp == nullptr)
130✔
3979
    {
3980
        CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s", pszFilename,
3✔
3981
                 VSIGetLastErrorMsg());
3982
        return nullptr;
3✔
3983
    }
3984
    VSILFILE *fpImage = nullptr;
127✔
3985
    std::string osExternalFilename;
254✔
3986
    GDALDataset *poExternalDS = nullptr;
127✔
3987
    bool bGeoTIFFAsRegularExternal = false;
127✔
3988
    if (EQUAL(pszDataLocation, "EXTERNAL"))
127✔
3989
    {
3990
        osExternalFilename = CSLFetchNameValueDef(
3991
            papszOptions, "EXTERNAL_FILENAME",
3992
            CPLResetExtensionSafe(pszFilename, "cub").c_str());
14✔
3993
        fpImage = VSIFOpenExL(osExternalFilename.c_str(), pszPermission, true);
14✔
3994
        if (fpImage == nullptr)
14✔
3995
        {
3996
            CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s: %s",
1✔
3997
                     osExternalFilename.c_str(), VSIGetLastErrorMsg());
3998
            VSIFCloseL(fp);
1✔
3999
            return nullptr;
1✔
4000
        }
4001
    }
4002
    else if (EQUAL(pszDataLocation, "GEOTIFF"))
113✔
4003
    {
4004
        osExternalFilename = CSLFetchNameValueDef(
4005
            papszOptions, "EXTERNAL_FILENAME",
4006
            CPLResetExtensionSafe(pszFilename, "tif").c_str());
19✔
4007
        GDALDriver *poDrv =
4008
            static_cast<GDALDriver *>(GDALGetDriverByName("GTiff"));
19✔
4009
        if (poDrv == nullptr)
19✔
4010
        {
4011
            CPLError(CE_Failure, CPLE_AppDefined, "Cannot find GTiff driver");
×
4012
            VSIFCloseL(fp);
×
4013
            return nullptr;
×
4014
        }
4015
        char **papszGTiffOptions = nullptr;
19✔
4016
        papszGTiffOptions =
4017
            CSLSetNameValue(papszGTiffOptions, "ENDIANNESS", "LITTLE");
19✔
4018
        if (bIsTiled)
19✔
4019
        {
4020
            papszGTiffOptions =
4021
                CSLSetNameValue(papszGTiffOptions, "TILED", "YES");
3✔
4022
            papszGTiffOptions = CSLSetNameValue(papszGTiffOptions, "BLOCKXSIZE",
3✔
4023
                                                CPLSPrintf("%d", nBlockXSize));
4024
            papszGTiffOptions = CSLSetNameValue(papszGTiffOptions, "BLOCKYSIZE",
3✔
4025
                                                CPLSPrintf("%d", nBlockYSize));
4026
        }
4027
        const char *pszGTiffOptions =
4028
            CSLFetchNameValueDef(papszOptions, "GEOTIFF_OPTIONS", "");
19✔
4029
        char **papszTokens = CSLTokenizeString2(pszGTiffOptions, ",", 0);
19✔
4030
        for (int i = 0; papszTokens[i] != nullptr; i++)
28✔
4031
        {
4032
            papszGTiffOptions = CSLAddString(papszGTiffOptions, papszTokens[i]);
9✔
4033
        }
4034
        CSLDestroy(papszTokens);
19✔
4035

4036
        // If the user didn't specify any compression and
4037
        // GEOTIFF_AS_REGULAR_EXTERNAL is set (or unspecified), then the
4038
        // GeoTIFF file can be seen as a regular external raw file, provided
4039
        // we make some provision on its organization.
4040
        if (CSLFetchNameValue(papszGTiffOptions, "COMPRESS") == nullptr &&
29✔
4041
            CPLFetchBool(papszOptions, "GEOTIFF_AS_REGULAR_EXTERNAL", true))
10✔
4042
        {
4043
            bGeoTIFFAsRegularExternal = true;
9✔
4044
            papszGTiffOptions =
4045
                CSLSetNameValue(papszGTiffOptions, "INTERLEAVE", "BAND");
9✔
4046
            // Will make sure that our blocks at nodata are not optimized
4047
            // away but indeed well written
4048
            papszGTiffOptions = CSLSetNameValue(
9✔
4049
                papszGTiffOptions, "@WRITE_EMPTY_TILES_SYNCHRONOUSLY", "YES");
4050
            if (!bIsTiled && nBandsIn > 1)
9✔
4051
            {
4052
                papszGTiffOptions =
4053
                    CSLSetNameValue(papszGTiffOptions, "BLOCKYSIZE", "1");
1✔
4054
            }
4055
        }
4056

4057
        poExternalDS = poDrv->Create(osExternalFilename.c_str(), nXSize, nYSize,
19✔
4058
                                     nBandsIn, eType, papszGTiffOptions);
4059
        CSLDestroy(papszGTiffOptions);
19✔
4060
        if (poExternalDS == nullptr)
19✔
4061
        {
4062
            CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s",
1✔
4063
                     osExternalFilename.c_str());
4064
            VSIFCloseL(fp);
1✔
4065
            return nullptr;
1✔
4066
        }
4067
    }
4068

4069
    ISIS3Dataset *poDS = new ISIS3Dataset();
125✔
4070
    poDS->SetDescription(pszFilename);
125✔
4071
    poDS->eAccess = GA_Update;
125✔
4072
    poDS->nRasterXSize = nXSize;
125✔
4073
    poDS->nRasterYSize = nYSize;
125✔
4074
    poDS->m_osExternalFilename = std::move(osExternalFilename);
125✔
4075
    poDS->m_poExternalDS = poExternalDS;
125✔
4076
    poDS->m_bGeoTIFFAsRegularExternal = bGeoTIFFAsRegularExternal;
125✔
4077
    if (bGeoTIFFAsRegularExternal)
125✔
4078
        poDS->m_bGeoTIFFInitDone = false;
8✔
4079
    poDS->m_fpLabel = fp;
125✔
4080
    poDS->m_fpImage = fpImage ? fpImage : fp;
125✔
4081
    poDS->m_bIsLabelWritten = false;
125✔
4082
    poDS->m_bIsTiled = bIsTiled;
125✔
4083
    poDS->m_bInitToNodata = (poDS->m_poExternalDS == nullptr);
125✔
4084
    poDS->m_osComment = CSLFetchNameValueDef(papszOptions, "COMMENT", "");
125✔
4085
    poDS->m_osLatitudeType =
4086
        CSLFetchNameValueDef(papszOptions, "LATITUDE_TYPE", "");
125✔
4087
    poDS->m_osLongitudeDirection =
4088
        CSLFetchNameValueDef(papszOptions, "LONGITUDE_DIRECTION", "");
125✔
4089
    poDS->m_osTargetName =
4090
        CSLFetchNameValueDef(papszOptions, "TARGET_NAME", "");
125✔
4091
    poDS->m_bForce360 = CPLFetchBool(papszOptions, "FORCE_360", false);
125✔
4092
    poDS->m_bWriteBoundingDegrees =
125✔
4093
        CPLFetchBool(papszOptions, "WRITE_BOUNDING_DEGREES", true);
125✔
4094
    poDS->m_osBoundingDegrees =
4095
        CSLFetchNameValueDef(papszOptions, "BOUNDING_DEGREES", "");
125✔
4096
    poDS->m_bUseSrcLabel = CPLFetchBool(papszOptions, "USE_SRC_LABEL", true);
125✔
4097
    poDS->m_bUseSrcMapping =
125✔
4098
        CPLFetchBool(papszOptions, "USE_SRC_MAPPING", false);
125✔
4099
    poDS->m_bUseSrcHistory =
125✔
4100
        CPLFetchBool(papszOptions, "USE_SRC_HISTORY", true);
125✔
4101
    poDS->m_bAddGDALHistory =
125✔
4102
        CPLFetchBool(papszOptions, "ADD_GDAL_HISTORY", true);
125✔
4103
    if (poDS->m_bAddGDALHistory)
125✔
4104
    {
4105
        poDS->m_osGDALHistory =
4106
            CSLFetchNameValueDef(papszOptions, "GDAL_HISTORY", "");
123✔
4107
    }
4108
    const double dfNoData = (eType == GDT_Byte)     ? ISIS3_NULL1
125✔
4109
                            : (eType == GDT_UInt16) ? ISIS3_NULLU2
4110
                            : (eType == GDT_Int16)
4111
                                ? ISIS3_NULL2
4112
                                :
4113
                                /*(eType == GDT_Float32) ?*/ ISIS3_NULL4;
4114

4115
    for (int i = 0; i < nBandsIn; i++)
291✔
4116
    {
4117
        GDALRasterBand *poBand = nullptr;
166✔
4118

4119
        if (poDS->m_poExternalDS != nullptr)
166✔
4120
        {
4121
            ISIS3WrapperRasterBand *poISISBand = new ISIS3WrapperRasterBand(
4122
                poDS->m_poExternalDS->GetRasterBand(i + 1));
24✔
4123
            poBand = poISISBand;
24✔
4124
        }
4125
        else if (bIsTiled)
142✔
4126
        {
4127
            ISISTiledBand *poISISBand = new ISISTiledBand(
4128
                poDS, poDS->m_fpImage, i + 1, eType, nBlockXSize, nBlockYSize,
4129
                0,  // nSkipBytes, to be hacked
4130
                // afterwards for in-label imagery
4131
                0, 0, CPL_IS_LSB);
10✔
4132

4133
            poBand = poISISBand;
10✔
4134
        }
4135
        else
4136
        {
4137
            const int nPixelOffset = GDALGetDataTypeSizeBytes(eType);
132✔
4138
            const int nLineOffset = nPixelOffset * nXSize;
132✔
4139
            const vsi_l_offset nBandOffset =
132✔
4140
                static_cast<vsi_l_offset>(nLineOffset) * nYSize;
132✔
4141
            ISIS3RawRasterBand *poISISBand = new ISIS3RawRasterBand(
4142
                poDS, i + 1, poDS->m_fpImage,
4143
                nBandOffset * i,  // nImgOffset, to be
132✔
4144
                // hacked afterwards for in-label imagery
4145
                nPixelOffset, nLineOffset, eType, CPL_IS_LSB);
132✔
4146

4147
            poBand = poISISBand;
132✔
4148
        }
4149
        poDS->SetBand(i + 1, poBand);
166✔
4150
        poBand->SetNoDataValue(dfNoData);
166✔
4151
    }
4152

4153
    return poDS;
125✔
4154
}
4155

4156
/************************************************************************/
4157
/*                      GetUnderlyingDataset()                          */
4158
/************************************************************************/
4159

4160
static GDALDataset *GetUnderlyingDataset(GDALDataset *poSrcDS)
73✔
4161
{
4162
    if (poSrcDS->GetDriver() != nullptr &&
146✔
4163
        poSrcDS->GetDriver() == GDALGetDriverByName("VRT"))
73✔
4164
    {
4165
        VRTDataset *poVRTDS = reinterpret_cast<VRTDataset *>(poSrcDS);
2✔
4166
        poSrcDS = poVRTDS->GetSingleSimpleSource();
2✔
4167
    }
4168

4169
    return poSrcDS;
73✔
4170
}
4171

4172
/************************************************************************/
4173
/*                            CreateCopy()                              */
4174
/************************************************************************/
4175

4176
GDALDataset *ISIS3Dataset::CreateCopy(const char *pszFilename,
73✔
4177
                                      GDALDataset *poSrcDS, int /*bStrict*/,
4178
                                      char **papszOptions,
4179
                                      GDALProgressFunc pfnProgress,
4180
                                      void *pProgressData)
4181
{
4182
    const char *pszDataLocation =
4183
        CSLFetchNameValueDef(papszOptions, "DATA_LOCATION", "LABEL");
73✔
4184
    GDALDataset *poSrcUnderlyingDS = GetUnderlyingDataset(poSrcDS);
73✔
4185
    if (poSrcUnderlyingDS == nullptr)
73✔
4186
        poSrcUnderlyingDS = poSrcDS;
2✔
4187
    if (EQUAL(pszDataLocation, "GEOTIFF") &&
83✔
4188
        strcmp(poSrcUnderlyingDS->GetDescription(),
10✔
4189
               CSLFetchNameValueDef(
4190
                   papszOptions, "EXTERNAL_FILENAME",
4191
                   CPLResetExtensionSafe(pszFilename, "tif").c_str())) == 0)
83✔
4192
    {
4193
        CPLError(CE_Failure, CPLE_NotSupported,
1✔
4194
                 "Output file has same name as input file");
4195
        return nullptr;
1✔
4196
    }
4197
    if (poSrcDS->GetRasterCount() == 0)
72✔
4198
    {
4199
        CPLError(CE_Failure, CPLE_NotSupported, "Unsupported band count");
1✔
4200
        return nullptr;
1✔
4201
    }
4202

4203
    const int nXSize = poSrcDS->GetRasterXSize();
71✔
4204
    const int nYSize = poSrcDS->GetRasterYSize();
71✔
4205
    const int nBands = poSrcDS->GetRasterCount();
71✔
4206
    GDALDataType eType = poSrcDS->GetRasterBand(1)->GetRasterDataType();
71✔
4207
    ISIS3Dataset *poDS = reinterpret_cast<ISIS3Dataset *>(
4208
        Create(pszFilename, nXSize, nYSize, nBands, eType, papszOptions));
71✔
4209
    if (poDS == nullptr)
71✔
4210
        return nullptr;
10✔
4211
    poDS->m_osFromFilename = poSrcUnderlyingDS->GetDescription();
61✔
4212

4213
    GDALGeoTransform gt;
61✔
4214
    if (poSrcDS->GetGeoTransform(gt) == CE_None && gt != GDALGeoTransform())
61✔
4215
    {
4216
        poDS->SetGeoTransform(gt);
37✔
4217
    }
4218

4219
    auto poSrcSRS = poSrcDS->GetSpatialRef();
61✔
4220
    if (poSrcSRS)
61✔
4221
    {
4222
        poDS->SetSpatialRef(poSrcSRS);
37✔
4223
    }
4224

4225
    for (int i = 1; i <= nBands; i++)
147✔
4226
    {
4227
        const double dfOffset = poSrcDS->GetRasterBand(i)->GetOffset();
86✔
4228
        if (dfOffset != 0.0)
86✔
4229
            poDS->GetRasterBand(i)->SetOffset(dfOffset);
1✔
4230

4231
        const double dfScale = poSrcDS->GetRasterBand(i)->GetScale();
86✔
4232
        if (dfScale != 1.0)
86✔
4233
            poDS->GetRasterBand(i)->SetScale(dfScale);
1✔
4234
    }
4235

4236
    // Do we need to remap nodata ?
4237
    int bHasNoData = FALSE;
61✔
4238
    poDS->m_dfSrcNoData =
61✔
4239
        poSrcDS->GetRasterBand(1)->GetNoDataValue(&bHasNoData);
61✔
4240
    poDS->m_bHasSrcNoData = CPL_TO_BOOL(bHasNoData);
61✔
4241

4242
    if (poDS->m_bUseSrcLabel)
61✔
4243
    {
4244
        char **papszMD_ISIS3 = poSrcDS->GetMetadata("json:ISIS3");
53✔
4245
        if (papszMD_ISIS3 != nullptr)
53✔
4246
        {
4247
            poDS->SetMetadata(papszMD_ISIS3, "json:ISIS3");
18✔
4248
        }
4249
    }
4250

4251
    // We don't need to initialize the imagery as we are going to copy it
4252
    // completely
4253
    poDS->m_bInitToNodata = false;
61✔
4254
    CPLErr eErr = GDALDatasetCopyWholeRaster(poSrcDS, poDS, nullptr,
61✔
4255
                                             pfnProgress, pProgressData);
4256
    poDS->FlushCache(false);
61✔
4257
    poDS->m_bHasSrcNoData = false;
61✔
4258
    if (eErr != CE_None)
61✔
4259
    {
4260
        delete poDS;
11✔
4261
        return nullptr;
11✔
4262
    }
4263

4264
    return poDS;
50✔
4265
}
4266

4267
/************************************************************************/
4268
/*                         GDALRegister_ISIS3()                         */
4269
/************************************************************************/
4270

4271
void GDALRegister_ISIS3()
1,911✔
4272

4273
{
4274
    if (GDALGetDriverByName(ISIS3_DRIVER_NAME) != nullptr)
1,911✔
4275
        return;
282✔
4276

4277
    GDALDriver *poDriver = new GDALDriver();
1,629✔
4278
    ISIS3DriverSetCommonMetadata(poDriver);
1,629✔
4279

4280
    poDriver->pfnOpen = ISIS3Dataset::Open;
1,629✔
4281
    poDriver->pfnCreate = ISIS3Dataset::Create;
1,629✔
4282
    poDriver->pfnCreateCopy = ISIS3Dataset::CreateCopy;
1,629✔
4283

4284
    GetGDALDriverManager()->RegisterDriver(poDriver);
1,629✔
4285
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc