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

OSGeo / gdal / 15885686134

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

push

github

rouault
gdal_priv.h: fix C++11 compatibility

573814 of 807237 relevant lines covered (71.08%)

250621.56 hits per line

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

82.38
/frmts/dimap/dimapdataset.cpp
1
/******************************************************************************
2
 *
3
 * Project:  SPOT Dimap Driver
4
 * Purpose:  Implementation of SPOT Dimap driver.
5
 * Author:   Frank Warmerdam, warmerdam@pobox.com
6
 *
7
 * Docs: http://www.spotimage.fr/dimap/spec/documentation/refdoc.htm
8
 *
9
 ******************************************************************************
10
 * Copyright (c) 2007, Frank Warmerdam <warmerdam@pobox.com>
11
 * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
12
 *
13
 * SPDX-License-Identifier: MIT
14
 ****************************************************************************/
15

16
#include "cpl_minixml.h"
17
#include "gdal_frmts.h"
18
#include "gdal_pam.h"
19
#include "ogr_spatialref.h"
20
#include "mdreader/reader_pleiades.h"
21
#include "vrtdataset.h"
22
#include <map>
23
#include <algorithm>
24

25
/************************************************************************/
26
/* ==================================================================== */
27
/*                              DIMAPDataset                            */
28
/* ==================================================================== */
29
/************************************************************************/
30

31
class DIMAPDataset final : public GDALPamDataset
32
{
33
    CPLXMLNode *psProduct{};
34

35
    CPLXMLNode *psProductDim{};    // DIMAP2, DIM_<product_id>.XML
36
    CPLXMLNode *psProductStrip{};  // DIMAP2, STRIP_<product_id>.XML
37
    CPLString osRPCFilename{};     // DIMAP2, RPC_<product_id>.XML
38

39
    VRTDataset *poVRTDS{};
40

41
    int nGCPCount{};
42
    GDAL_GCP *pasGCPList{};
43

44
    OGRSpatialReference m_oSRS{};
45
    OGRSpatialReference m_oGCPSRS{};
46

47
    int bHaveGeoTransform{};
48
    GDALGeoTransform m_gt{};
49

50
    CPLString osMDFilename{};
51
    CPLString osImageDSFilename{};
52
    CPLString osDIMAPFilename{};
53
    int nProductVersion = 1;
54

55
    char **papszXMLDimapMetadata{};
56

57
    CPL_DISALLOW_COPY_ASSIGN(DIMAPDataset)
58

59
  protected:
60
    int CloseDependentDatasets() override;
61

62
    int ReadImageInformation();
63
    int ReadImageInformation2();  // DIMAP 2.
64

65
    void SetMetadataFromXML(CPLXMLNode *psProduct,
66
                            const char *const apszMetadataTranslation[],
67
                            bool bKeysFromRoot = true);
68

69
  public:
70
    DIMAPDataset();
71
    ~DIMAPDataset() override;
72

73
    const OGRSpatialReference *GetSpatialRef() const override;
74
    CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
75
    int GetGCPCount() override;
76
    const OGRSpatialReference *GetGCPSpatialRef() const override;
77
    const GDAL_GCP *GetGCPs() override;
78
    char **GetMetadataDomainList() override;
79
    char **GetMetadata(const char *pszDomain) override;
80
    char **GetFileList() override;
81

82
    CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
83
                     GDALDataType, int, BANDMAP_TYPE, GSpacing, GSpacing,
84
                     GSpacing, GDALRasterIOExtraArg *psExtraArg) override;
85

86
    static int Identify(GDALOpenInfo *);
87
    static GDALDataset *Open(GDALOpenInfo *);
88

89
    CPLXMLNode *GetProduct()
90
    {
91
        return psProduct;
92
    }
93
};
94

95
/************************************************************************/
96
/* ==================================================================== */
97
/*                              DIMAPDataset                            */
98
/* ==================================================================== */
99
/************************************************************************/
100

101
/************************************************************************/
102
/*                             DIMAPDataset()                            */
103
/************************************************************************/
104

105
DIMAPDataset::DIMAPDataset()
10✔
106
{
107
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
10✔
108
    m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
10✔
109
}
10✔
110

111
/************************************************************************/
112
/*                            ~DIMAPDataset()                           */
113
/************************************************************************/
114

115
DIMAPDataset::~DIMAPDataset()
20✔
116

117
{
118
    DIMAPDataset::FlushCache(true);
10✔
119

120
    CPLDestroyXMLNode(psProduct);
10✔
121

122
    if (psProductDim != nullptr && psProductDim != psProduct)
10✔
123
        CPLDestroyXMLNode(psProductDim);
6✔
124
    if (psProductStrip != nullptr)
10✔
125
        CPLDestroyXMLNode(psProductStrip);
6✔
126
    if (nGCPCount > 0)
10✔
127
    {
128
        GDALDeinitGCPs(nGCPCount, pasGCPList);
2✔
129
        CPLFree(pasGCPList);
2✔
130
    }
131

132
    CSLDestroy(papszXMLDimapMetadata);
10✔
133

134
    DIMAPDataset::CloseDependentDatasets();
10✔
135
}
20✔
136

137
/************************************************************************/
138
/*                        CloseDependentDatasets()                      */
139
/************************************************************************/
140

141
int DIMAPDataset::CloseDependentDatasets()
25✔
142
{
143
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
25✔
144

145
    if (poVRTDS != nullptr)
25✔
146
    {
147
        delete poVRTDS;
10✔
148
        poVRTDS = nullptr;
10✔
149
        bHasDroppedRef = TRUE;
10✔
150
    }
151

152
    return bHasDroppedRef;
25✔
153
}
154

155
/************************************************************************/
156
/*                      GetMetadataDomainList()                         */
157
/************************************************************************/
158

159
char **DIMAPDataset::GetMetadataDomainList()
×
160
{
161
    return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
×
162
                                   TRUE, "xml:dimap", nullptr);
×
163
}
164

165
/************************************************************************/
166
/*                            GetMetadata()                             */
167
/*                                                                      */
168
/*      We implement special support for fetching the full product      */
169
/*      metadata as xml.                                                */
170
/************************************************************************/
171

172
char **DIMAPDataset::GetMetadata(const char *pszDomain)
17✔
173

174
{
175
    if (pszDomain && EQUAL(pszDomain, "xml:dimap"))
17✔
176
    {
177
        if (papszXMLDimapMetadata == nullptr)
×
178
        {
179
            papszXMLDimapMetadata =
×
180
                reinterpret_cast<char **>(CPLCalloc(sizeof(char *), 2));
×
181
            papszXMLDimapMetadata[0] = CPLSerializeXMLTree(psProduct);
×
182
        }
183
        return papszXMLDimapMetadata;
×
184
    }
185

186
    return GDALPamDataset::GetMetadata(pszDomain);
17✔
187
}
188

189
/************************************************************************/
190
/*                          GetSpatialRef()                             */
191
/************************************************************************/
192

193
const OGRSpatialReference *DIMAPDataset::GetSpatialRef() const
1✔
194

195
{
196
    if (!m_oSRS.IsEmpty())
1✔
197
        return &m_oSRS;
×
198

199
    return GDALPamDataset::GetSpatialRef();
1✔
200
}
201

202
/************************************************************************/
203
/*                          GetGeoTransform()                           */
204
/************************************************************************/
205

206
CPLErr DIMAPDataset::GetGeoTransform(GDALGeoTransform &gt) const
×
207

208
{
209
    if (bHaveGeoTransform)
×
210
    {
211
        gt = m_gt;
×
212
        return CE_None;
×
213
    }
214

215
    return GDALPamDataset::GetGeoTransform(gt);
×
216
}
217

218
/************************************************************************/
219
/*                            GetFileList()                             */
220
/************************************************************************/
221

222
char **DIMAPDataset::GetFileList()
×
223

224
{
225
    char **papszFileList = GDALPamDataset::GetFileList();
×
226
    char **papszImageFiles = poVRTDS->GetFileList();
×
227

228
    papszFileList = CSLInsertStrings(papszFileList, -1, papszImageFiles);
×
229

230
    CSLDestroy(papszImageFiles);
×
231

232
    return papszFileList;
×
233
}
234

235
/************************************************************************/
236
/* ==================================================================== */
237
/*                            DIMAPRasterBand                           */
238
/* ==================================================================== */
239
/************************************************************************/
240

241
class DIMAPRasterBand final : public GDALPamRasterBand
242
{
243
    friend class DIMAPDataset;
244

245
    VRTSourcedRasterBand *poVRTBand;
246

247
    CPL_DISALLOW_COPY_ASSIGN(DIMAPRasterBand)
248

249
  public:
250
    DIMAPRasterBand(DIMAPDataset *, int, VRTSourcedRasterBand *);
251

252
    ~DIMAPRasterBand() override
60✔
253
    {
30✔
254
    }
60✔
255

256
    CPLErr IReadBlock(int, int, void *) override;
257
    CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
258
                     GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace,
259
                     GDALRasterIOExtraArg *psExtraArg) override;
260
    int GetOverviewCount() override;
261
    GDALRasterBand *GetOverview(int) override;
262
    CPLErr ComputeRasterMinMax(int bApproxOK, double adfMinMax[2]) override;
263
    CPLErr ComputeStatistics(int bApproxOK, double *pdfMin, double *pdfMax,
264
                             double *pdfMean, double *pdfStdDev,
265
                             GDALProgressFunc, void *pProgressData) override;
266

267
    CPLErr GetHistogram(double dfMin, double dfMax, int nBuckets,
268
                        GUIntBig *panHistogram, int bIncludeOutOfRange,
269
                        int bApproxOK, GDALProgressFunc,
270
                        void *pProgressData) override;
271
};
272

273
/************************************************************************/
274
/*                          DIMAPRasterBand()                           */
275
/************************************************************************/
276

277
DIMAPRasterBand::DIMAPRasterBand(DIMAPDataset *poDIMAPDS, int nBandIn,
30✔
278
                                 VRTSourcedRasterBand *poVRTBandIn)
30✔
279
    : poVRTBand(poVRTBandIn)
30✔
280
{
281
    poDS = poDIMAPDS;
30✔
282
    nBand = nBandIn;
30✔
283
    eDataType = poVRTBandIn->GetRasterDataType();
30✔
284

285
    poVRTBandIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
30✔
286
}
30✔
287

288
/************************************************************************/
289
/*                             IReadBlock()                             */
290
/************************************************************************/
291

