Coverage Report

Created: 2026-08-11 08:26

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/pds/pds4dataset.h
Line
Count
Source
1
/******************************************************************************
2
 *
3
 * Project:  PDS 4 Driver; Planetary Data System Format
4
 * Purpose:  Implementation of PDS4Dataset
5
 * Author:   Even Rouault, even.rouault at spatialys.com
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2019, Hobu Inc
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ****************************************************************************/
12
13
#pragma once
14
15
#include "cpl_string.h"
16
#include "gdal_priv.h"
17
#include "gdal_proxy.h"
18
#include "ogreditablelayer.h"
19
#include "rawdataset.h"
20
#include "ogr_spatialref.h"
21
22
#include <array>
23
#include <vector>
24
25
class PDS4Dataset;
26
27
/************************************************************************/
28
/* ==================================================================== */
29
/*                        PDS4TableBaseLayer                            */
30
/* ==================================================================== */
31
/************************************************************************/
32
33
class PDS4TableBaseLayer CPL_NON_FINAL : public OGRLayer
34
{
35
  protected:
36
    PDS4Dataset *m_poDS = nullptr;
37
    OGRFeatureDefn *m_poRawFeatureDefn = nullptr;
38
    OGRFeatureDefn *m_poFeatureDefn = nullptr;
39
    CPLString m_osFilename{};
40
    int m_iLatField = -1;
41
    int m_iLongField = -1;
42
    int m_iAltField = -1;
43
    int m_iWKT = -1;
44
    bool m_bKeepGeomColmuns = false;
45
    bool m_bDirtyHeader = false;
46
    VSILFILE *m_fp = nullptr;
47
    GIntBig m_nFeatureCount = -1;
48
    GIntBig m_nFID = 1;
49
    vsi_l_offset m_nOffset = 0;
50
    CPLStringList m_aosLCO{};
51
    std::string m_osLineEnding{};
52
53
    void SetupGeomField();
54
    OGRFeature *AddGeometryFromFields(OGRFeature *poFeature);
55
    OGRFeature *AddFieldsFromGeometry(OGRFeature *poFeature);
56
    void MarkHeaderDirty();
57
    CPLXMLNode *RefreshFileAreaObservationalBeginningCommon(
58
        CPLXMLNode *psFAO, const CPLString &osPrefix,
59
        const char *pszTableEltName, CPLString &osDescription);
60
    void ParseLineEndingOption(CSLConstList papszOptions);
61
62
    CPL_DISALLOW_COPY_ASSIGN(PDS4TableBaseLayer)
63
64
  public:
65
    PDS4TableBaseLayer(PDS4Dataset *poDS, const char *pszName,
66
                       const char *pszFilename);
67
    ~PDS4TableBaseLayer() override;
68
69
    using OGRLayer::GetLayerDefn;
70
71
    const OGRFeatureDefn *GetLayerDefn() const override
72
1.74M
    {
73
1.74M
        return m_poFeatureDefn;
74
1.74M
    }
75
76
    GIntBig GetFeatureCount(int bForce) override;
77
78
    const char *GetFileName() const
79
5.46k
    {
80
5.46k
        return m_osFilename.c_str();
81
5.46k
    }
82
83
    bool IsDirtyHeader() const
84
2.78k
    {
85
2.78k
        return m_bDirtyHeader;
86
2.78k
    }
87
88
    int GetRawFieldCount() const
89
2.78k
    {
90
2.78k
        return m_poRawFeatureDefn->GetFieldCount();
91
2.78k
    }
92
93
    bool RenameFileTo(const char *pszNewName);
94
    virtual char **GetFileList() const;
95
96
    virtual void RefreshFileAreaObservational(CPLXMLNode *psFAO) = 0;
97
98
    GDALDataset *GetDataset() override;
99
};
100
101
/************************************************************************/
102
/* ==================================================================== */
103
/*                        PDS4FixedWidthTable                           */
104
/* ==================================================================== */
105
/************************************************************************/
106
107
template <class T> class PDS4EditableSynchronizer;
108
109
class PDS4FixedWidthTable CPL_NON_FINAL : public PDS4TableBaseLayer
110
{
111
    friend class PDS4EditableSynchronizer<PDS4FixedWidthTable>;
112
113
  protected:
114
    int m_nRecordSize = 0;
115
    CPLString m_osBuffer{};
116
117
    struct Field
118
    {
119
        int m_nOffset = 0;  // in XML 1-based, here 0-based
120
        int m_nLength = 0;
121
        CPLString m_osDataType{};
122
        CPLString m_osUnit{};
123
        CPLString m_osDescription{};
124
        CPLString m_osSpecialConstantsXML{};
125
    };
126
127
    std::vector<Field> m_aoFields{};
128
129
    virtual CPLString GetSubType() const = 0;
130
131
    virtual bool CreateFieldInternal(OGRFieldType eType,
132
                                     OGRFieldSubType eSubType, int nWidth,
133
                                     Field &f) = 0;
134
135
    bool ReadFields(const CPLXMLNode *psParent, int nBaseOffset,
136
                    const CPLString &osSuffixFieldName);
137
138
  public:
139
    PDS4FixedWidthTable(PDS4Dataset *poDS, const char *pszName,
140
                        const char *pszFilename);
141
142
    void ResetReading() override;
143
    OGRFeature *GetFeature(GIntBig nFID) override;
144
    OGRFeature *GetNextFeature() override;
145
    int TestCapability(const char *) const override;
146
    OGRErr ISetFeature(OGRFeature *poFeature) override;
147
    OGRErr ICreateFeature(OGRFeature *poFeature) override;
148
    OGRErr CreateField(const OGRFieldDefn *poFieldIn, int) override;
149
150
    bool ReadTableDef(const CPLXMLNode *psTable);
151
152
    bool InitializeNewLayer(const OGRSpatialReference *poSRS,
153
                            bool bForceGeographic, OGRwkbGeometryType eGType,
154
                            const char *const *papszOptions);
155
156
    virtual PDS4FixedWidthTable *NewLayer(PDS4Dataset *poDS,
157
                                          const char *pszName,
158
                                          const char *pszFilename) = 0;
159
160
    void RefreshFileAreaObservational(CPLXMLNode *psFAO) override;
161
};
162
163
/************************************************************************/
164
/* ==================================================================== */
165
/*                        PDS4TableCharacter                            */
166
/* ==================================================================== */
167
/************************************************************************/
168
169
class PDS4TableCharacter final : public PDS4FixedWidthTable
170
{
171
    CPLString GetSubType() const override
172
0
    {
173
0
        return "Character";
174
0
    }
175
176
    bool CreateFieldInternal(OGRFieldType eType, OGRFieldSubType eSubType,
177
                             int nWidth, Field &f) override;
178
179
  public:
180
    PDS4TableCharacter(PDS4Dataset *poDS, const char *pszName,
181
                       const char *pszFilename);
182
183
    PDS4FixedWidthTable *NewLayer(PDS4Dataset *poDS, const char *pszName,
184
                                  const char *pszFilename) override
185
0
    {
186
0
        return new PDS4TableCharacter(poDS, pszName, pszFilename);
187
0
    }
188
};
189
190
/************************************************************************/
191
/* ==================================================================== */
192
/*                        PDS4TableBinary                               */
193
/* ==================================================================== */
194
/************************************************************************/
195
196
class PDS4TableBinary final : public PDS4FixedWidthTable
197
{
198
    CPLString GetSubType() const override
199
0
    {
200
0
        return "Binary";
201
0
    }
202
203
    bool CreateFieldInternal(OGRFieldType eType, OGRFieldSubType eSubType,
204
                             int nWidth, Field &f) override;
205
206
  public:
207
    PDS4TableBinary(PDS4Dataset *poDS, const char *pszName,
208
                    const char *pszFilename);
209
210
    PDS4FixedWidthTable *NewLayer(PDS4Dataset *poDS, const char *pszName,
211
                                  const char *pszFilename) override
212
0
    {
213
0
        return new PDS4TableBinary(poDS, pszName, pszFilename);
214
0
    }
215
};
216
217
/************************************************************************/
218
/* ==================================================================== */
219
/*                        PDS4DelimitedTable                            */
220
/* ==================================================================== */
221
/************************************************************************/
222
223
class PDS4DelimitedTable CPL_NON_FINAL : public PDS4TableBaseLayer
224
{
225
    friend class PDS4EditableSynchronizer<PDS4DelimitedTable>;
226
227
  protected:
228
    bool m_bCreation = false;
229
    char m_chFieldDelimiter = ',';
230
    bool m_bAddWKTColumnPending = false;
231
232
    struct Field
233
    {
234
        CPLString m_osDataType{};
235
        CPLString m_osUnit{};
236
        CPLString m_osDescription{};
237
        CPLString m_osSpecialConstantsXML{};
238
        CPLString m_osMissingConstant{};  // included in above potentially
239
    };
240
241
    std::vector<Field> m_aoFields{};
242
243
    OGRFeature *GetNextFeatureRaw();
244
    CPLString QuoteIfNeeded(const char *pszVal);
245
    void GenerateVRT();
246
247
    bool ReadFields(const CPLXMLNode *psParent,
248
                    const CPLString &osSuffixFieldName);
249
250
  public:
251
    PDS4DelimitedTable(PDS4Dataset *poDS, const char *pszName,
252
                       const char *pszFilename);
253
    ~PDS4DelimitedTable() override;
254
255
    void ResetReading() override;
256
    OGRFeature *GetNextFeature() override;
257
    int TestCapability(const char *) const override;
258
    OGRErr ICreateFeature(OGRFeature *poFeature) override;
259
    OGRErr CreateField(const OGRFieldDefn *poFieldIn, int) override;
260
261
    bool ReadTableDef(const CPLXMLNode *psTable);
262
263
    bool InitializeNewLayer(const OGRSpatialReference *poSRS,
264
                            bool bForceGeographic, OGRwkbGeometryType eGType,
265
                            const char *const *papszOptions);
266
267
    void RefreshFileAreaObservational(CPLXMLNode *psFAO) override;
268
    char **GetFileList() const override;
269
270
    PDS4DelimitedTable *NewLayer(PDS4Dataset *poDS, const char *pszName,
271
                                 const char *pszFilename)
272
279
    {
273
279
        return new PDS4DelimitedTable(poDS, pszName, pszFilename);
274
279
    }
275
};
276
277
/************************************************************************/
278
/* ==================================================================== */
279
/*                         PDS4EditableLayer                            */
280
/* ==================================================================== */
281
/************************************************************************/
282
283
class PDS4EditableLayer final : public OGREditableLayer
284
{
285
    PDS4TableBaseLayer *GetBaseLayer() const;
286
287
  public:
288
    explicit PDS4EditableLayer(
289
        std::unique_ptr<PDS4FixedWidthTable> poBaseLayer);
290
    explicit PDS4EditableLayer(std::unique_ptr<PDS4DelimitedTable> poBaseLayer);
291
    ~PDS4EditableLayer() override;
292
293
    void RefreshFileAreaObservational(CPLXMLNode *psFAO)
294
2.78k
    {
295
2.78k
        GetBaseLayer()->RefreshFileAreaObservational(psFAO);
296
2.78k
    }
297
298
    const char *GetFileName() const
299
2.78k
    {
300
2.78k
        return GetBaseLayer()->GetFileName();
301
2.78k
    }
302
303
    bool IsDirtyHeader() const
304
2.78k
    {
305
2.78k
        return GetBaseLayer()->IsDirtyHeader();
306
2.78k
    }
307
308
    int GetRawFieldCount() const
309
2.78k
    {
310
2.78k
        return GetBaseLayer()->GetRawFieldCount();
311
2.78k
    }
312
313
    void SetSpatialRef(OGRSpatialReference *poSRS);
314
315
    char **GetFileList() const
316
2.40k
    {
317
2.40k
        return GetBaseLayer()->GetFileList();
318
2.40k
    }
319
};
320
321
/************************************************************************/
322
/*                             PDS4Dataset                              */
323
/************************************************************************/
324
325
class PDS4Dataset final : public RawDataset
326
{
327
    friend class PDS4RawRasterBand;
328
    friend class PDS4WrapperRasterBand;
329
330
    VSILFILE *m_fpImage = nullptr;
331
    vsi_l_offset m_nBaseOffset = 0;
332
    GDALDataset *m_poExternalDS = nullptr;  // external dataset (GeoTIFF)
333
    OGRSpatialReference m_oSRS{};
334
    bool m_bGotTransform = false;
335
    GDALGeoTransform m_gt{};
336
    CPLString m_osXMLFilename{};
337
    CPLString m_osImageFilename{};
338
    CPLString m_osUnits{};
339
    bool m_bCreatedFromExistingBinaryFile = false;
340
341
    std::vector<std::unique_ptr<PDS4EditableLayer>> m_apoLayers{};
342
343
    // Write dedicated parameters
344
    bool m_bMustInitImageFile = false;
345
    bool m_bUseSrcLabel = true;
346
    bool m_bDirtyHeader = false;
347
    bool m_bCreateHeader = false;
348
    bool m_bStripFileAreaObservationalFromTemplate = false;
349
    bool m_bIsLSB = true;
350
    CPLString m_osHeaderParsingStandard{};
351
    CPLString m_osInterleave{};
352
    char **m_papszCreationOptions = nullptr;
353
    CPLString m_osXMLPDS4{};
354
355
    void CreateHeader(CPLXMLNode *psProduct, const char *pszCARTVersion);
356
    void WriteHeader();
357
    void WriteHeaderAppendCase();
358
    void WriteVectorLayers(CPLXMLNode *psProduct);
359
    void WriteArray(const CPLString &osPrefix, CPLXMLNode *psFAO,
360
                    const char *pszLocalIdentifier,
361
                    CPLXMLNode *psTemplateSpecialConstants);
362
    void WriteGeoreferencing(CPLXMLNode *psCart, const char *pszCARTVersion);
363
    void ReadGeoreferencing(CPLXMLNode *psProduct);
364
    bool InitImageFile();
365
366
    void SubstituteVariables(CPLXMLNode *psNode, char **papszDict);
367
368
    bool OpenTableCharacter(const char *pszFilename, const CPLXMLNode *psTable);
369
370
    bool OpenTableBinary(const char *pszFilename, const CPLXMLNode *psTable);
371
372
    bool OpenTableDelimited(const char *pszFilename, const CPLXMLNode *psTable);
373
374
    static std::unique_ptr<PDS4Dataset>
375
    CreateInternal(const char *pszFilename, GDALDataset *poSrcDS, int nXSize,
376
                   int nYSize, int nBands, GDALDataType eType,
377
                   const char *const *papszOptions);
378
379
    CPLErr Close(GDALProgressFunc = nullptr, void * = nullptr) override;
380
381
    static std::unique_ptr<PDS4Dataset> OpenBrowse(GDALOpenInfo *,
382
                                                   const CPLXMLNode *);
383
384
    CPL_DISALLOW_COPY_ASSIGN(PDS4Dataset)
385
386
  public:
387
    PDS4Dataset();
388
    ~PDS4Dataset() override;
389
390
    int CloseDependentDatasets() override;
391
392
    const OGRSpatialReference *GetSpatialRef() const override;
393
    CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
394
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
395
    CPLErr SetGeoTransform(const GDALGeoTransform &gt) override;
396
    char **GetFileList() override;
397
    CPLErr SetMetadata(CSLConstList papszMD,
398
                       const char *pszDomain = "") override;
399
400
    int GetLayerCount() const override
401
66.8k
    {
402
66.8k
        return static_cast<int>(m_apoLayers.size());
403
66.8k
    }
404
405
    using GDALDataset::GetLayer;
406
    const OGRLayer *GetLayer(int) const override;
407
408
    OGRLayer *ICreateLayer(const char *pszName,
409
                           const OGRGeomFieldDefn *poGeomFieldDefn,
410
                           CSLConstList papszOptions) override;
411
412
    int TestCapability(const char *pszCap) const override;
413
414
    bool GetRawBinaryLayout(GDALDataset::RawBinaryLayout &) override;
415
416
    static std::unique_ptr<PDS4Dataset> OpenInternal(GDALOpenInfo *);
417
418
    static GDALDataset *Open(GDALOpenInfo *poOpenInfo)
419
916
    {
420
916
        return OpenInternal(poOpenInfo).release();
421
916
    }
422
423
    static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
424
                               int nBands, GDALDataType eType,
425
                               CSLConstList papszOptions);
