Coverage Report

Created: 2025-06-09 08:44

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