292
CPLErr DIMAPRasterBand::IReadBlock(int iBlockX, int iBlockY, void *pBuffer)
×
293

294
{
295
    return poVRTBand->ReadBlock(iBlockX, iBlockY, pBuffer);
×
296
}
297

298
/************************************************************************/
299
/*                             IRasterIO()                              */
300
/************************************************************************/
301

302
CPLErr DIMAPRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
12✔
303
                                  int nXSize, int nYSize, void *pData,
304
                                  int nBufXSize, int nBufYSize,
305
                                  GDALDataType eBufType, GSpacing nPixelSpace,
306
                                  GSpacing nLineSpace,
307
                                  GDALRasterIOExtraArg *psExtraArg)
308

309
{
310
    if (GDALPamRasterBand::GetOverviewCount() > 0)
12✔
311
    {
312
        return GDALPamRasterBand::IRasterIO(
×
313
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
314
            eBufType, nPixelSpace, nLineSpace, psExtraArg);
×
315
    }
316

317
    // If not exist DIMAP overviews, try to use band source overviews.
318
    return poVRTBand->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
12✔
319
                                nBufXSize, nBufYSize, eBufType, nPixelSpace,
320
                                nLineSpace, psExtraArg);
12✔
321
}
322

323
/************************************************************************/
324
/*                             IRasterIO()                              */
325
/************************************************************************/
326

327
CPLErr DIMAPDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
2✔
328
                               int nXSize, int nYSize, void *pData,
329
                               int nBufXSize, int nBufYSize,
330
                               GDALDataType eBufType, int nBandCount,
331
                               BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
332
                               GSpacing nLineSpace, GSpacing nBandSpace,
333
                               GDALRasterIOExtraArg *psExtraArg)
334

335
{
336
    if (cpl::down_cast<DIMAPRasterBand *>(papoBands[0])
2✔
337
            ->GDALPamRasterBand::GetOverviewCount() > 0)
2✔
338
    {
339
        return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
×
340
                                         pData, nBufXSize, nBufYSize, eBufType,
341
                                         nBandCount, panBandMap, nPixelSpace,
342
                                         nLineSpace, nBandSpace, psExtraArg);
×
343
    }
344

345
    return poVRTDS->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
2✔
346
                              nBufXSize, nBufYSize, eBufType, nBandCount,
347
                              panBandMap, nPixelSpace, nLineSpace, nBandSpace,
348
                              psExtraArg);
2✔
349
}
350

351
/************************************************************************/
352
/*                          GetOverviewCount()                          */
353
/************************************************************************/
354

355
int DIMAPRasterBand::GetOverviewCount()
3✔
356
{
357
    if (GDALPamRasterBand::GetOverviewCount() > 0)
3✔
358
    {
359
        return GDALPamRasterBand::GetOverviewCount();
×
360
    }
361
    return poVRTBand->GetOverviewCount();
3✔
362
}
363

364
/************************************************************************/
365
/*                             GetOverview()                            */
366
/************************************************************************/
367

368
GDALRasterBand *DIMAPRasterBand::GetOverview(int iOvr)
×
369
{
370
    if (GDALPamRasterBand::GetOverviewCount() > 0)
×
371
    {
372
        return GDALPamRasterBand::GetOverview(iOvr);
×
373
    }
374
    return poVRTBand->GetOverview(iOvr);
×
375
}
376

377
/************************************************************************/
378
/*                         ComputeRasterMinMax()                        */
379
/************************************************************************/
380

381
CPLErr DIMAPRasterBand::ComputeRasterMinMax(int bApproxOK, double adfMinMax[2])
6✔
382
{
383
    if (GDALPamRasterBand::GetOverviewCount() > 0)
6✔
384
    {
385
        return GDALPamRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
×
386
    }
387
    return poVRTBand->ComputeRasterMinMax(bApproxOK, adfMinMax);
6✔
388
}
389

390
/************************************************************************/
391
/*                          ComputeStatistics()                         */
392
/************************************************************************/
393

394
CPLErr DIMAPRasterBand::ComputeStatistics(int bApproxOK, double *pdfMin,
×
395
                                          double *pdfMax, double *pdfMean,
396
                                          double *pdfStdDev,
397
                                          GDALProgressFunc pfnProgress,
398
                                          void *pProgressData)
399
{
400
    if (GDALPamRasterBand::GetOverviewCount() > 0)
×
401
    {
402
        return GDALPamRasterBand::ComputeStatistics(bApproxOK, pdfMin, pdfMax,
×
403
                                                    pdfMean, pdfStdDev,
404
                                                    pfnProgress, pProgressData);
×
405
    }
406
    return poVRTBand->ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean,
×
407
                                        pdfStdDev, pfnProgress, pProgressData);
×
408
}
409

410
/************************************************************************/
411
/*                            GetHistogram()                            */
412
/************************************************************************/
413

414
CPLErr DIMAPRasterBand::GetHistogram(double dfMin, double dfMax, int nBuckets,
×
415
                                     GUIntBig *panHistogram,
416
                                     int bIncludeOutOfRange, int bApproxOK,
417
                                     GDALProgressFunc pfnProgress,
418
                                     void *pProgressData)
419
{
420
    if (GDALPamRasterBand::GetOverviewCount() > 0)
×
421
    {
422
        return GDALPamRasterBand::GetHistogram(
×
423
            dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
424
            pfnProgress, pProgressData);
×
425
    }
426
    return poVRTBand->GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
×
427
                                   bIncludeOutOfRange, bApproxOK, pfnProgress,
428
                                   pProgressData);
×
429
}
430

431
/************************************************************************/
432
/*                              Identify()                              */
433
/************************************************************************/
434

435
int DIMAPDataset::Identify(GDALOpenInfo *poOpenInfo)
60,960✔
436

437
{
438
    if (STARTS_WITH(poOpenInfo->pszFilename, "DIMAP:"))
60,960✔
439
        return true;
4✔
440

441
    if (poOpenInfo->nHeaderBytes >= 100)
60,956✔
442
    {
443
        if ((strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
6,068✔
444
                    "<Dimap_Document") == nullptr) &&
6,058✔
445
            (strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
6,058✔
446
                    "<PHR_DIMAP_Document") == nullptr))
447
            return FALSE;
6,058✔
448

449
        return TRUE;
10✔
450
    }
451
    else if (poOpenInfo->bIsDirectory)
54,888✔
452
    {
453
        // DIMAP file.
454
        CPLString osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
619✔
455
                                                       "METADATA.DIM", nullptr);
619✔
456

457
        VSIStatBufL sStat;
458
        if (VSIStatL(osMDFilename, &sStat) == 0)
619✔
459
        {
460
            // Make sure this is really a Dimap format.
461
            GDALOpenInfo oOpenInfo(osMDFilename, GA_ReadOnly, nullptr);
×
462
            if (oOpenInfo.nHeaderBytes >= 100)
×
463
            {
464
                if (strstr(reinterpret_cast<char *>(oOpenInfo.pabyHeader),
×
465
                           "<Dimap_Document") == nullptr)
466
                    return FALSE;
×
467

468
                return TRUE;
×
469
            }
470
        }
471
        else
472
        {
473
            // DIMAP 2 file.
474
            osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
619✔
475
                                                 "VOL_PHR.XML", nullptr);
619✔
476

477
            if (VSIStatL(osMDFilename, &sStat) == 0)
619✔
478
                return TRUE;
4✔
479

480
            // DIMAP VHR2020 file.
481
            osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
615✔
482
                                                 "VOL_PNEO.XML", nullptr);
615✔
483

484
            if (VSIStatL(osMDFilename, &sStat) == 0)
615✔
485
                return TRUE;
2✔
486

487
            return FALSE;
613✔
488
        }
489
    }
490

491
    return FALSE;
54,269✔
492
}
493

494
/************************************************************************/
495
/*                                Open()                                */
496
/************************************************************************/
497

498
GDALDataset *DIMAPDataset::Open(GDALOpenInfo *poOpenInfo)
10✔
499