426
    static GDALDataset *CreateCopy(const char *pszFilename,
427
                                   GDALDataset *poSrcDS, int bStrict,
428
                                   CSLConstList papszOptions,
429
                                   GDALProgressFunc pfnProgress,
430
                                   void *pProgressData);
431
    static CPLErr Delete(const char *pszName);
432
433
    const char *const *GetOpenOptions() const
434
13.0k
    {
435
13.0k
        return papszOpenOptions;
436
13.0k
    }
437
438
    void MarkHeaderDirty()
439
25.6k
    {
440
25.6k
        m_bDirtyHeader = true;
441
25.6k
    }
442
};
443
444
/************************************************************************/
445
/* ==================================================================== */
446
/*                        PDS4RawRasterBand                            */
447
/* ==================================================================== */
448
/************************************************************************/
449
450
class PDS4RawRasterBand final : public RawRasterBand
451
{
452
    friend class PDS4Dataset;
453
454
    bool m_bHasOffset{};
455
    bool m_bHasScale{};
456
    bool m_bHasNoData{};
457
    bool m_bHasNoDataInt64{};
458
    bool m_bHasNoDataUInt64{};
459
    double m_dfOffset{};
460
    double m_dfScale{};
461
    double m_dfNoData{};
462
    int64_t m_nNoDataInt64{};
463
    uint64_t m_nNoDataUInt64{};
464
465
  public:
466
    PDS4RawRasterBand(GDALDataset *l_poDS, int l_nBand, VSILFILE *l_fpRaw,
467
                      vsi_l_offset l_nImgOffset, int l_nPixelOffset,
468
                      int l_nLineOffset, GDALDataType l_eDataType,
469
                      RawRasterBand::ByteOrder eByteOrderIn);
470
471
    CPLErr IWriteBlock(int, int, void *) override;
472
473
    CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
474
                     GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace,
475
                     GDALRasterIOExtraArg *psExtraArg) override;
