Coverage Report

Created: 2025-07-23 09:13

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