500
{
501
    if (!Identify(poOpenInfo))
10✔
502
        return nullptr;
×
503

504
    /* -------------------------------------------------------------------- */
505
    /*      Confirm the requested access is supported.                      */
506
    /* -------------------------------------------------------------------- */
507
    if (poOpenInfo->eAccess == GA_Update)
10✔
508
    {
509
        ReportUpdateNotSupportedByDriver("DIMAP");
×
510
        return nullptr;
×
511
    }
512
    /* -------------------------------------------------------------------- */
513
    /*      Get the metadata filename.                                      */
514
    /* -------------------------------------------------------------------- */
515
    CPLString osFilename;
20✔
516
    CPLString osSelectedSubdataset;
20✔
517

518
    if (STARTS_WITH(poOpenInfo->pszFilename, "DIMAP:"))
10✔
519
    {
520
        CPLStringList aosTokens(CSLTokenizeString2(poOpenInfo->pszFilename, ":",
2✔
521
                                                   CSLT_HONOURSTRINGS));
2✔
522
        if (aosTokens.size() != 3)
2✔
523
            return nullptr;
×
524

525
        osFilename = aosTokens[1];
2✔
526
        osSelectedSubdataset = aosTokens[2];
2✔
527
    }
528
    else
529
    {
530
        osFilename = poOpenInfo->pszFilename;
8✔
531
    }
532

533
    VSIStatBufL sStat;
534
    std::string osMDFilename(osFilename);
20✔
535
    if (VSIStatL(osFilename.c_str(), &sStat) == 0 && VSI_ISDIR(sStat.st_mode))
10✔
536
    {
537
        osMDFilename =
538
            CPLFormCIFilenameSafe(osFilename, "METADATA.DIM", nullptr);
5✔
539

540
        /* DIMAP2 */
541
        if (VSIStatL(osMDFilename.c_str(), &sStat) != 0)
5✔
542
        {
543
            osMDFilename =
544
                CPLFormCIFilenameSafe(osFilename, "VOL_PHR.XML", nullptr);
5✔
545
            if (VSIStatL(osMDFilename.c_str(), &sStat) != 0)
5✔
546
            {
547
                // DIMAP VHR2020 file.
548
                osMDFilename =
549
                    CPLFormCIFilenameSafe(osFilename, "VOL_PNEO.XML", nullptr);
1✔
550
            }
551
        }
552
    }
553

554
    /* -------------------------------------------------------------------- */
555
    /*      Ingest the xml file.                                            */
556
    /* -------------------------------------------------------------------- */
557
    CPLXMLNode *psProduct = CPLParseXMLFile(osMDFilename.c_str());
10✔
558
    if (psProduct == nullptr)
10✔
559
        return nullptr;
×
560

561
    CPLXMLNode *psDoc = CPLGetXMLNode(psProduct, "=Dimap_Document");
10✔
562
    if (!psDoc)
10✔
563
        psDoc = CPLGetXMLNode(psProduct, "=PHR_DIMAP_Document");
×
564

565
    // We check the for the tag Metadata_Identification.METADATA_FORMAT.
566
    // The metadata will be set to 2.0 for DIMAP2.
567
    double dfMetadataFormatVersion = CPLAtof(CPLGetXMLValue(
10✔
568
        CPLGetXMLNode(psDoc, "Metadata_Identification.METADATA_FORMAT"),
10✔
569
        "version", "1"));
570

571
    const int nProductVersion = dfMetadataFormatVersion >= 2.0 ? 2 : 1;
10✔
572

573
    std::string osImageDSFilename;
20✔
574
    std::string osDIMAPFilename;
20✔
575
    std::string osRPCFilename;
20✔
576
    CPLXMLNode *psProductDim = nullptr;
10✔
577
    CPLXMLNode *psProductStrip = nullptr;
10✔
578

579
    CPLStringList aosSubdatasets;
20✔
580

581
    // Check needed information for the DIMAP format.
582
    if (nProductVersion == 1)
10✔
583
    {
584
        CPLXMLNode *psImageAttributes =
585
            CPLGetXMLNode(psDoc, "Raster_Dimensions");
2✔
586
        if (psImageAttributes == nullptr)
2✔
587
        {
588
            CPLError(CE_Failure, CPLE_OpenFailed,
×
589
                     "Failed to find <Raster_Dimensions> in document.");
590
            CPLDestroyXMLNode(psProduct);
×
591
            return nullptr;
×
592
        }
593
    }
594
    else  // DIMAP2.
595
    {
596
        // Verify if the opened file is not already a product dimap
597
        if (CPLGetXMLNode(psDoc, "Raster_Data"))
8✔
598
        {
599
            psProductDim = psProduct;
2✔
600
            osDIMAPFilename = osMDFilename;
2✔
601
        }
602
        else
603
        {
604
            // Verify the presence of the DIMAP product file.
605
            CPLXMLNode *psDatasetComponents =
606
                CPLGetXMLNode(psDoc, "Dataset_Content.Dataset_Components");
6✔
607

608
            if (psDatasetComponents == nullptr)
6✔
609
            {
610
                CPLError(CE_Failure, CPLE_OpenFailed,
×
611
                         "Failed to find <Dataset_Components> in document.");
612
                CPLDestroyXMLNode(psProduct);
×
613
                return nullptr;
×
614
            }
615

616
            for (CPLXMLNode *psDatasetComponent = psDatasetComponents->psChild;
6✔
617
                 psDatasetComponent != nullptr;
17✔
618
                 psDatasetComponent = psDatasetComponent->psNext)
11✔
619
            {
620
                const char *pszComponentType =
621
                    CPLGetXMLValue(psDatasetComponent, "COMPONENT_TYPE", "");
11✔
622
                if (strcmp(pszComponentType, "DIMAP") == 0)
11✔
623
                {
624
                    // DIMAP product found.
625
                    const char *pszHref = CPLGetXMLValue(
10✔
626
                        psDatasetComponent, "COMPONENT_PATH.href", "");
627
                    const CPLString osComponentTitle(CPLGetXMLValue(
628
                        psDatasetComponent, "COMPONENT_TITLE", ""));
20✔
629
                    const CPLString osComponentTitleLaundered(
630
                        CPLString(osComponentTitle).replaceAll(' ', '_'));
20✔
631

632
                    if (strlen(pszHref) > 0 && osDIMAPFilename.empty() &&
20✔
633
                        (osSelectedSubdataset.empty() ||
10✔
634
                         osSelectedSubdataset == osComponentTitleLaundered))
3✔
635
                    {
636
                        if (poOpenInfo->bIsDirectory)
6✔
637
                        {
638
                            osDIMAPFilename = CPLFormCIFilenameSafe(
3✔
639
                                poOpenInfo->pszFilename, pszHref, nullptr);
3✔
640
                        }
641
                        else
642
                        {
643
                            CPLString osPath =
644
                                CPLGetPathSafe(osMDFilename.c_str());
3✔
645
                            osDIMAPFilename =
646
                                CPLFormFilenameSafe(osPath, pszHref, nullptr);
3✔
647
                        }
648

649
                        // Data file might be specified there.
650
                        const char *pszDataFileHref = CPLGetXMLValue(
6✔
651
                            psDatasetComponent,
652
                            "Data_Files.Data_File.DATA_FILE_PATH.href", "");
653

654
                        if (strlen(pszDataFileHref) > 0)
6✔
655
                        {
656
                            CPLString osPath =
657
                                CPLGetPathSafe(osMDFilename.c_str());
×
658
                            osImageDSFilename = CPLFormFilenameSafe(
×
659
                                osPath, pszDataFileHref, nullptr);
×
660
                        }
661
                    }
662

663
                    const int iIdx =
664
                        static_cast<int>(aosSubdatasets.size() / 2 + 1);
10✔
665
                    aosSubdatasets.SetNameValue(
666
                        CPLSPrintf("SUBDATASET_%d_NAME", iIdx),
667
                        CPLSPrintf("DIMAP:\"%s\":%s", poOpenInfo->pszFilename,
668
                                   osComponentTitleLaundered.c_str()));
10✔
669
                    aosSubdatasets.SetNameValue(
670
                        CPLSPrintf("SUBDATASET_%d_DESC", iIdx),
671
                        CPLSPrintf("Component %s", osComponentTitle.c_str()));
10✔
672
                }
673
            }
674

675
            psProductDim = CPLParseXMLFile(osDIMAPFilename.c_str());
6✔
676
            if (psProductDim == nullptr)
6✔
677
            {
678
                CPLDestroyXMLNode(psProduct);
×
679
                return nullptr;
×
680
            }
681
        }
682

683
        // We need the {STRIP|RPC}_<product_id>.XML file for a few metadata.
684
        CPLXMLNode *psDocDim = CPLGetXMLNode(psProductDim, "=Dimap_Document");
8✔
685
        if (!psDocDim)
8✔
686
            psDocDim = CPLGetXMLNode(psProductDim, "=PHR_DIMAP_Document");
×
687

688
        CPLXMLNode *psDatasetSources =
689
            CPLGetXMLNode(psDocDim, "Dataset_Sources");
8✔
690
        if (psDatasetSources != nullptr)
8✔
691
        {
692
            CPLString osSTRIPFilename;
14✔
693

694
            for (CPLXMLNode *psDatasetSource = psDatasetSources->psChild;
7✔
695
                 psDatasetSource != nullptr;
8✔
696
                 psDatasetSource = psDatasetSource->psNext)
1✔
697
            {
698
                const char *pszSourceType =
699
                    CPLGetXMLValue(psDatasetSource, "SOURCE_TYPE", "");
7✔
700
                if (strcmp(pszSourceType, "Strip_Source") == 0)
7✔
701
                {
702
                    const char *pszHref = CPLGetXMLValue(
7✔
703
                        psDatasetSource, "Component.COMPONENT_PATH.href", "");
704

705
                    if (strlen(pszHref) > 0)  // STRIP product found.
7✔
706
                    {
707
                        CPLString osPath =
708
                            CPLGetPathSafe(osDIMAPFilename.c_str());
6✔
709
                        osSTRIPFilename =
710
                            CPLFormCIFilenameSafe(osPath, pszHref, nullptr);
6✔
711
                        if (VSIStatL(osSTRIPFilename, &sStat) == 0)
6✔
712
                        {
713
                            psProductStrip = CPLParseXMLFile(osSTRIPFilename);
6✔
714
                            break;
6✔
715
                        }
716
                    }
717
                }
718
            }
719
        }
720

721
        CPLXMLNode *psDatasetRFMComponents = CPLGetXMLNode(
8✔
722
            psDocDim, "Geoposition.Geoposition_Models.Rational_Function_Model");
723
        if (psDatasetRFMComponents != nullptr)
8✔
724
        {
725
            for (CPLXMLNode *psDatasetRFMComponent =
7✔
726
                     psDatasetRFMComponents->psChild;
727
                 psDatasetRFMComponent != nullptr;
7✔
728
                 psDatasetRFMComponent = psDatasetRFMComponent->psNext)
×
729
            {
730
                const char *pszComponentTitle = CPLGetXMLValue(
7✔
731
                    psDatasetRFMComponent, "COMPONENT_TITLE", "");
732
                if (strcmp(pszComponentTitle, "RPC Model") == 0)
7✔
733
                {
734
                    const char *pszHref = CPLGetXMLValue(
7✔
735
                        psDatasetRFMComponent, "COMPONENT_PATH.href", "");
736

737
                    if (strlen(pszHref) > 0)  // RPC product found.
7✔
738
                    {
739
                        CPLString osPath =
740
                            CPLGetPathSafe(osDIMAPFilename.c_str());
14✔
741
                        osRPCFilename =
742
                            CPLFormCIFilenameSafe(osPath, pszHref, nullptr);
7✔
743

744
                        break;
7✔
745
                    }
746
                }
747
            }
748
        }
749
    }
750

751
    /* -------------------------------------------------------------------- */
752
    /*      Create the dataset.                                             */
753
    /* -------------------------------------------------------------------- */
754
    DIMAPDataset *poDS = new DIMAPDataset();
10✔
755

756
    if (osSelectedSubdataset.empty() && aosSubdatasets.size() > 2)
10✔
757
    {
758
        poDS->GDALDataset::SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
2✔
759
    }
760
    poDS->psProduct = psProduct;
10✔
761
    poDS->psProductDim = psProductDim;
10✔
762
    poDS->psProductStrip = psProductStrip;
10✔
763
    poDS->osRPCFilename = std::move(osRPCFilename);
10✔
764
    poDS->nProductVersion = nProductVersion;
10✔
765
    poDS->osMDFilename = std::move(osMDFilename);
10✔
766
    poDS->osImageDSFilename = std::move(osImageDSFilename);
10✔
767
    poDS->osDIMAPFilename = std::move(osDIMAPFilename);
10✔
768

769
    const int res = (nProductVersion == 2) ? poDS->ReadImageInformation2()
10✔
770
                                           : poDS->ReadImageInformation();
2✔
771

772
    if (res == FALSE)
10✔
773
    {
774
        delete poDS;
×
775
        return nullptr;
×
776
    }
777

778
    return poDS;
10✔
779
}
780