476
477
    double GetOffset(int *pbSuccess = nullptr) override;
478
    double GetScale(int *pbSuccess = nullptr) override;
479
    CPLErr SetOffset(double dfNewOffset) override;
480
    CPLErr SetScale(double dfNewScale) override;
481
    double GetNoDataValue(int *pbSuccess = nullptr) override;
482
    CPLErr SetNoDataValue(double dfNewNoData) override;
483
    int64_t GetNoDataValueAsInt64(int *pbSuccess = nullptr) override;
484
    uint64_t GetNoDataValueAsUInt64(int *pbSuccess = nullptr) override;
485
    CPLErr SetNoDataValueAsInt64(int64_t nNoData) override;
486
    CPLErr SetNoDataValueAsUInt64(uint64_t nNoData) override;
487
488
    const char *GetUnitType() override
489
10
    {
490
10
        return static_cast<PDS4Dataset *>(poDS)->m_osUnits.c_str();
491
10
    }
492
493
    CPLErr SetUnitType(const char *pszUnits) override
494
1.32k
    {
495
1.32k
        static_cast<PDS4Dataset *>(poDS)->m_osUnits = pszUnits;
496
1.32k
        return CE_None;
497
1.32k
    }
498
499
    void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
500
};
501
502
/************************************************************************/
503
/* ==================================================================== */
504
/*                      PDS4BrowseImageProxyRasterBand                  */
505
/*                                                                      */
506
/*      proxy for bands stored in browse images                         */
507
/* ==================================================================== */
508
/************************************************************************/
509
class PDS4BrowseImageProxyRasterBand final : public GDALProxyRasterBand
510
{
511
    friend class PDS4Dataset;
512
513
    GDALRasterBand *m_poBaseBand{};
514
515
    CPL_DISALLOW_COPY_ASSIGN(PDS4BrowseImageProxyRasterBand)
516
517
  protected:
518
    GDALRasterBand *RefUnderlyingRasterBand(bool /*bForceOpen*/) const override;
519
520
  public:
521
    explicit PDS4BrowseImageProxyRasterBand(GDALRasterBand *poBaseBandIn);
522
};
523
524
/************************************************************************/
525
/* ==================================================================== */
526
/*                         PDS4WrapperRasterBand                       */
527
/*                                                                      */
528
/*      proxy for bands stored in other formats.                        */
529
/* ==================================================================== */
530
/************************************************************************/
531
class PDS4WrapperRasterBand final : public GDALProxyRasterBand
532
{
533
    friend class PDS4Dataset;
534
535
    GDALRasterBand *m_poBaseBand{};
536
    bool m_bHasOffset{};
537
    bool m_bHasScale{};
538
    bool m_bHasNoData{};
539
    bool m_bHasNoDataInt64{};
540
    bool m_bHasNoDataUInt64{};
541
    double m_dfOffset{};
542
    double m_dfScale{1};
543
    double m_dfNoData{};
544
    int64_t m_nNoDataInt64{};
545
    uint64_t m_nNoDataUInt64{};
546
547
    CPL_DISALLOW_COPY_ASSIGN(PDS4WrapperRasterBand)
548
549
  protected:
550
    virtual GDALRasterBand *
551
    RefUnderlyingRasterBand(bool /*bForceOpen*/) const override
552
0
    {
553
0
        return m_poBaseBand;
554
0
    }
555
556
  public:
557
    explicit PDS4WrapperRasterBand(GDALRasterBand *poBaseBandIn);
558
559
    virtual CPLErr Fill(double dfRealValue,
560
                        double dfImaginaryValue = 0) override;
561
    CPLErr IWriteBlock(int, int, void *) override;
562
563
    CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
564
                     GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace,
565
                     GDALRasterIOExtraArg *psExtraArg) override;