781
/************************************************************************/
782
/*               ReadImageInformation() DIMAP Version 1                 */
783
/************************************************************************/
784

785
int DIMAPDataset::ReadImageInformation()
2✔
786
{
787
    CPLXMLNode *psDoc = CPLGetXMLNode(psProduct, "=Dimap_Document");
2✔
788
    if (!psDoc)
2✔
789
        psDoc = CPLGetXMLNode(psProduct, "=PHR_DIMAP_Document");
×
790

791
    /* -------------------------------------------------------------------- */
792
    /*      Get overall image information.                                  */
793
    /* -------------------------------------------------------------------- */
794

795
    // TODO: DIMAP 1 probably handle mosaics? Like DIMAP 2?
796

797
    /* -------------------------------------------------------------------- */
798
    /*      Get the name of the underlying file.                            */
799
    /* -------------------------------------------------------------------- */
800

801
    const char *pszHref =
802
        CPLGetXMLValue(psDoc, "Data_Access.Data_File.DATA_FILE_PATH.href", "");
2✔
803
    CPLString osPath = CPLGetPathSafe(osMDFilename);
4✔
804
    CPLString osImageFilename = CPLFormFilenameSafe(osPath, pszHref, nullptr);
4✔
805

806
    /* -------------------------------------------------------------------- */
807
    /*      Try and open the file.                                          */
808
    /* -------------------------------------------------------------------- */
809

810
    auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
811
        osImageFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
4✔
812
    if (poImageDS == nullptr)
2✔
813
    {
814
        return FALSE;
×
815
    }
816
    nRasterXSize = poImageDS->GetRasterXSize();
2✔
817
    nRasterYSize = poImageDS->GetRasterYSize();
2✔
818

819
    /* -------------------------------------------------------------------- */
820
    /*      Create and initialize the corresponding VRT dataset used to     */
821
    /*      manage the tiled data access.                                   */
822
    /* -------------------------------------------------------------------- */
823
    poVRTDS = new VRTDataset(nRasterXSize, nRasterYSize);
2✔
824

825
    // Don't try to write a VRT file.
826
    poVRTDS->SetWritable(FALSE);
2✔
827

828
    for (int iBand = 0; iBand < poImageDS->GetRasterCount(); iBand++)
4✔
829
    {
830
        poVRTDS->AddBand(
2✔
831
            poImageDS->GetRasterBand(iBand + 1)->GetRasterDataType(), nullptr);
2✔
832

833
        VRTSourcedRasterBand *poVRTBand =
834
            reinterpret_cast<VRTSourcedRasterBand *>(
835
                poVRTDS->GetRasterBand(iBand + 1));
2✔
836

837
        poVRTBand->AddSimpleSource(osImageFilename, iBand + 1, 0, 0,
2✔
838
                                   nRasterXSize, nRasterYSize, 0, 0,
2✔
839
                                   nRasterXSize, nRasterYSize);
2✔
840
    }
841

842
    /* -------------------------------------------------------------------- */
843
    /*      Create band information objects.                                */
844
    /* -------------------------------------------------------------------- */
845
    for (int iBand = 1; iBand <= poVRTDS->GetRasterCount(); iBand++)
4✔
846
    {
847
        SetBand(iBand, new DIMAPRasterBand(this, iBand,
2✔
848
                                           static_cast<VRTSourcedRasterBand *>(
849
                                               poVRTDS->GetRasterBand(iBand))));
2✔
850
    }
851

852
    /* -------------------------------------------------------------------- */
853
    /*      Try to collect simple insertion point.                          */
854
    /* -------------------------------------------------------------------- */
855
    CPLXMLNode *psGeoLoc =
856
        CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Insert");
2✔
857

858
    if (psGeoLoc != nullptr)
2✔
859
    {
860
        bHaveGeoTransform = TRUE;
×
861
        m_gt[0] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULXMAP", "0"));
×
862
        m_gt[1] = CPLAtof(CPLGetXMLValue(psGeoLoc, "XDIM", "0"));
×
863
        m_gt[2] = 0.0;
×
864
        m_gt[3] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULYMAP", "0"));
×
865
        m_gt[4] = 0.0;
×
866
        m_gt[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc, "YDIM", "0"));
×
867
    }
868
    else
869
    {
870
        // Try to get geotransform from underlying raster.
871
        if (poImageDS->GetGeoTransform(m_gt) == CE_None)
2✔
872
            bHaveGeoTransform = TRUE;
×
873
    }
874

875
    /* -------------------------------------------------------------------- */
876
    /*      Collect GCPs.                                                   */
877
    /* -------------------------------------------------------------------- */
878
    psGeoLoc = CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Points");
2✔
879

880
    if (psGeoLoc != nullptr)
2✔
881
    {
882
        // Count gcps.
883
        nGCPCount = 0;
2✔
884
        for (CPLXMLNode *psNode = psGeoLoc->psChild; psNode != nullptr;
10✔
885
             psNode = psNode->psNext)
8✔
886
        {
887
            if (EQUAL(psNode->pszValue, "Tie_Point"))
8✔
888
                nGCPCount++;
8✔
889
        }
890

891
        pasGCPList =
2✔
892
            static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nGCPCount));
2✔
893

894
        nGCPCount = 0;
2✔
895

896
        for (CPLXMLNode *psNode = psGeoLoc->psChild; psNode != nullptr;
10✔
897
             psNode = psNode->psNext)
8✔
898
        {
899
            GDAL_GCP *psGCP = pasGCPList + nGCPCount;
8✔
900

901
            if (!EQUAL(psNode->pszValue, "Tie_Point"))
8✔
902
                continue;
×
903

904
            nGCPCount++;
8✔
905

906
            char szID[32] = {};
8✔
907
            snprintf(szID, sizeof(szID), "%d", nGCPCount);
8✔
908
            psGCP->pszId = CPLStrdup(szID);
8✔
909
            psGCP->pszInfo = CPLStrdup("");
8✔
910
            psGCP->dfGCPPixel =
8✔
911
                CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_DATA_X", "0")) - 0.5;
8✔
912
            psGCP->dfGCPLine =
8✔
913
                CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_DATA_Y", "0")) - 0.5;
8✔
914
            psGCP->dfGCPX =
8✔
915
                CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_X", ""));
8✔
916
            psGCP->dfGCPY =
8✔
917
                CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_Y", ""));
8✔
918
            psGCP->dfGCPZ =
8✔
919
                CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_Z", ""));
8✔
920
        }
921
    }
922

923
    /* -------------------------------------------------------------------- */
924
    /*      Collect the CRS.  For now we look only for EPSG codes.          */
925
    /* -------------------------------------------------------------------- */
926
    const char *pszSRS = CPLGetXMLValue(
2✔
927
        psDoc, "Coordinate_Reference_System.Horizontal_CS.HORIZONTAL_CS_CODE",
928
        nullptr);
929

930
    if (pszSRS != nullptr)
2✔
931
    {
932
        OGRSpatialReference &oSRS = nGCPCount > 0 ? m_oGCPSRS : m_oSRS;
2✔
933
        oSRS.SetFromUserInput(
2✔
934
            pszSRS, OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
935
    }
936
    else
937
    {
938
        // Check underlying raster for SRS. We have cases where
939
        // HORIZONTAL_CS_CODE is empty and the underlying raster
940
        // is georeferenced (rprinceley).
941
        const auto poSRS = poImageDS->GetSpatialRef();
×
942
        if (poSRS)
×
943
        {
944
            m_oSRS = *poSRS;
×
945
        }
946
    }
947

948
    /* -------------------------------------------------------------------- */
949
    /*      Translate other metadata of interest.                           */
950
    /* -------------------------------------------------------------------- */
951
    static const char *const apszMetadataTranslation[] = {
952
        "Production",
953
        "",
954
        "Production.Facility",
955
        "FACILITY_",
956
        "Dataset_Sources.Source_Information.Scene_Source",
957
        "",
958
        "Data_Processing",
959
        "",
960
        "Image_Interpretation.Spectral_Band_Info",
961
        "SPECTRAL_",
962
        nullptr,
963
        nullptr};
964

965
    SetMetadataFromXML(psProduct, apszMetadataTranslation);
2✔
966

967
    /* -------------------------------------------------------------------- */
968
    /*      Set Band metadata from the <Spectral_Band_Info> content         */
969
    /* -------------------------------------------------------------------- */
970

971
    CPLXMLNode *psImageInterpretationNode =
972
        CPLGetXMLNode(psDoc, "Image_Interpretation");
2✔
973
    if (psImageInterpretationNode != nullptr)
2✔
974
    {
975
        CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
2✔
976
        while (psSpectralBandInfoNode != nullptr)
4✔
977
        {
978
            if (psSpectralBandInfoNode->eType == CXT_Element &&
2✔
979
                EQUAL(psSpectralBandInfoNode->pszValue, "Spectral_Band_Info"))
2✔
980
            {
981
                CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
2✔
982
                int nBandIndex = 0;
2✔
983
                while (psTag != nullptr)
14✔
984
                {
985
                    if (psTag->eType == CXT_Element &&
12✔
986
                        psTag->psChild != nullptr &&
12✔
987
                        psTag->psChild->eType == CXT_Text &&
12✔
988
                        psTag->pszValue != nullptr)
12✔
989
                    {
990
                        if (EQUAL(psTag->pszValue, "BAND_INDEX"))
12✔
991
                        {
992
                            nBandIndex = atoi(psTag->psChild->pszValue);
2✔
993
                            if (nBandIndex <= 0 ||
4✔
994
                                nBandIndex > poImageDS->GetRasterCount())
2✔
995
                            {
996
                                CPLError(CE_Warning, CPLE_AppDefined,
×
997
                                         "Bad BAND_INDEX value : %s",
998
                                         psTag->psChild->pszValue);
×
999
                                nBandIndex = 0;
×
1000
                            }
1001
                        }
1002
                        else if (nBandIndex >= 1)
10✔
1003
                        {
1004
                            GetRasterBand(nBandIndex)
10✔
1005
                                ->SetMetadataItem(psTag->pszValue,
10✔
1006
                                                  psTag->psChild->pszValue);
10✔
1007
                        }
1008
                    }
1009
                    psTag = psTag->psNext;
12✔
1010
                }
1011
            }
1012
            psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
2✔
1013
        }
1014
    }
1015

1016
    /* -------------------------------------------------------------------- */
1017
    /*      Initialize any PAM information.                                 */
1018
    /* -------------------------------------------------------------------- */
1019
    SetDescription(osMDFilename);
2✔
1020
    TryLoadXML();
2✔
1021

1022
    /* -------------------------------------------------------------------- */
1023
    /*      Check for overviews.                                            */
1024
    /* -------------------------------------------------------------------- */
1025
    oOvManager.Initialize(this, osMDFilename);
2✔
1026

1027
    // CID 163546 - poTileDS dereferenced above.
1028
    // coverity[leaked_storage]
1029
    return TRUE;
2✔
1030
}
1031

1032
/************************************************************************/
1033
/*               ReadImageInformation() DIMAP Version 2                 */
1034
/************************************************************************/
1035

1036
int DIMAPDataset::ReadImageInformation2()
8✔
1037
{
1038
    CPLXMLNode *psDoc = CPLGetXMLNode(psProductDim, "=Dimap_Document");
8✔
1039
    if (!psDoc)
8✔
1040
        psDoc = CPLGetXMLNode(psProductDim, "=PHR_DIMAP_Document");
×
1041

1042
    CPLXMLNode *psImageAttributes =
1043
        CPLGetXMLNode(psDoc, "Raster_Data.Raster_Dimensions");
8✔
1044
    if (psImageAttributes == nullptr)
8✔
1045
    {
1046
        CPLError(CE_Failure, CPLE_OpenFailed,
×
1047
                 "Failed to find <Raster_Dimensions> in document.");
1048
        return FALSE;
×
1049
    }
1050

1051
    /* -------------------------------------------------------------------- */
1052
    /*      Get overall image information.                                  */
1053
    /* -------------------------------------------------------------------- */
1054

1055
    /*
1056
        <Raster_Dimensions>
1057
           <NROWS>30</NROWS>
1058
           <NCOLS>20</NCOLS>
1059
           <NBANDS>4</NBANDS>
1060
           <Tile_Set>
1061
              <NTILES>2</NTILES>
1062
              <Regular_Tiling>
1063
                 <NTILES_SIZE nrows="20" ncols="20"/>
1064
                 <NTILES_COUNT ntiles_R="2" ntiles_C="1"/>
1065
                 <OVERLAP_ROW>0</OVERLAP_ROW>
1066
                 <OVERLAP_COL>0</OVERLAP_COL>
1067
              </Regular_Tiling>
1068
           </Tile_Set>
1069
        </Raster_Dimensions>
1070
      */
1071

1072
    const int l_nBands =
1073
        atoi(CPLGetXMLValue(psImageAttributes, "NBANDS", "-1"));
8✔
1074
    nRasterXSize = atoi(CPLGetXMLValue(psImageAttributes, "NCOLS", "-1"));
8✔
1075
    nRasterYSize = atoi(CPLGetXMLValue(psImageAttributes, "NROWS", "-1"));
8✔
1076
    if (nRasterXSize <= 0 || nRasterYSize <= 0)
8✔
1077
    {
1078
        CPLError(CE_Failure, CPLE_AppDefined,
×
1079
                 "Invalid NCOLS(=%d)/NROWS(=%d) value", nRasterXSize,
1080
                 nRasterYSize);
1081
        return FALSE;
×
1082
    }
1083
    int nTileWidth = atoi(CPLGetXMLValue(
8✔
1084
        psImageAttributes, "Tile_Set.Regular_Tiling.NTILES_SIZE.ncols", "-1"));
1085
    int nTileHeight = atoi(CPLGetXMLValue(
8✔
1086
        psImageAttributes, "Tile_Set.Regular_Tiling.NTILES_SIZE.nrows", "-1"));
1087
    int nOverlapRow = atoi(CPLGetXMLValue(
8✔
1088
        psImageAttributes, "Tile_Set.Regular_Tiling.OVERLAP_ROW", "-1"));
1089
    int nOverlapCol = atoi(CPLGetXMLValue(
8✔
1090
        psImageAttributes, "Tile_Set.Regular_Tiling.OVERLAP_COL", "-1"));
1091
    const int nBits =
1092
        atoi(CPLGetXMLValue(psDoc, "Raster_Data.Raster_Encoding.NBITS", "-1"));
8✔
1093
    CPLString osDataFormat =
1094
        CPLGetXMLValue(psDoc, "Raster_Data.Data_Access.DATA_FILE_FORMAT", "");
16✔
1095
    if (osDataFormat == "image/jp2")
8✔
1096
    {
1097
        SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE");
1✔
1098
    }
1099

1100
    // For VHR2020: SPECTRAL_PROCESSING = PAN, MS, MS-FS, PMS, PMS-N, PMS-X,
1101
    // PMS-FS
1102
    const CPLString osSpectralProcessing = CPLGetXMLValue(
1103
        psDoc, "Processing_Information.Product_Settings.SPECTRAL_PROCESSING",
1104
        "");
16✔
1105
    const bool bTwoDataFilesPerTile =
1106
        osSpectralProcessing == "MS-FS" || osSpectralProcessing == "PMS-FS";
8✔
1107

1108
    /* -------------------------------------------------------------------- */
1109
    /*      Get the name of the underlying file.                            */
1110
    /* -------------------------------------------------------------------- */
1111

1112
    CPLXMLNode *psDataFiles =
1113
        CPLGetXMLNode(psDoc, "Raster_Data.Data_Access.Data_Files");
8✔
1114

1115
    /*  <Data_Files>
1116
            <Data_File tile_R="1" tile_C="1">
1117
               <DATA_FILE_PATH href="IMG_foo_R1C1.TIF"/>
1118
            </Data_File>
1119
            <Data_File tile_R="2" tile_C="1">
1120
               <DATA_FILE_PATH href="IMG_foo_R2C1.TIF"/>
1121
            </Data_File>
1122
         </Data_Files>
1123
    */
1124

1125
    struct TileIdx
1126
    {
1127
        int nRow;
1128
        int nCol;
1129
        int nPart;  // typically 0.  But for VHR2020 0=RGB, 1=NED
1130

1131
        TileIdx(int nRowIn, int nColIn, int nPartIn = 0)
15✔
1132
            : nRow(nRowIn), nCol(nColIn), nPart(nPartIn)
15✔
1133
        {
1134
        }
15✔
1135

1136
        bool operator<(const TileIdx &other) const
21✔
1137
        {
1138
            if (nRow < other.nRow)
21✔
1139
                return true;
12✔
1140
            if (nRow > other.nRow)
9✔
1141
                return false;
6✔
1142
            if (nCol < other.nCol)
3✔
1143
                return true;
×
1144
            if (nCol > other.nCol)
3✔
1145
                return false;
×
1146
            return nPart < other.nPart;
3✔
1147
        }
1148
    };
1149

1150
    std::map<TileIdx, CPLString> oMapTileIdxToName;
16✔
1151
    int nImageDSRow = 1, nImageDSCol = 1;
8✔
1152
    if (psDataFiles)
8✔
1153
    {
1154
        const CPLString osPath = CPLGetPathSafe(osDIMAPFilename);
8✔
1155
        for (int nPart = 0; psDataFiles != nullptr;
17✔
1156
             psDataFiles = psDataFiles->psNext, nPart++)
9✔
1157
        {
1158
            for (CPLXMLNode *psDataFile = psDataFiles->psChild; psDataFile;
27✔
1159
                 psDataFile = psDataFile->psNext)
18✔
1160
            {
1161
                if (psDataFile->eType == CXT_Element &&
18✔
1162
                    strcmp(psDataFile->pszValue, "Data_File") == 0)
18✔
1163
                {
1164
                    const char *pszR =
1165
                        CPLGetXMLValue(psDataFile, "tile_R", nullptr);
15✔
1166
                    const char *pszC =
1167
                        CPLGetXMLValue(psDataFile, "tile_C", nullptr);
15✔
1168
                    const char *pszHref = CPLGetXMLValue(
15✔
1169
                        psDataFile, "DATA_FILE_PATH.href", nullptr);
1170
                    if (pszR && pszC && pszHref)
15✔
1171
                    {
1172
                        int nRow = atoi(pszR);
15✔
1173
                        int nCol = atoi(pszC);
15✔
1174
                        if (nRow < 0 || nCol < 0)
15✔
1175
                        {
1176
                            return false;
×
1177
                        }
1178
                        std::string osTileFilename(
1179
                            CPLFormCIFilenameSafe(osPath, pszHref, nullptr));
15✔
1180
                        if ((nRow == 1 && nCol == 1 && nPart == 0) ||
22✔
1181
                            osImageDSFilename.empty())
7✔
1182
                        {
1183
                            osImageDSFilename = osTileFilename;
8✔
1184
                            nImageDSRow = nRow;
8✔
1185
                            nImageDSCol = nCol;
8✔
1186
                        }
1187
                        oMapTileIdxToName[TileIdx(nRow, nCol, nPart)] =
15✔
1188
                            std::move(osTileFilename);
30✔
1189
                    }
1190
                }
1191
            }
1192
        }
1193
        if (nOverlapRow > 0 || nOverlapCol > 0)
8✔
1194
        {
1195
            CPLError(CE_Warning, CPLE_AppDefined,
×
1196
                     "Overlap between tiles is not handled currently. "
1197
                     "Only taking into account top left tile");
1198
            oMapTileIdxToName.clear();
×
1199
            oMapTileIdxToName[TileIdx(1, 1)] = osImageDSFilename;
×
1200
        }
1201
    }
1202
    else
1203
    {
1204
        oMapTileIdxToName[TileIdx(1, 1)] = osImageDSFilename;
×
1205
    }
1206

1207
    if (osImageDSFilename.empty())
8✔
1208
    {
1209
        CPLError(CE_Failure, CPLE_OpenFailed,
×
1210
                 "Failed to find <DATA_FILE_PATH> in document.");
1211
        return FALSE;
×
1212
    }
1213

1214
    /* -------------------------------------------------------------------- */
1215
    /*      Try and open the file.                                          */
1216
    /* -------------------------------------------------------------------- */
1217
    auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1218
        osImageDSFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
16✔
1219
    if (poImageDS == nullptr)
8✔
1220
    {
1221
        return FALSE;
×
1222
    }
1223
    if (bTwoDataFilesPerTile)
8✔
1224
    {
1225
        if (l_nBands != 6 || poImageDS->GetRasterCount() != 3)
1✔
1226
        {
1227
            CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent band count");
×
1228
            return FALSE;
×
1229
        }
1230
    }
1231
    else if (poImageDS->GetRasterCount() != l_nBands)
7✔
1232
    {
1233
        CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent band count");
×
1234
        return FALSE;
×
1235
    }
1236

1237
    if (nTileWidth > 0 && nTileHeight > 0)
8✔
1238
    {
1239
        // ok
1240
    }
1241
    else if (oMapTileIdxToName.size() == 1 ||
3✔
1242
             (bTwoDataFilesPerTile && oMapTileIdxToName.size() == 2))
1✔
1243
    {
1244
        nTileWidth = poImageDS->GetRasterXSize();
2✔
1245
        nTileHeight = poImageDS->GetRasterYSize();
2✔
1246
    }
1247

1248
    if (!(nTileWidth > 0 && nTileHeight > 0))
8✔
1249
    {
1250
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot get tile dimension");
×
1251
        return FALSE;
×
1252
    }
1253

1254
    /* -------------------------------------------------------------------- */
1255
    /*      Create and initialize the corresponding VRT dataset used to     */
1256
    /*      manage the tiled data access.                                   */
1257
    /* -------------------------------------------------------------------- */
1258
    poVRTDS = new VRTDataset(nRasterXSize, nRasterYSize);
8✔
1259

1260
    // Don't try to write a VRT file.
1261
    poVRTDS->SetWritable(FALSE);
8✔
1262

1263
    for (int iBand = 0; iBand < l_nBands; iBand++)
36✔
1264
    {
1265
        auto poSrcBandFirstImage = poImageDS->GetRasterBand(
56✔
1266
            iBand < poImageDS->GetRasterCount() ? iBand + 1 : 1);
28✔
1267
        CPLStringList aosAddBandOptions;
56✔
1268
        int nSrcBlockXSize, nSrcBlockYSize;
1269
        poSrcBandFirstImage->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
28✔
1270
        if (oMapTileIdxToName.size() == 1 ||
55✔
1271
            ((nTileWidth % nSrcBlockXSize) == 0 &&
27✔
1272
             (nTileHeight % nSrcBlockYSize) == 0))
15✔
1273
        {
1274
            aosAddBandOptions.SetNameValue("BLOCKXSIZE",
1275
                                           CPLSPrintf("%d", nSrcBlockXSize));
16✔
1276
            aosAddBandOptions.SetNameValue("BLOCKYSIZE",
1277
                                           CPLSPrintf("%d", nSrcBlockYSize));
16✔
1278
        }
1279
        poVRTDS->AddBand(poSrcBandFirstImage->GetRasterDataType(),
28✔
1280
                         aosAddBandOptions.List());
28✔
1281

1282
        VRTSourcedRasterBand *poVRTBand =
1283
            reinterpret_cast<VRTSourcedRasterBand *>(
1284
                poVRTDS->GetRasterBand(iBand + 1));
28✔
1285
        if (nBits > 0 && nBits != 8 && nBits != 16)
28✔
1286
        {
1287
            poVRTBand->SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
21✔
1288
                                       "IMAGE_STRUCTURE");
21✔
1289
        }
1290

1291
        for (const auto &oTileIdxNameTuple : oMapTileIdxToName)
83✔
1292
        {
1293
            const int nRow = oTileIdxNameTuple.first.nRow;
55✔
1294
            const int nCol = oTileIdxNameTuple.first.nCol;
55✔
1295
            if (static_cast<int64_t>(nRow - 1) * nTileHeight < nRasterYSize &&
55✔
1296
                static_cast<int64_t>(nCol - 1) * nTileWidth < nRasterXSize)
55✔
1297
            {
1298
                int nSrcBand;
1299
                if (bTwoDataFilesPerTile)
55✔
1300
                {
1301
                    const int nPart = oTileIdxNameTuple.first.nPart;
12✔
1302
                    if (nPart == 0 && iBand < 3)
12✔
1303
                    {
1304
                        nSrcBand = iBand + 1;
3✔
1305
                    }
1306
                    else if (nPart == 1 && iBand >= 3)
9✔
1307
                    {
1308
                        nSrcBand = iBand + 1 - 3;
3✔
1309
                    }
1310
                    else
1311
                    {
1312
                        continue;
6✔
1313
                    }
1314
                }
1315
                else
1316
                {
1317
                    nSrcBand = iBand + 1;
43✔
1318
                }
1319

1320
                int nHeight = nTileHeight;
49✔
1321
                if (static_cast<int64_t>(nRow) * nTileHeight > nRasterYSize)
49✔
1322
                {
1323
                    nHeight = nRasterYSize - (nRow - 1) * nTileHeight;
21✔
1324
                }
1325
                int nWidth = nTileWidth;
49✔
1326
                if (static_cast<int64_t>(nCol) * nTileWidth > nRasterXSize)
49✔
1327
                {
1328
                    nWidth = nRasterXSize - (nCol - 1) * nTileWidth;
×
1329
                }
1330

1331
                poVRTBand->AddSimpleSource(
49✔
1332
                    oTileIdxNameTuple.second, nSrcBand, 0, 0, nWidth, nHeight,
1333
                    (nCol - 1) * nTileWidth, (nRow - 1) * nTileHeight, nWidth,
49✔
1334
                    nHeight);
1335
            }
1336
        }
1337
    }