566
567
    double GetOffset(int *pbSuccess = nullptr) override;
568
    double GetScale(int *pbSuccess = nullptr) override;
569
    CPLErr SetOffset(double dfNewOffset) override;
570
    CPLErr SetScale(double dfNewScale) override;
571
    double GetNoDataValue(int *pbSuccess = nullptr) override;
572
    CPLErr SetNoDataValue(double dfNewNoData) override;
573
    int64_t GetNoDataValueAsInt64(int *pbSuccess = nullptr) override;
574
    uint64_t GetNoDataValueAsUInt64(int *pbSuccess = nullptr) override;
575
    CPLErr SetNoDataValueAsInt64(int64_t nNoData) override;
576
    CPLErr SetNoDataValueAsUInt64(uint64_t nNoData) override;
577
578
    const char *GetUnitType() override
579
0
    {
580
0
        return static_cast<PDS4Dataset *>(poDS)->m_osUnits.c_str();
581
0
    }
582
583
    CPLErr SetUnitType(const char *pszUnits) override
584
0
    {
585
0
        static_cast<PDS4Dataset *>(poDS)->m_osUnits = pszUnits;
586
0
        return CE_None;
587
0
    }
588
589
    int GetMaskFlags() override
590
0
    {
591
0
        return nMaskFlags;
592
0
    }
593
594
    GDALRasterBand *GetMaskBand() override
595
0
    {
596
0
        return poMask;
597
0
    }
598
599
    void SetMaskBand(std::unique_ptr<GDALRasterBand> poMaskBand);
600
};
601
602
/************************************************************************/
603
/* ==================================================================== */
604
/*                             PDS4MaskBand                             */
605
/* ==================================================================== */
606
607
class PDS4MaskBand final : public GDALRasterBand
608
{
609
    GDALRasterBand *m_poBaseBand{};
610
    void *m_pBuffer{};
611
    std::vector<double> m_adfConstants{};
612
613
    CPL_DISALLOW_COPY_ASSIGN(PDS4MaskBand)
614
615
  public:
616
    PDS4MaskBand(GDALRasterBand *poBaseBand,
617
                 const std::vector<double> &adfConstants);
618
    ~PDS4MaskBand() override;
619
620
    CPLErr IReadBlock(int, int, void *) override;
621
};