1338

1339
    /* -------------------------------------------------------------------- */
1340
    /*      Expose Overviews if available                                   */
1341
    /* -------------------------------------------------------------------- */
1342
    auto poSrcBandFirstImage = poImageDS->GetRasterBand(1);
8✔
1343
    const int nSrcOverviews =
1344
        std::min(30, poSrcBandFirstImage->GetOverviewCount());
8✔
1345
    if (nSrcOverviews > 0)
8✔
1346
    {
1347
        CPLConfigOptionSetter oSetter("VRT_VIRTUAL_OVERVIEWS", "YES", false);
6✔
1348
        std::unique_ptr<int[]> ovrLevels(new int[nSrcOverviews]);
6✔
1349
        int iLvl = 1;
3✔
1350
        for (int i = 0; i < nSrcOverviews; i++)
6✔
1351
        {
1352
            iLvl *= 2;
3✔
1353
            ovrLevels[i] = iLvl;
3✔
1354
        }
1355
        poVRTDS->IBuildOverviews("average", nSrcOverviews, ovrLevels.get(), 0,
3✔
1356
                                 nullptr, nullptr, nullptr, nullptr);
3✔
1357
    }
1358

1359
#ifdef DEBUG_VERBOSE
1360
    CPLDebug("DIMAP", "VRT XML: %s", poVRTDS->GetMetadata("xml:VRT")[0]);
1361
#endif
1362

1363
    /* -------------------------------------------------------------------- */
1364
    /*      Create band information objects.                                */
1365
    /* -------------------------------------------------------------------- */
1366
    for (int iBand = 1; iBand <= poVRTDS->GetRasterCount(); iBand++)
36✔
1367
    {
1368
        GDALRasterBand *poBand = new DIMAPRasterBand(
1369
            this, iBand,
1370
            static_cast<VRTSourcedRasterBand *>(poVRTDS->GetRasterBand(iBand)));
28✔
1371
        if (nBits > 0 && nBits != 8 && nBits != 16)
28✔
1372
        {
1373
            poBand->SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
21✔
1374
                                    "IMAGE_STRUCTURE");
21✔
1375
        }
1376
        if (bTwoDataFilesPerTile)
28✔
1377
        {
1378
            switch (iBand)
6✔
1379
            {
1380
                case 1:
1✔
1381
                {
1382
                    poBand->SetColorInterpretation(GCI_RedBand);
1✔
1383
                    poBand->SetDescription("Red");
1✔
1384
                    break;
1✔
1385
                }
1386
                case 2:
1✔
1387
                {
1388
                    poBand->SetColorInterpretation(GCI_GreenBand);
1✔
1389
                    poBand->SetDescription("Green");
1✔
1390
                    break;
1✔
1391
                }
1392
                case 3:
1✔
1393
                {
1394
                    poBand->SetColorInterpretation(GCI_BlueBand);
1✔
1395
                    poBand->SetDescription("Blue");
1✔
1396
                    break;
1✔
1397
                }
1398
                case 4:
1✔
1399
                {
1400
                    poBand->SetColorInterpretation(GCI_NIRBand);
1✔
1401
                    poBand->SetDescription("NIR");
1✔
1402
                    break;
1✔
1403
                }
1404
                case 5:
1✔
1405
                {
1406
                    poBand->SetColorInterpretation(GCI_RedEdgeBand);
1✔
1407
                    poBand->SetDescription("Red Edge");
1✔
1408
                    break;
1✔
1409
                }
1410
                case 6:
1✔
1411
                {
1412
                    poBand->SetColorInterpretation(GCI_CoastalBand);
1✔
1413
                    poBand->SetDescription("Deep Blue");
1✔
1414
                    break;
1✔
1415
                }
1416
                default:
×
1417
                    break;
×
1418
            }
1419
        }
1420
        else if (l_nBands == 1 && osSpectralProcessing == "PAN")
22✔
1421
        {
1422
            poBand->SetColorInterpretation(GCI_PanBand);
×
1423
            poBand->SetDescription("Panchromatic");
×
1424
        }
1425
        SetBand(iBand, poBand);
28✔
1426
    }
1427

1428
    /* -------------------------------------------------------------------- */
1429
    /*      Try to collect simple insertion point.                          */
1430
    /* -------------------------------------------------------------------- */
1431
    CPLXMLNode *psGeoLoc =
1432
        CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Insert");
8✔
1433

1434
    if (psGeoLoc != nullptr)
8✔
1435
    {
1436
        bHaveGeoTransform = TRUE;
1✔
1437
        m_gt[0] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULXMAP", "0"));
1✔
1438
        m_gt[1] = CPLAtof(CPLGetXMLValue(psGeoLoc, "XDIM", "0"));
1✔
1439
        m_gt[2] = 0.0;
1✔
1440
        m_gt[3] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULYMAP", "0"));
1✔
1441
        m_gt[4] = 0.0;
1✔
1442
        m_gt[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc, "YDIM", "0"));
1✔
1443
    }
1444
    else
1445
    {
1446
        // Try to get geotransform from underlying raster,
1447
        // but make sure it is a real geotransform.
1448
        if (poImageDS->GetGeoTransform(m_gt) == CE_None &&
13✔
1449
            !(m_gt[0] <= 1.5 && fabs(m_gt[3]) <= 1.5))
6✔
1450
        {
1451
            bHaveGeoTransform = TRUE;
6✔
1452
            // fix up the origin if we did not get the geotransform from the
1453
            // top-left tile
1454
            m_gt[0] -= (nImageDSCol - 1) * m_gt[1] * nTileWidth +
6✔
1455
                       (nImageDSRow - 1) * m_gt[2] * nTileHeight;
6✔
1456
            m_gt[3] -= (nImageDSCol - 1) * m_gt[4] * nTileWidth +
12✔
1457
                       (nImageDSRow - 1) * m_gt[5] * nTileHeight;
6✔
1458
        }
1459
    }
1460

1461
    /* -------------------------------------------------------------------- */
1462
    /*      Collect the CRS.  For now we look only for EPSG codes.          */
1463
    /* -------------------------------------------------------------------- */
1464
    const char *pszSRS = CPLGetXMLValue(
8✔
1465
        psDoc, "Coordinate_Reference_System.Projected_CRS.PROJECTED_CRS_CODE",
1466
        nullptr);
1467
    if (pszSRS == nullptr)
8✔
1468
        pszSRS = CPLGetXMLValue(
8✔
1469
            psDoc, "Coordinate_Reference_System.Geodetic_CRS.GEODETIC_CRS_CODE",
1470
            nullptr);
1471

1472
    if (pszSRS != nullptr)
8✔
1473
    {
1474
        if (bHaveGeoTransform)
8✔
1475
        {
1476
            OGRSpatialReference &oSRS = m_oSRS;
7✔
1477
            oSRS.SetFromUserInput(
7✔
1478
                pszSRS,
1479
                OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1480
        }
1481
    }
1482
    else
1483
    {
1484
        // Check underlying raster for SRS. We have cases where
1485
        // HORIZONTAL_CS_CODE is empty and the underlying raster
1486
        // is georeferenced (rprinceley).
1487
        const auto poSRS = poImageDS->GetSpatialRef();
×
1488
        GDALGeoTransform tmpGT;
×
1489
        if (poSRS && poImageDS->GetGeoTransform(tmpGT) == CE_None)
×
1490
        {
1491
            m_oSRS = *poSRS;
×
1492
        }
1493
    }
1494

1495
    /* -------------------------------------------------------------------- */
1496
    /*      Translate other metadata of interest: DIM_<product_name>.XML    */
1497
    /* -------------------------------------------------------------------- */
1498

1499
    static const char *const apszMetadataTranslationDim[] = {
1500
        "Product_Information.Delivery_Identification",
1501
        "DATASET_",
1502
        "Product_Information.Producer_Information",
1503
        "DATASET_",
1504
        "Dataset_Sources.Source_Identification.Strip_Source",
1505
        "",
1506
        "Processing_Information.Production_Facility",
1507
        "FACILITY_",
1508
        "Processing_Information.Product_Settings",
1509
        "",
1510
        "Processing_Information.Product_Settings.Geometric_Settings",
1511
        "GEOMETRIC_",
1512
        "Processing_Information.Product_Settings.Radiometric_Settings",
1513
        "RADIOMETRIC_",
1514
        "Quality_Assessment.Imaging_Quality_Measurement",
1515
        "CLOUDCOVER_",
1516
        nullptr,
1517
        nullptr};
1518

1519
    SetMetadataFromXML(psProductDim, apszMetadataTranslationDim);
8✔
1520

1521
    /* -------------------------------------------------------------------- */
1522
    /*      Translate other metadata of interest: STRIP_<product_name>.XML    */
1523
    /* -------------------------------------------------------------------- */
1524

1525
    static const char *const apszMetadataTranslationStrip[] = {
1526
        "Catalog.Full_Strip.Notations.Cloud_And_Quality_Notation."
1527
        "Data_Strip_Notation",
1528
        "CLOUDCOVER_",
1529
        "Acquisition_Configuration.Platform_Configuration."
1530
        "Ephemeris_Configuration",
1531
        "EPHEMERIS_",
1532
        nullptr,
1533
        nullptr};
1534

1535
    if (psProductStrip != nullptr)
8✔
1536
        SetMetadataFromXML(psProductStrip, apszMetadataTranslationStrip);
6✔
1537

1538
    if (!osRPCFilename.empty())
8✔
1539
    {
1540
        GDALMDReaderPleiades *poReader =
1541
            GDALMDReaderPleiades::CreateReaderForRPC(osRPCFilename);
7✔
1542
        char **papszRPC = poReader->LoadRPCXmlFile(psDoc);
7✔
1543
        delete poReader;
7✔
1544
        if (papszRPC)
7✔
1545
            SetMetadata(papszRPC, "RPC");
7✔
1546
        CSLDestroy(papszRPC);
7✔
1547
    }
1548

1549
    CPLXMLNode *psLocatedUseAreaNode =
1550
        CPLGetXMLNode(psDoc, "Geometric_Data.Use_Area");
8✔
1551
    if (psLocatedUseAreaNode != nullptr)
8✔
1552
    {
1553
        CPLXMLNode *psLocatedGeometricValuesNode =
4✔
1554
            psLocatedUseAreaNode->psChild;
1555
        while (psLocatedGeometricValuesNode != nullptr)
11✔
1556
        {
1557
            CPLXMLNode *psLocationType =
1558
                CPLGetXMLNode(psLocatedGeometricValuesNode, "LOCATION_TYPE");
11✔
1559
            if (psLocationType == nullptr ||
11✔
1560
                psLocationType->psChild == nullptr ||
11✔
1561
                !EQUAL(psLocationType->psChild->pszValue, "center"))
11✔
1562
            {
1563
                psLocatedGeometricValuesNode =
7✔
1564
                    psLocatedGeometricValuesNode->psNext;
1565
                continue;
7✔
1566
            }
1567
            static const char *const apszLGVTranslationDim[] = {
1568
                "SATELLITE_ALTITUDE",
1569
                "",
1570
                "Acquisition_Angles",
1571
                "",
1572
                "Solar_Incidences",
1573
                "",
1574
                "Ground_Sample_Distance",
1575
                "",
1576
                nullptr,
1577
                nullptr};
1578

1579
            SetMetadataFromXML(psLocatedGeometricValuesNode,
4✔
1580
                               apszLGVTranslationDim, false);
1581
            break;
4✔
1582
        }
1583
    }
1584

1585
    /* -------------------------------------------------------------------- */
1586
    /*      Set Band metadata from the <Band_Radiance> and                  */
1587
    /*                                <Band_Spectral_Range> content         */
1588
    /* -------------------------------------------------------------------- */
1589

1590
    CPLXMLNode *psImageInterpretationNode = CPLGetXMLNode(
8✔
1591
        psDoc,
1592
        "Radiometric_Data.Radiometric_Calibration.Instrument_Calibration."
1593
        "Band_Measurement_List");
1594
    if (psImageInterpretationNode != nullptr)
8✔
1595
    {
1596
        CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
7✔
1597
        while (psSpectralBandInfoNode != nullptr)
100✔
1598
        {
1599
            if (psSpectralBandInfoNode->eType == CXT_Element &&
93✔
1600
                (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance") ||
93✔
1601
                 EQUAL(psSpectralBandInfoNode->pszValue,
66✔
1602
                       "Band_Spectral_Range") ||
39✔
1603
                 EQUAL(psSpectralBandInfoNode->pszValue,
39✔
1604
                       "Band_Solar_Irradiance")))
1605
            {
1606
                CPLString osName;
162✔
1607

1608
                if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance"))
81✔
1609
                    osName = "RADIANCE_";
27✔
1610
                else if (EQUAL(psSpectralBandInfoNode->pszValue,
54✔
1611
                               "Band_Spectral_Range"))
1612
                    osName = "SPECTRAL_RANGE_";
27✔
1613
                else if (EQUAL(psSpectralBandInfoNode->pszValue,
27✔
1614
                               "Band_Solar_Irradiance"))
1615
                    osName = "SOLAR_IRRADIANCE_";
27✔
1616

1617
                CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
81✔
1618
                int nBandIndex = 0;
81✔
1619
                while (psTag != nullptr)
651✔
1620
                {
1621
                    if (psTag->eType == CXT_Element &&
570✔
1622
                        psTag->psChild != nullptr &&
570✔
1623
                        psTag->pszValue != nullptr &&
558✔
1624
                        (psTag->psChild->eType == CXT_Text ||
558✔
1625
                         EQUAL(psTag->pszValue, "FWHM")))
6✔
1626
                    {
1627
                        if (EQUAL(psTag->pszValue, "BAND_ID"))
558✔
1628
                        {
1629
                            nBandIndex = 0;
81✔
1630
                            if (EQUAL(psTag->psChild->pszValue, "P") ||
81✔
1631
                                EQUAL(psTag->psChild->pszValue, "PAN") ||
81✔
1632
                                EQUAL(psTag->psChild->pszValue, "B0") ||
81✔
1633
                                EQUAL(psTag->psChild->pszValue, "R"))
63✔
1634
                                nBandIndex = 1;
21✔
1635
                            else if (EQUAL(psTag->psChild->pszValue, "B1") ||
60✔
1636
                                     EQUAL(psTag->psChild->pszValue, "G"))
45✔
1637
                                nBandIndex = 2;
18✔
1638
                            else if (EQUAL(psTag->psChild->pszValue, "B2") ||
42✔
1639
                                     EQUAL(psTag->psChild->pszValue, "B"))
27✔
1640
                                nBandIndex = 3;
18✔
1641
                            else if (EQUAL(psTag->psChild->pszValue, "B3") ||
24✔
1642
                                     EQUAL(psTag->psChild->pszValue, "NIR"))
9✔
1643
                                nBandIndex = 4;
18✔
1644
                            else if (EQUAL(psTag->psChild->pszValue, "RE"))
6✔
1645
                                nBandIndex = 5;
3✔
1646
                            else if (EQUAL(psTag->psChild->pszValue, "DB"))
3✔
1647
                                nBandIndex = 6;
3✔
1648

1649
                            if (nBandIndex <= 0 ||
162✔
1650
                                nBandIndex > GetRasterCount())
81✔
1651
                            {
1652
                                CPLError(CE_Warning, CPLE_AppDefined,
×
1653
                                         "Bad BAND_ID value : %s",
1654
                                         psTag->psChild->pszValue);
×
1655
                                nBandIndex = 0;
×
1656
                            }
1657
                        }
1658
                        else if (nBandIndex >= 1)
477✔
1659
                        {
1660
                            CPLString osMDName = osName;
954✔
1661
                            osMDName += psTag->pszValue;
477✔
1662

1663
                            auto poBand = GetRasterBand(nBandIndex);
477✔
1664
                            if (EQUAL(psTag->pszValue, "FWHM"))
477✔
1665
                            {
1666
                                if (const char *pszMIN =
6✔
1667
                                        CPLGetXMLValue(psTag, "MIN", nullptr))
6✔
1668
                                    poBand->SetMetadataItem(
6✔
1669
                                        (osMDName + "_MIN").c_str(), pszMIN);
12✔
1670
                                if (const char *pszMAX =
6✔
1671
                                        CPLGetXMLValue(psTag, "MAX", nullptr))
6✔
1672
                                    poBand->SetMetadataItem(
6✔
1673
                                        (osMDName + "_MAX").c_str(), pszMAX);
12✔
1674
                            }
1675
                            else
1676
                            {
1677
                                poBand->SetMetadataItem(
471✔
1678
                                    osMDName, psTag->psChild->pszValue);
471✔
1679
                            }
1680
                        }
1681
                    }
1682
                    psTag = psTag->psNext;
570✔
1683
                }
1684
            }
1685
            psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
93✔
1686
        }
1687
    }
1688

1689
    // Fill raster band IMAGERY metadata domain from FWHM metadata.
1690
    for (int i = 1; i <= nBands; ++i)
36✔
1691
    {
1692
        auto poBand = GetRasterBand(i);
28✔
1693
        const char *SPECTRAL_RANGE_MEASURE_UNIT =
1694
            poBand->GetMetadataItem("SPECTRAL_RANGE_MEASURE_UNIT");
28✔
1695
        const char *SPECTRAL_RANGE_FWHM_MIN =
1696
            poBand->GetMetadataItem("SPECTRAL_RANGE_FWHM_MIN");
28✔
1697
        const char *SPECTRAL_RANGE_FWHM_MAX =
1698
            poBand->GetMetadataItem("SPECTRAL_RANGE_FWHM_MAX");
28✔
1699
        if (SPECTRAL_RANGE_MEASURE_UNIT && SPECTRAL_RANGE_FWHM_MIN &&
28✔
1700
            SPECTRAL_RANGE_FWHM_MAX &&
6✔
1701
            (EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "nanometer") ||
6✔
1702
             EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "micrometer")))
5✔
1703
        {
1704
            const double dfFactorToMicrometer =
6✔
1705
                EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "nanometer") ? 1e-3 : 1.0;
6✔
1706
            const double dfMin =
1707
                CPLAtof(SPECTRAL_RANGE_FWHM_MIN) * dfFactorToMicrometer;
6✔
1708
            const double dfMax =
1709
                CPLAtof(SPECTRAL_RANGE_FWHM_MAX) * dfFactorToMicrometer;
6✔
1710
            poBand->SetMetadataItem("CENTRAL_WAVELENGTH_UM",
6✔
1711
                                    CPLSPrintf("%.3f", (dfMin + dfMax) / 2),
6✔
1712
                                    "IMAGERY");
6✔
1713
            poBand->SetMetadataItem(
6✔
1714
                "FWHM_UM", CPLSPrintf("%.3f", dfMax - dfMin), "IMAGERY");
6✔
1715
        }
1716
    }
1717

1718
    /* -------------------------------------------------------------------- */
1719
    /*      Initialize any PAM information.                                 */
1720
    /* -------------------------------------------------------------------- */
1721
    SetDescription(osMDFilename);
8✔
1722
    TryLoadXML();
8✔
1723

1724
    /* -------------------------------------------------------------------- */
1725
    /*      Check for overviews.                                            */
1726
    /* -------------------------------------------------------------------- */
1727
    oOvManager.Initialize(this, osMDFilename);
8✔
1728

1729
    return TRUE;
8✔
1730
}
1731

1732
/************************************************************************/
1733
/*                          SetMetadataFromXML()                        */
1734
/************************************************************************/
1735

1736
void DIMAPDataset::SetMetadataFromXML(
20✔
1737
    CPLXMLNode *psProductIn, const char *const apszMetadataTranslation[],
1738
    bool bKeysFromRoot)
1739
{
1740
    CPLXMLNode *psDoc = psProductIn;
20✔
1741
    if (bKeysFromRoot)
20✔
1742
    {
1743
        psDoc = CPLGetXMLNode(psProductIn, "=Dimap_Document");
16✔
1744
        if (psDoc == nullptr)
16✔
1745
        {
1746
            psDoc = CPLGetXMLNode(psProductIn, "=PHR_DIMAP_Document");
×
1747
        }
1748
    }
1749

1750
    bool bWarnedDiscarding = false;
20✔
1751

1752
    for (int iTrItem = 0; apszMetadataTranslation[iTrItem] != nullptr;
122✔
1753
         iTrItem += 2)
102✔
1754
    {
1755
        CPLXMLNode *psParent =
1756
            CPLGetXMLNode(psDoc, apszMetadataTranslation[iTrItem]);
102✔
1757

1758
        if (psParent == nullptr)
102✔
1759
            continue;
16✔
1760

1761
        // Logic to support directly access a name/value entry
1762
        if (psParent->psChild != nullptr &&
86✔
1763
            psParent->psChild->eType == CXT_Text)
86✔
1764
        {
1765
            CPLString osName = apszMetadataTranslation[iTrItem + 1];
8✔
1766
            osName += apszMetadataTranslation[iTrItem];
4✔
1767
            // Limit size to avoid perf issues when inserting
1768
            // in metadata list
1769
            if (osName.size() < 128)
4✔
1770
                SetMetadataItem(osName, psParent->psChild->pszValue);
4✔
1771
            else if (!bWarnedDiscarding)
×
1772
            {
1773
                bWarnedDiscarding = true;
×
1774
                CPLDebug("DIMAP", "Discarding too long metadata item");
×
1775
            }
1776
            continue;
4✔
1777
        }
1778

1779
        // Logic to support a parent element with many name/values.
1780
        CPLXMLNode *psTarget = psParent->psChild;
82✔
1781
        for (; psTarget != nullptr && psTarget != psParent;
525✔
1782
             psTarget = psTarget->psNext)
443✔
1783
        {
1784
            if (psTarget->eType == CXT_Element && psTarget->psChild != nullptr)
443✔
1785
            {
1786
                CPLString osName = apszMetadataTranslation[iTrItem + 1];
874✔
1787

1788
                if (psTarget->psChild->eType == CXT_Text)
437✔
1789
                {
1790
                    osName += psTarget->pszValue;
307✔
1791
                    // Limit size to avoid perf issues when inserting
1792
                    // in metadata list
1793
                    if (osName.size() < 128)
307✔
1794
                        SetMetadataItem(osName, psTarget->psChild->pszValue);
307✔
1795
                    else if (!bWarnedDiscarding)
×
1796
                    {
1797
                        bWarnedDiscarding = true;
×
1798
                        CPLDebug("DIMAP", "Discarding too long metadata item");
×
1799
                    }
1800
                }
1801
                else if (psTarget->psChild->eType == CXT_Attribute)
130✔
1802
                {
1803
                    // find the tag value, at the end of the attributes.
1804
                    for (CPLXMLNode *psNode = psTarget->psChild;
76✔
1805
                         psNode != nullptr; psNode = psNode->psNext)
219✔
1806
                    {
1807
                        if (psNode->eType == CXT_Attribute)
143✔
1808
                            continue;
76✔
1809
                        else if (psNode->eType == CXT_Text)
67✔
1810
                        {
1811
                            osName += psTarget->pszValue;
67✔
1812
                            // Limit size to avoid perf issues when inserting
1813
                            // in metadata list
1814
                            if (osName.size() < 128)
67✔
1815
                                SetMetadataItem(osName, psNode->pszValue);
67✔
1816
                            else if (!bWarnedDiscarding)
×
1817
                            {
1818
                                bWarnedDiscarding = true;
×
1819
                                CPLDebug("DIMAP",
×
1820
                                         "Discarding too long metadata item");
1821
                            }
1822
                        }
1823
                    }
1824
                }
1825
            }
1826
        }
1827
    }
1828
}
20✔
1829

1830
/************************************************************************/
1831
/*                            GetGCPCount()                             */
1832
/************************************************************************/
1833

1834
int DIMAPDataset::GetGCPCount()
1✔
1835

1836
{
1837
    return nGCPCount;
1✔
1838
}
1839

1840
/************************************************************************/
1841
/*                          GetGCPSpatialRef()                          */
1842
/************************************************************************/
1843

1844
const OGRSpatialReference *DIMAPDataset::GetGCPSpatialRef() const
1✔
1845

1846
{
1847
    return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
1✔
1848
}
1849

1850
/************************************************************************/
1851
/*                               GetGCPs()                              */
1852
/************************************************************************/
1853

1854
const GDAL_GCP *DIMAPDataset::GetGCPs()
1✔
1855

1856
{
1857
    return pasGCPList;
1✔
1858
}
1859

1860
/************************************************************************/
1861
/*                         GDALRegister_DIMAP()                         */
1862
/************************************************************************/
1863

1864
void GDALRegister_DIMAP()
1,911✔
1865

1866
{
1867
    if (GDALGetDriverByName("DIMAP") != nullptr)
1,911✔
1868
        return;
282✔
1869

1870
    GDALDriver *poDriver = new GDALDriver();
1,629✔
1871

1872
    poDriver->SetDescription("DIMAP");
1,629✔
1873
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1,629✔
1874
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "SPOT DIMAP");
1,629✔
1875
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/dimap.html");
1,629✔
1876
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1,629✔
1877
    poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
1,629✔
1878

1879
    poDriver->pfnOpen = DIMAPDataset::Open;
1,629✔
1880
    poDriver->pfnIdentify = DIMAPDataset::Identify;
1,629✔
1881

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

© 2025 Coveralls, Inc