Coverage Report

Created: 2025-06-09 07:07

/src/gdal/frmts/dimap/dimapdataset.cpp
Line
Count
Source (jump to first uncovered line)
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
    double adfGeoTransform[6];
49
50
    CPLString osMDFilename;
51
    CPLString osImageDSFilename;
52
    CPLString osDIMAPFilename;
53
    int nProductVersion;
54
55
    char **papszXMLDimapMetadata;
56
57
  protected:
58
    int CloseDependentDatasets() override;
59
60
    int ReadImageInformation();
61
    int ReadImageInformation2();  // DIMAP 2.
62
63
    void SetMetadataFromXML(CPLXMLNode *psProduct,
64
                            const char *const apszMetadataTranslation[],
65
                            bool bKeysFromRoot = true);
66
67
  public:
68
    DIMAPDataset();
69
    ~DIMAPDataset() override;
70
71
    const OGRSpatialReference *GetSpatialRef() const override;
72
    CPLErr GetGeoTransform(double *) override;
73
    int GetGCPCount() override;
74
    const OGRSpatialReference *GetGCPSpatialRef() const override;
75
    const GDAL_GCP *GetGCPs() override;
76
    char **GetMetadataDomainList() override;
77
    char **GetMetadata(const char *pszDomain) override;
78
    char **GetFileList() override;
79
80
    CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
81
                     GDALDataType, int, BANDMAP_TYPE, GSpacing, GSpacing,
82
                     GSpacing, GDALRasterIOExtraArg *psExtraArg) override;
83
84
    static int Identify(GDALOpenInfo *);
85
    static GDALDataset *Open(GDALOpenInfo *);
86
87
    CPLXMLNode *GetProduct()
88
0
    {
89
0
        return psProduct;
90
0
    }
91
};
92
93
/************************************************************************/
94
/* ==================================================================== */
95
/*                              DIMAPDataset                            */
96
/* ==================================================================== */
97
/************************************************************************/
98
99
/************************************************************************/
100
/*                             DIMAPDataset()                            */
101
/************************************************************************/
102
103
DIMAPDataset::DIMAPDataset()
104
0
    : psProduct(nullptr), psProductDim(nullptr), psProductStrip(nullptr),
105
0
      poVRTDS(nullptr), nGCPCount(0), pasGCPList(nullptr),
106
0
      bHaveGeoTransform(FALSE), nProductVersion(1),
107
0
      papszXMLDimapMetadata(nullptr)
108
0
{
109
0
    m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
110
0
    m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
111
0
    adfGeoTransform[0] = 0.0;
112
0
    adfGeoTransform[1] = 1.0;
113
0
    adfGeoTransform[2] = 0.0;
114
0
    adfGeoTransform[3] = 0.0;
115
0
    adfGeoTransform[4] = 0.0;
116
0
    adfGeoTransform[5] = 1.0;
117
0
}
118
119
/************************************************************************/
120
/*                            ~DIMAPDataset()                           */
121
/************************************************************************/
122
123
DIMAPDataset::~DIMAPDataset()
124
125
0
{
126
0
    DIMAPDataset::FlushCache(true);
127
128
0
    CPLDestroyXMLNode(psProduct);
129
130
0
    if (psProductDim != nullptr && psProductDim != psProduct)
131
0
        CPLDestroyXMLNode(psProductDim);
132
0
    if (psProductStrip != nullptr)
133
0
        CPLDestroyXMLNode(psProductStrip);
134
0
    if (nGCPCount > 0)
135
0
    {
136
0
        GDALDeinitGCPs(nGCPCount, pasGCPList);
137
0
        CPLFree(pasGCPList);
138
0
    }
139
140
0
    CSLDestroy(papszXMLDimapMetadata);
141
142
0
    DIMAPDataset::CloseDependentDatasets();
143
0
}
144
145
/************************************************************************/
146
/*                        CloseDependentDatasets()                      */
147
/************************************************************************/
148
149
int DIMAPDataset::CloseDependentDatasets()
150
0
{
151
0
    int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
152
153
0
    if (poVRTDS != nullptr)
154
0
    {
155
0
        delete poVRTDS;
156
0
        poVRTDS = nullptr;
157
0
        bHasDroppedRef = TRUE;
158
0
    }
159
160
0
    return bHasDroppedRef;
161
0
}
162
163
/************************************************************************/
164
/*                      GetMetadataDomainList()                         */
165
/************************************************************************/
166
167
char **DIMAPDataset::GetMetadataDomainList()
168
0
{
169
0
    return BuildMetadataDomainList(GDALPamDataset::GetMetadataDomainList(),
170
0
                                   TRUE, "xml:dimap", nullptr);
171
0
}
172
173
/************************************************************************/
174
/*                            GetMetadata()                             */
175
/*                                                                      */
176
/*      We implement special support for fetching the full product      */
177
/*      metadata as xml.                                                */
178
/************************************************************************/
179
180
char **DIMAPDataset::GetMetadata(const char *pszDomain)
181
182
0
{
183
0
    if (pszDomain && EQUAL(pszDomain, "xml:dimap"))
184
0
    {
185
0
        if (papszXMLDimapMetadata == nullptr)
186
0
        {
187
0
            papszXMLDimapMetadata =
188
0
                reinterpret_cast<char **>(CPLCalloc(sizeof(char *), 2));
189
0
            papszXMLDimapMetadata[0] = CPLSerializeXMLTree(psProduct);
190
0
        }
191
0
        return papszXMLDimapMetadata;
192
0
    }
193
194
0
    return GDALPamDataset::GetMetadata(pszDomain);
195
0
}
196
197
/************************************************************************/
198
/*                          GetSpatialRef()                             */
199
/************************************************************************/
200
201
const OGRSpatialReference *DIMAPDataset::GetSpatialRef() const
202
203
0
{
204
0
    if (!m_oSRS.IsEmpty())
205
0
        return &m_oSRS;
206
207
0
    return GDALPamDataset::GetSpatialRef();
208
0
}
209
210
/************************************************************************/
211
/*                          GetGeoTransform()                           */
212
/************************************************************************/
213
214
CPLErr DIMAPDataset::GetGeoTransform(double *padfGeoTransform)
215
216
0
{
217
0
    if (bHaveGeoTransform)
218
0
    {
219
0
        memcpy(padfGeoTransform, adfGeoTransform, sizeof(double) * 6);
220
0
        return CE_None;
221
0
    }
222
223
0
    return GDALPamDataset::GetGeoTransform(padfGeoTransform);
224
0
}
225
226
/************************************************************************/
227
/*                            GetFileList()                             */
228
/************************************************************************/
229
230
char **DIMAPDataset::GetFileList()
231
232
0
{
233
0
    char **papszFileList = GDALPamDataset::GetFileList();
234
0
    char **papszImageFiles = poVRTDS->GetFileList();
235
236
0
    papszFileList = CSLInsertStrings(papszFileList, -1, papszImageFiles);
237
238
0
    CSLDestroy(papszImageFiles);
239
240
0
    return papszFileList;
241
0
}
242
243
/************************************************************************/
244
/* ==================================================================== */
245
/*                            DIMAPRasterBand                           */
246
/* ==================================================================== */
247
/************************************************************************/
248
249
class DIMAPRasterBand final : public GDALPamRasterBand
250
{
251
    friend class DIMAPDataset;
252
253
    VRTSourcedRasterBand *poVRTBand;
254
255
  public:
256
    DIMAPRasterBand(DIMAPDataset *, int, VRTSourcedRasterBand *);
257
258
    ~DIMAPRasterBand() override
259
0
    {
260
0
    }
261
262
    CPLErr IReadBlock(int, int, void *) override;
263
    CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
264
                     GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace,
265
                     GDALRasterIOExtraArg *psExtraArg) override;
266
    int GetOverviewCount() override;
267
    GDALRasterBand *GetOverview(int) override;
268
    CPLErr ComputeRasterMinMax(int bApproxOK, double adfMinMax[2]) override;
269
    CPLErr ComputeStatistics(int bApproxOK, double *pdfMin, double *pdfMax,
270
                             double *pdfMean, double *pdfStdDev,
271
                             GDALProgressFunc, void *pProgressData) override;
272
273
    CPLErr GetHistogram(double dfMin, double dfMax, int nBuckets,
274
                        GUIntBig *panHistogram, int bIncludeOutOfRange,
275
                        int bApproxOK, GDALProgressFunc,
276
                        void *pProgressData) override;
277
};
278
279
/************************************************************************/
280
/*                          DIMAPRasterBand()                           */
281
/************************************************************************/
282
283
DIMAPRasterBand::DIMAPRasterBand(DIMAPDataset *poDIMAPDS, int nBandIn,
284
                                 VRTSourcedRasterBand *poVRTBandIn)
285
0
    : poVRTBand(poVRTBandIn)
286
0
{
287
0
    poDS = poDIMAPDS;
288
0
    nBand = nBandIn;
289
0
    eDataType = poVRTBandIn->GetRasterDataType();
290
291
0
    poVRTBandIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
292
0
}
293
294
/************************************************************************/
295
/*                             IReadBlock()                             */
296
/************************************************************************/
297
298
CPLErr DIMAPRasterBand::IReadBlock(int iBlockX, int iBlockY, void *pBuffer)
299
300
0
{
301
0
    return poVRTBand->ReadBlock(iBlockX, iBlockY, pBuffer);
302
0
}
303
304
/************************************************************************/
305
/*                             IRasterIO()                              */
306
/************************************************************************/
307
308
CPLErr DIMAPRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
309
                                  int nXSize, int nYSize, void *pData,
310
                                  int nBufXSize, int nBufYSize,
311
                                  GDALDataType eBufType, GSpacing nPixelSpace,
312
                                  GSpacing nLineSpace,
313
                                  GDALRasterIOExtraArg *psExtraArg)
314
315
0
{
316
0
    if (GDALPamRasterBand::GetOverviewCount() > 0)
317
0
    {
318
0
        return GDALPamRasterBand::IRasterIO(
319
0
            eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
320
0
            eBufType, nPixelSpace, nLineSpace, psExtraArg);
321
0
    }
322
323
    // If not exist DIMAP overviews, try to use band source overviews.
324
0
    return poVRTBand->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
325
0
                                nBufXSize, nBufYSize, eBufType, nPixelSpace,
326
0
                                nLineSpace, psExtraArg);
327
0
}
328
329
/************************************************************************/
330
/*                             IRasterIO()                              */
331
/************************************************************************/
332
333
CPLErr DIMAPDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
334
                               int nXSize, int nYSize, void *pData,
335
                               int nBufXSize, int nBufYSize,
336
                               GDALDataType eBufType, int nBandCount,
337
                               BANDMAP_TYPE panBandMap, GSpacing nPixelSpace,
338
                               GSpacing nLineSpace, GSpacing nBandSpace,
339
                               GDALRasterIOExtraArg *psExtraArg)
340
341
0
{
342
0
    if (cpl::down_cast<DIMAPRasterBand *>(papoBands[0])
343
0
            ->GDALPamRasterBand::GetOverviewCount() > 0)
344
0
    {
345
0
        return GDALPamDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
346
0
                                         pData, nBufXSize, nBufYSize, eBufType,
347
0
                                         nBandCount, panBandMap, nPixelSpace,
348
0
                                         nLineSpace, nBandSpace, psExtraArg);
349
0
    }
350
351
0
    return poVRTDS->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
352
0
                              nBufXSize, nBufYSize, eBufType, nBandCount,
353
0
                              panBandMap, nPixelSpace, nLineSpace, nBandSpace,
354
0
                              psExtraArg);
355
0
}
356
357
/************************************************************************/
358
/*                          GetOverviewCount()                          */
359
/************************************************************************/
360
361
int DIMAPRasterBand::GetOverviewCount()
362
0
{
363
0
    if (GDALPamRasterBand::GetOverviewCount() > 0)
364
0
    {
365
0
        return GDALPamRasterBand::GetOverviewCount();
366
0
    }
367
0
    return poVRTBand->GetOverviewCount();
368
0
}
369
370
/************************************************************************/
371
/*                             GetOverview()                            */
372
/************************************************************************/
373
374
GDALRasterBand *DIMAPRasterBand::GetOverview(int iOvr)
375
0
{
376
0
    if (GDALPamRasterBand::GetOverviewCount() > 0)
377
0
    {
378
0
        return GDALPamRasterBand::GetOverview(iOvr);
379
0
    }
380
0
    return poVRTBand->GetOverview(iOvr);
381
0
}
382
383
/************************************************************************/
384
/*                         ComputeRasterMinMax()                        */
385
/************************************************************************/
386
387
CPLErr DIMAPRasterBand::ComputeRasterMinMax(int bApproxOK, double adfMinMax[2])
388
0
{
389
0
    if (GDALPamRasterBand::GetOverviewCount() > 0)
390
0
    {
391
0
        return GDALPamRasterBand::ComputeRasterMinMax(bApproxOK, adfMinMax);
392
0
    }
393
0
    return poVRTBand->ComputeRasterMinMax(bApproxOK, adfMinMax);
394
0
}
395
396
/************************************************************************/
397
/*                          ComputeStatistics()                         */
398
/************************************************************************/
399
400
CPLErr DIMAPRasterBand::ComputeStatistics(int bApproxOK, double *pdfMin,
401
                                          double *pdfMax, double *pdfMean,
402
                                          double *pdfStdDev,
403
                                          GDALProgressFunc pfnProgress,
404
                                          void *pProgressData)
405
0
{
406
0
    if (GDALPamRasterBand::GetOverviewCount() > 0)
407
0
    {
408
0
        return GDALPamRasterBand::ComputeStatistics(bApproxOK, pdfMin, pdfMax,
409
0
                                                    pdfMean, pdfStdDev,
410
0
                                                    pfnProgress, pProgressData);
411
0
    }
412
0
    return poVRTBand->ComputeStatistics(bApproxOK, pdfMin, pdfMax, pdfMean,
413
0
                                        pdfStdDev, pfnProgress, pProgressData);
414
0
}
415
416
/************************************************************************/
417
/*                            GetHistogram()                            */
418
/************************************************************************/
419
420
CPLErr DIMAPRasterBand::GetHistogram(double dfMin, double dfMax, int nBuckets,
421
                                     GUIntBig *panHistogram,
422
                                     int bIncludeOutOfRange, int bApproxOK,
423
                                     GDALProgressFunc pfnProgress,
424
                                     void *pProgressData)
425
0
{
426
0
    if (GDALPamRasterBand::GetOverviewCount() > 0)
427
0
    {
428
0
        return GDALPamRasterBand::GetHistogram(
429
0
            dfMin, dfMax, nBuckets, panHistogram, bIncludeOutOfRange, bApproxOK,
430
0
            pfnProgress, pProgressData);
431
0
    }
432
0
    return poVRTBand->GetHistogram(dfMin, dfMax, nBuckets, panHistogram,
433
0
                                   bIncludeOutOfRange, bApproxOK, pfnProgress,
434
0
                                   pProgressData);
435
0
}
436
437
/************************************************************************/
438
/*                              Identify()                              */
439
/************************************************************************/
440
441
int DIMAPDataset::Identify(GDALOpenInfo *poOpenInfo)
442
443
16.8k
{
444
16.8k
    if (STARTS_WITH(poOpenInfo->pszFilename, "DIMAP:"))
445
0
        return true;
446
447
16.8k
    if (poOpenInfo->nHeaderBytes >= 100)
448
15.8k
    {
449
15.8k
        if ((strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
450
15.8k
                    "<Dimap_Document") == nullptr) &&
451
15.8k
            (strstr(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
452
15.5k
                    "<PHR_DIMAP_Document") == nullptr))
453
14.7k
            return FALSE;
454
455
1.09k
        return TRUE;
456
15.8k
    }
457
1.00k
    else if (poOpenInfo->bIsDirectory)
458
93
    {
459
        // DIMAP file.
460
93
        CPLString osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
461
93
                                                       "METADATA.DIM", nullptr);
462
463
93
        VSIStatBufL sStat;
464
93
        if (VSIStatL(osMDFilename, &sStat) == 0)
465
0
        {
466
            // Make sure this is really a Dimap format.
467
0
            GDALOpenInfo oOpenInfo(osMDFilename, GA_ReadOnly, nullptr);
468
0
            if (oOpenInfo.nHeaderBytes >= 100)
469
0
            {
470
0
                if (strstr(reinterpret_cast<char *>(oOpenInfo.pabyHeader),
471
0
                           "<Dimap_Document") == nullptr)
472
0
                    return FALSE;
473
474
0
                return TRUE;
475
0
            }
476
0
        }
477
93
        else
478
93
        {
479
            // DIMAP 2 file.
480
93
            osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
481
93
                                                 "VOL_PHR.XML", nullptr);
482
483
93
            if (VSIStatL(osMDFilename, &sStat) == 0)
484
0
                return TRUE;
485
486
            // DIMAP VHR2020 file.
487
93
            osMDFilename = CPLFormCIFilenameSafe(poOpenInfo->pszFilename,
488
93
                                                 "VOL_PNEO.XML", nullptr);
489
490
93
            if (VSIStatL(osMDFilename, &sStat) == 0)
491
0
                return TRUE;
492
493
93
            return FALSE;
494
93
        }
495
93
    }
496
497
911
    return FALSE;
498
16.8k
}
499
500
/************************************************************************/
501
/*                                Open()                                */
502
/************************************************************************/
503
504
GDALDataset *DIMAPDataset::Open(GDALOpenInfo *poOpenInfo)
505
506
549
{
507
549
    if (!Identify(poOpenInfo))
508
0
        return nullptr;
509
510
    /* -------------------------------------------------------------------- */
511
    /*      Confirm the requested access is supported.                      */
512
    /* -------------------------------------------------------------------- */
513
549
    if (poOpenInfo->eAccess == GA_Update)
514
0
    {
515
0
        ReportUpdateNotSupportedByDriver("DIMAP");
516
0
        return nullptr;
517
0
    }
518
    /* -------------------------------------------------------------------- */
519
    /*      Get the metadata filename.                                      */
520
    /* -------------------------------------------------------------------- */
521
549
    CPLString osFilename;
522
549
    CPLString osSelectedSubdataset;
523
524
549
    if (STARTS_WITH(poOpenInfo->pszFilename, "DIMAP:"))
525
0
    {
526
0
        CPLStringList aosTokens(CSLTokenizeString2(poOpenInfo->pszFilename, ":",
527
0
                                                   CSLT_HONOURSTRINGS));
528
0
        if (aosTokens.size() != 3)
529
0
            return nullptr;
530
531
0
        osFilename = aosTokens[1];
532
0
        osSelectedSubdataset = aosTokens[2];
533
0
    }
534
549
    else
535
549
    {
536
549
        osFilename = poOpenInfo->pszFilename;
537
549
    }
538
539
549
    VSIStatBufL sStat;
540
549
    std::string osMDFilename(osFilename);
541
549
    if (VSIStatL(osFilename.c_str(), &sStat) == 0 && VSI_ISDIR(sStat.st_mode))
542
0
    {
543
0
        osMDFilename =
544
0
            CPLFormCIFilenameSafe(osFilename, "METADATA.DIM", nullptr);
545
546
        /* DIMAP2 */
547
0
        if (VSIStatL(osMDFilename.c_str(), &sStat) != 0)
548
0
        {
549
0
            osMDFilename =
550
0
                CPLFormCIFilenameSafe(osFilename, "VOL_PHR.XML", nullptr);
551
0
            if (VSIStatL(osMDFilename.c_str(), &sStat) != 0)
552
0
            {
553
                // DIMAP VHR2020 file.
554
0
                osMDFilename =
555
0
                    CPLFormCIFilenameSafe(osFilename, "VOL_PNEO.XML", nullptr);
556
0
            }
557
0
        }
558
0
    }
559
560
    /* -------------------------------------------------------------------- */
561
    /*      Ingest the xml file.                                            */
562
    /* -------------------------------------------------------------------- */
563
549
    CPLXMLNode *psProduct = CPLParseXMLFile(osMDFilename.c_str());
564
549
    if (psProduct == nullptr)
565
449
        return nullptr;
566
567
100
    CPLXMLNode *psDoc = CPLGetXMLNode(psProduct, "=Dimap_Document");
568
100
    if (!psDoc)
569
100
        psDoc = CPLGetXMLNode(psProduct, "=PHR_DIMAP_Document");
570
571
    // We check the for the tag Metadata_Identification.METADATA_FORMAT.
572
    // The metadata will be set to 2.0 for DIMAP2.
573
100
    double dfMetadataFormatVersion = CPLAtof(CPLGetXMLValue(
574
100
        CPLGetXMLNode(psDoc, "Metadata_Identification.METADATA_FORMAT"),
575
100
        "version", "1"));
576
577
100
    const int nProductVersion = dfMetadataFormatVersion >= 2.0 ? 2 : 1;
578
579
100
    std::string osImageDSFilename;
580
100
    std::string osDIMAPFilename;
581
100
    std::string osRPCFilename;
582
100
    CPLXMLNode *psProductDim = nullptr;
583
100
    CPLXMLNode *psProductStrip = nullptr;
584
585
100
    CPLStringList aosSubdatasets;
586
587
    // Check needed information for the DIMAP format.
588
100
    if (nProductVersion == 1)
589
100
    {
590
100
        CPLXMLNode *psImageAttributes =
591
100
            CPLGetXMLNode(psDoc, "Raster_Dimensions");
592
100
        if (psImageAttributes == nullptr)
593
100
        {
594
100
            CPLError(CE_Failure, CPLE_OpenFailed,
595
100
                     "Failed to find <Raster_Dimensions> in document.");
596
100
            CPLDestroyXMLNode(psProduct);
597
100
            return nullptr;
598
100
        }
599
100
    }
600
0
    else  // DIMAP2.
601
0
    {
602
        // Verify if the opened file is not already a product dimap
603
0
        if (CPLGetXMLNode(psDoc, "Raster_Data"))
604
0
        {
605
0
            psProductDim = psProduct;
606
0
            osDIMAPFilename = osMDFilename;
607
0
        }
608
0
        else
609
0
        {
610
            // Verify the presence of the DIMAP product file.
611
0
            CPLXMLNode *psDatasetComponents =
612
0
                CPLGetXMLNode(psDoc, "Dataset_Content.Dataset_Components");
613
614
0
            if (psDatasetComponents == nullptr)
615
0
            {
616
0
                CPLError(CE_Failure, CPLE_OpenFailed,
617
0
                         "Failed to find <Dataset_Components> in document.");
618
0
                CPLDestroyXMLNode(psProduct);
619
0
                return nullptr;
620
0
            }
621
622
0
            for (CPLXMLNode *psDatasetComponent = psDatasetComponents->psChild;
623
0
                 psDatasetComponent != nullptr;
624
0
                 psDatasetComponent = psDatasetComponent->psNext)
625
0
            {
626
0
                const char *pszComponentType =
627
0
                    CPLGetXMLValue(psDatasetComponent, "COMPONENT_TYPE", "");
628
0
                if (strcmp(pszComponentType, "DIMAP") == 0)
629
0
                {
630
                    // DIMAP product found.
631
0
                    const char *pszHref = CPLGetXMLValue(
632
0
                        psDatasetComponent, "COMPONENT_PATH.href", "");
633
0
                    const CPLString osComponentTitle(CPLGetXMLValue(
634
0
                        psDatasetComponent, "COMPONENT_TITLE", ""));
635
0
                    const CPLString osComponentTitleLaundered(
636
0
                        CPLString(osComponentTitle).replaceAll(' ', '_'));
637
638
0
                    if (strlen(pszHref) > 0 && osDIMAPFilename.empty() &&
639
0
                        (osSelectedSubdataset.empty() ||
640
0
                         osSelectedSubdataset == osComponentTitleLaundered))
641
0
                    {
642
0
                        if (poOpenInfo->bIsDirectory)
643
0
                        {
644
0
                            osDIMAPFilename = CPLFormCIFilenameSafe(
645
0
                                poOpenInfo->pszFilename, pszHref, nullptr);
646
0
                        }
647
0
                        else
648
0
                        {
649
0
                            CPLString osPath =
650
0
                                CPLGetPathSafe(osMDFilename.c_str());
651
0
                            osDIMAPFilename =
652
0
                                CPLFormFilenameSafe(osPath, pszHref, nullptr);
653
0
                        }
654
655
                        // Data file might be specified there.
656
0
                        const char *pszDataFileHref = CPLGetXMLValue(
657
0
                            psDatasetComponent,
658
0
                            "Data_Files.Data_File.DATA_FILE_PATH.href", "");
659
660
0
                        if (strlen(pszDataFileHref) > 0)
661
0
                        {
662
0
                            CPLString osPath =
663
0
                                CPLGetPathSafe(osMDFilename.c_str());
664
0
                            osImageDSFilename = CPLFormFilenameSafe(
665
0
                                osPath, pszDataFileHref, nullptr);
666
0
                        }
667
0
                    }
668
669
0
                    const int iIdx =
670
0
                        static_cast<int>(aosSubdatasets.size() / 2 + 1);
671
0
                    aosSubdatasets.SetNameValue(
672
0
                        CPLSPrintf("SUBDATASET_%d_NAME", iIdx),
673
0
                        CPLSPrintf("DIMAP:\"%s\":%s", poOpenInfo->pszFilename,
674
0
                                   osComponentTitleLaundered.c_str()));
675
0
                    aosSubdatasets.SetNameValue(
676
0
                        CPLSPrintf("SUBDATASET_%d_DESC", iIdx),
677
0
                        CPLSPrintf("Component %s", osComponentTitle.c_str()));
678
0
                }
679
0
            }
680
681
0
            psProductDim = CPLParseXMLFile(osDIMAPFilename.c_str());
682
0
            if (psProductDim == nullptr)
683
0
            {
684
0
                CPLDestroyXMLNode(psProduct);
685
0
                return nullptr;
686
0
            }
687
0
        }
688
689
        // We need the {STRIP|RPC}_<product_id>.XML file for a few metadata.
690
0
        CPLXMLNode *psDocDim = CPLGetXMLNode(psProductDim, "=Dimap_Document");
691
0
        if (!psDocDim)
692
0
            psDocDim = CPLGetXMLNode(psProductDim, "=PHR_DIMAP_Document");
693
694
0
        CPLXMLNode *psDatasetSources =
695
0
            CPLGetXMLNode(psDocDim, "Dataset_Sources");
696
0
        if (psDatasetSources != nullptr)
697
0
        {
698
0
            CPLString osSTRIPFilename;
699
700
0
            for (CPLXMLNode *psDatasetSource = psDatasetSources->psChild;
701
0
                 psDatasetSource != nullptr;
702
0
                 psDatasetSource = psDatasetSource->psNext)
703
0
            {
704
0
                const char *pszSourceType =
705
0
                    CPLGetXMLValue(psDatasetSource, "SOURCE_TYPE", "");
706
0
                if (strcmp(pszSourceType, "Strip_Source") == 0)
707
0
                {
708
0
                    const char *pszHref = CPLGetXMLValue(
709
0
                        psDatasetSource, "Component.COMPONENT_PATH.href", "");
710
711
0
                    if (strlen(pszHref) > 0)  // STRIP product found.
712
0
                    {
713
0
                        CPLString osPath =
714
0
                            CPLGetPathSafe(osDIMAPFilename.c_str());
715
0
                        osSTRIPFilename =
716
0
                            CPLFormCIFilenameSafe(osPath, pszHref, nullptr);
717
0
                        if (VSIStatL(osSTRIPFilename, &sStat) == 0)
718
0
                        {
719
0
                            psProductStrip = CPLParseXMLFile(osSTRIPFilename);
720
0
                            break;
721
0
                        }
722
0
                    }
723
0
                }
724
0
            }
725
0
        }
726
727
0
        CPLXMLNode *psDatasetRFMComponents = CPLGetXMLNode(
728
0
            psDocDim, "Geoposition.Geoposition_Models.Rational_Function_Model");
729
0
        if (psDatasetRFMComponents != nullptr)
730
0
        {
731
0
            for (CPLXMLNode *psDatasetRFMComponent =
732
0
                     psDatasetRFMComponents->psChild;
733
0
                 psDatasetRFMComponent != nullptr;
734
0
                 psDatasetRFMComponent = psDatasetRFMComponent->psNext)
735
0
            {
736
0
                const char *pszComponentTitle = CPLGetXMLValue(
737
0
                    psDatasetRFMComponent, "COMPONENT_TITLE", "");
738
0
                if (strcmp(pszComponentTitle, "RPC Model") == 0)
739
0
                {
740
0
                    const char *pszHref = CPLGetXMLValue(
741
0
                        psDatasetRFMComponent, "COMPONENT_PATH.href", "");
742
743
0
                    if (strlen(pszHref) > 0)  // RPC product found.
744
0
                    {
745
0
                        CPLString osPath =
746
0
                            CPLGetPathSafe(osDIMAPFilename.c_str());
747
0
                        osRPCFilename =
748
0
                            CPLFormCIFilenameSafe(osPath, pszHref, nullptr);
749
750
0
                        break;
751
0
                    }
752
0
                }
753
0
            }
754
0
        }
755
0
    }
756
757
    /* -------------------------------------------------------------------- */
758
    /*      Create the dataset.                                             */
759
    /* -------------------------------------------------------------------- */
760
0
    DIMAPDataset *poDS = new DIMAPDataset();
761
762
0
    if (osSelectedSubdataset.empty() && aosSubdatasets.size() > 2)
763
0
    {
764
0
        poDS->GDALDataset::SetMetadata(aosSubdatasets.List(), "SUBDATASETS");
765
0
    }
766
0
    poDS->psProduct = psProduct;
767
0
    poDS->psProductDim = psProductDim;
768
0
    poDS->psProductStrip = psProductStrip;
769
0
    poDS->osRPCFilename = std::move(osRPCFilename);
770
0
    poDS->nProductVersion = nProductVersion;
771
0
    poDS->osMDFilename = std::move(osMDFilename);
772
0
    poDS->osImageDSFilename = std::move(osImageDSFilename);
773
0
    poDS->osDIMAPFilename = std::move(osDIMAPFilename);
774
775
0
    const int res = (nProductVersion == 2) ? poDS->ReadImageInformation2()
776
0
                                           : poDS->ReadImageInformation();
777
778
0
    if (res == FALSE)
779
0
    {
780
0
        delete poDS;
781
0
        return nullptr;
782
0
    }
783
784
0
    return poDS;
785
0
}
786
787
/************************************************************************/
788
/*               ReadImageInformation() DIMAP Version 1                 */
789
/************************************************************************/
790
791
int DIMAPDataset::ReadImageInformation()
792
0
{
793
0
    CPLXMLNode *psDoc = CPLGetXMLNode(psProduct, "=Dimap_Document");
794
0
    if (!psDoc)
795
0
        psDoc = CPLGetXMLNode(psProduct, "=PHR_DIMAP_Document");
796
797
    /* -------------------------------------------------------------------- */
798
    /*      Get overall image information.                                  */
799
    /* -------------------------------------------------------------------- */
800
801
    // TODO: DIMAP 1 probably handle mosaics? Like DIMAP 2?
802
803
    /* -------------------------------------------------------------------- */
804
    /*      Get the name of the underlying file.                            */
805
    /* -------------------------------------------------------------------- */
806
807
0
    const char *pszHref =
808
0
        CPLGetXMLValue(psDoc, "Data_Access.Data_File.DATA_FILE_PATH.href", "");
809
0
    CPLString osPath = CPLGetPathSafe(osMDFilename);
810
0
    CPLString osImageFilename = CPLFormFilenameSafe(osPath, pszHref, nullptr);
811
812
    /* -------------------------------------------------------------------- */
813
    /*      Try and open the file.                                          */
814
    /* -------------------------------------------------------------------- */
815
816
0
    auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
817
0
        osImageFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
818
0
    if (poImageDS == nullptr)
819
0
    {
820
0
        return FALSE;
821
0
    }
822
0
    nRasterXSize = poImageDS->GetRasterXSize();
823
0
    nRasterYSize = poImageDS->GetRasterYSize();
824
825
    /* -------------------------------------------------------------------- */
826
    /*      Create and initialize the corresponding VRT dataset used to     */
827
    /*      manage the tiled data access.                                   */
828
    /* -------------------------------------------------------------------- */
829
0
    poVRTDS = new VRTDataset(nRasterXSize, nRasterYSize);
830
831
    // Don't try to write a VRT file.
832
0
    poVRTDS->SetWritable(FALSE);
833
834
0
    for (int iBand = 0; iBand < poImageDS->GetRasterCount(); iBand++)
835
0
    {
836
0
        poVRTDS->AddBand(
837
0
            poImageDS->GetRasterBand(iBand + 1)->GetRasterDataType(), nullptr);
838
839
0
        VRTSourcedRasterBand *poVRTBand =
840
0
            reinterpret_cast<VRTSourcedRasterBand *>(
841
0
                poVRTDS->GetRasterBand(iBand + 1));
842
843
0
        poVRTBand->AddSimpleSource(osImageFilename, iBand + 1, 0, 0,
844
0
                                   nRasterXSize, nRasterYSize, 0, 0,
845
0
                                   nRasterXSize, nRasterYSize);
846
0
    }
847
848
    /* -------------------------------------------------------------------- */
849
    /*      Create band information objects.                                */
850
    /* -------------------------------------------------------------------- */
851
0
    for (int iBand = 1; iBand <= poVRTDS->GetRasterCount(); iBand++)
852
0
    {
853
0
        SetBand(iBand, new DIMAPRasterBand(this, iBand,
854
0
                                           static_cast<VRTSourcedRasterBand *>(
855
0
                                               poVRTDS->GetRasterBand(iBand))));
856
0
    }
857
858
    /* -------------------------------------------------------------------- */
859
    /*      Try to collect simple insertion point.                          */
860
    /* -------------------------------------------------------------------- */
861
0
    CPLXMLNode *psGeoLoc =
862
0
        CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Insert");
863
864
0
    if (psGeoLoc != nullptr)
865
0
    {
866
0
        bHaveGeoTransform = TRUE;
867
0
        adfGeoTransform[0] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULXMAP", "0"));
868
0
        adfGeoTransform[1] = CPLAtof(CPLGetXMLValue(psGeoLoc, "XDIM", "0"));
869
0
        adfGeoTransform[2] = 0.0;
870
0
        adfGeoTransform[3] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULYMAP", "0"));
871
0
        adfGeoTransform[4] = 0.0;
872
0
        adfGeoTransform[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc, "YDIM", "0"));
873
0
    }
874
0
    else
875
0
    {
876
        // Try to get geotransform from underlying raster.
877
0
        if (poImageDS->GetGeoTransform(adfGeoTransform) == CE_None)
878
0
            bHaveGeoTransform = TRUE;
879
0
    }
880
881
    /* -------------------------------------------------------------------- */
882
    /*      Collect GCPs.                                                   */
883
    /* -------------------------------------------------------------------- */
884
0
    psGeoLoc = CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Points");
885
886
0
    if (psGeoLoc != nullptr)
887
0
    {
888
        // Count gcps.
889
0
        nGCPCount = 0;
890
0
        for (CPLXMLNode *psNode = psGeoLoc->psChild; psNode != nullptr;
891
0
             psNode = psNode->psNext)
892
0
        {
893
0
            if (EQUAL(psNode->pszValue, "Tie_Point"))
894
0
                nGCPCount++;
895
0
        }
896
897
0
        pasGCPList =
898
0
            static_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), nGCPCount));
899
900
0
        nGCPCount = 0;
901
902
0
        for (CPLXMLNode *psNode = psGeoLoc->psChild; psNode != nullptr;
903
0
             psNode = psNode->psNext)
904
0
        {
905
0
            GDAL_GCP *psGCP = pasGCPList + nGCPCount;
906
907
0
            if (!EQUAL(psNode->pszValue, "Tie_Point"))
908
0
                continue;
909
910
0
            nGCPCount++;
911
912
0
            char szID[32] = {};
913
0
            snprintf(szID, sizeof(szID), "%d", nGCPCount);
914
0
            psGCP->pszId = CPLStrdup(szID);
915
0
            psGCP->pszInfo = CPLStrdup("");
916
0
            psGCP->dfGCPPixel =
917
0
                CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_DATA_X", "0")) - 0.5;
918
0
            psGCP->dfGCPLine =
919
0
                CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_DATA_Y", "0")) - 0.5;
920
0
            psGCP->dfGCPX =
921
0
                CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_X", ""));
922
0
            psGCP->dfGCPY =
923
0
                CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_Y", ""));
924
0
            psGCP->dfGCPZ =
925
0
                CPLAtof(CPLGetXMLValue(psNode, "TIE_POINT_CRS_Z", ""));
926
0
        }
927
0
    }
928
929
    /* -------------------------------------------------------------------- */
930
    /*      Collect the CRS.  For now we look only for EPSG codes.          */
931
    /* -------------------------------------------------------------------- */
932
0
    const char *pszSRS = CPLGetXMLValue(
933
0
        psDoc, "Coordinate_Reference_System.Horizontal_CS.HORIZONTAL_CS_CODE",
934
0
        nullptr);
935
936
0
    if (pszSRS != nullptr)
937
0
    {
938
0
        OGRSpatialReference &oSRS = nGCPCount > 0 ? m_oGCPSRS : m_oSRS;
939
0
        oSRS.SetFromUserInput(
940
0
            pszSRS, OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
941
0
    }
942
0
    else
943
0
    {
944
        // Check underlying raster for SRS. We have cases where
945
        // HORIZONTAL_CS_CODE is empty and the underlying raster
946
        // is georeferenced (rprinceley).
947
0
        const auto poSRS = poImageDS->GetSpatialRef();
948
0
        if (poSRS)
949
0
        {
950
0
            m_oSRS = *poSRS;
951
0
        }
952
0
    }
953
954
    /* -------------------------------------------------------------------- */
955
    /*      Translate other metadata of interest.                           */
956
    /* -------------------------------------------------------------------- */
957
0
    static const char *const apszMetadataTranslation[] = {
958
0
        "Production",
959
0
        "",
960
0
        "Production.Facility",
961
0
        "FACILITY_",
962
0
        "Dataset_Sources.Source_Information.Scene_Source",
963
0
        "",
964
0
        "Data_Processing",
965
0
        "",
966
0
        "Image_Interpretation.Spectral_Band_Info",
967
0
        "SPECTRAL_",
968
0
        nullptr,
969
0
        nullptr};
970
971
0
    SetMetadataFromXML(psProduct, apszMetadataTranslation);
972
973
    /* -------------------------------------------------------------------- */
974
    /*      Set Band metadata from the <Spectral_Band_Info> content         */
975
    /* -------------------------------------------------------------------- */
976
977
0
    CPLXMLNode *psImageInterpretationNode =
978
0
        CPLGetXMLNode(psDoc, "Image_Interpretation");
979
0
    if (psImageInterpretationNode != nullptr)
980
0
    {
981
0
        CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
982
0
        while (psSpectralBandInfoNode != nullptr)
983
0
        {
984
0
            if (psSpectralBandInfoNode->eType == CXT_Element &&
985
0
                EQUAL(psSpectralBandInfoNode->pszValue, "Spectral_Band_Info"))
986
0
            {
987
0
                CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
988
0
                int nBandIndex = 0;
989
0
                while (psTag != nullptr)
990
0
                {
991
0
                    if (psTag->eType == CXT_Element &&
992
0
                        psTag->psChild != nullptr &&
993
0
                        psTag->psChild->eType == CXT_Text &&
994
0
                        psTag->pszValue != nullptr)
995
0
                    {
996
0
                        if (EQUAL(psTag->pszValue, "BAND_INDEX"))
997
0
                        {
998
0
                            nBandIndex = atoi(psTag->psChild->pszValue);
999
0
                            if (nBandIndex <= 0 ||
1000
0
                                nBandIndex > poImageDS->GetRasterCount())
1001
0
                            {
1002
0
                                CPLError(CE_Warning, CPLE_AppDefined,
1003
0
                                         "Bad BAND_INDEX value : %s",
1004
0
                                         psTag->psChild->pszValue);
1005
0
                                nBandIndex = 0;
1006
0
                            }
1007
0
                        }
1008
0
                        else if (nBandIndex >= 1)
1009
0
                        {
1010
0
                            GetRasterBand(nBandIndex)
1011
0
                                ->SetMetadataItem(psTag->pszValue,
1012
0
                                                  psTag->psChild->pszValue);
1013
0
                        }
1014
0
                    }
1015
0
                    psTag = psTag->psNext;
1016
0
                }
1017
0
            }
1018
0
            psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
1019
0
        }
1020
0
    }
1021
1022
    /* -------------------------------------------------------------------- */
1023
    /*      Initialize any PAM information.                                 */
1024
    /* -------------------------------------------------------------------- */
1025
0
    SetDescription(osMDFilename);
1026
0
    TryLoadXML();
1027
1028
    /* -------------------------------------------------------------------- */
1029
    /*      Check for overviews.                                            */
1030
    /* -------------------------------------------------------------------- */
1031
0
    oOvManager.Initialize(this, osMDFilename);
1032
1033
    // CID 163546 - poTileDS dereferenced above.
1034
    // coverity[leaked_storage]
1035
0
    return TRUE;
1036
0
}
1037
1038
/************************************************************************/
1039
/*               ReadImageInformation() DIMAP Version 2                 */
1040
/************************************************************************/
1041
1042
int DIMAPDataset::ReadImageInformation2()
1043
0
{
1044
0
    CPLXMLNode *psDoc = CPLGetXMLNode(psProductDim, "=Dimap_Document");
1045
0
    if (!psDoc)
1046
0
        psDoc = CPLGetXMLNode(psProductDim, "=PHR_DIMAP_Document");
1047
1048
0
    CPLXMLNode *psImageAttributes =
1049
0
        CPLGetXMLNode(psDoc, "Raster_Data.Raster_Dimensions");
1050
0
    if (psImageAttributes == nullptr)
1051
0
    {
1052
0
        CPLError(CE_Failure, CPLE_OpenFailed,
1053
0
                 "Failed to find <Raster_Dimensions> in document.");
1054
0
        return FALSE;
1055
0
    }
1056
1057
    /* -------------------------------------------------------------------- */
1058
    /*      Get overall image information.                                  */
1059
    /* -------------------------------------------------------------------- */
1060
1061
    /*
1062
        <Raster_Dimensions>
1063
           <NROWS>30</NROWS>
1064
           <NCOLS>20</NCOLS>
1065
           <NBANDS>4</NBANDS>
1066
           <Tile_Set>
1067
              <NTILES>2</NTILES>
1068
              <Regular_Tiling>
1069
                 <NTILES_SIZE nrows="20" ncols="20"/>
1070
                 <NTILES_COUNT ntiles_R="2" ntiles_C="1"/>
1071
                 <OVERLAP_ROW>0</OVERLAP_ROW>
1072
                 <OVERLAP_COL>0</OVERLAP_COL>
1073
              </Regular_Tiling>
1074
           </Tile_Set>
1075
        </Raster_Dimensions>
1076
      */
1077
1078
0
    const int l_nBands =
1079
0
        atoi(CPLGetXMLValue(psImageAttributes, "NBANDS", "-1"));
1080
0
    nRasterXSize = atoi(CPLGetXMLValue(psImageAttributes, "NCOLS", "-1"));
1081
0
    nRasterYSize = atoi(CPLGetXMLValue(psImageAttributes, "NROWS", "-1"));
1082
0
    if (nRasterXSize <= 0 || nRasterYSize <= 0)
1083
0
    {
1084
0
        CPLError(CE_Failure, CPLE_AppDefined,
1085
0
                 "Invalid NCOLS(=%d)/NROWS(=%d) value", nRasterXSize,
1086
0
                 nRasterYSize);
1087
0
        return FALSE;
1088
0
    }
1089
0
    int nTileWidth = atoi(CPLGetXMLValue(
1090
0
        psImageAttributes, "Tile_Set.Regular_Tiling.NTILES_SIZE.ncols", "-1"));
1091
0
    int nTileHeight = atoi(CPLGetXMLValue(
1092
0
        psImageAttributes, "Tile_Set.Regular_Tiling.NTILES_SIZE.nrows", "-1"));
1093
0
    int nOverlapRow = atoi(CPLGetXMLValue(
1094
0
        psImageAttributes, "Tile_Set.Regular_Tiling.OVERLAP_ROW", "-1"));
1095
0
    int nOverlapCol = atoi(CPLGetXMLValue(
1096
0
        psImageAttributes, "Tile_Set.Regular_Tiling.OVERLAP_COL", "-1"));
1097
0
    const int nBits =
1098
0
        atoi(CPLGetXMLValue(psDoc, "Raster_Data.Raster_Encoding.NBITS", "-1"));
1099
0
    CPLString osDataFormat =
1100
0
        CPLGetXMLValue(psDoc, "Raster_Data.Data_Access.DATA_FILE_FORMAT", "");
1101
0
    if (osDataFormat == "image/jp2")
1102
0
    {
1103
0
        SetMetadataItem("COMPRESSION", "JPEG2000", "IMAGE_STRUCTURE");
1104
0
    }
1105
1106
    // For VHR2020: SPECTRAL_PROCESSING = PAN, MS, MS-FS, PMS, PMS-N, PMS-X,
1107
    // PMS-FS
1108
0
    const CPLString osSpectralProcessing = CPLGetXMLValue(
1109
0
        psDoc, "Processing_Information.Product_Settings.SPECTRAL_PROCESSING",
1110
0
        "");
1111
0
    const bool bTwoDataFilesPerTile =
1112
0
        osSpectralProcessing == "MS-FS" || osSpectralProcessing == "PMS-FS";
1113
1114
    /* -------------------------------------------------------------------- */
1115
    /*      Get the name of the underlying file.                            */
1116
    /* -------------------------------------------------------------------- */
1117
1118
0
    CPLXMLNode *psDataFiles =
1119
0
        CPLGetXMLNode(psDoc, "Raster_Data.Data_Access.Data_Files");
1120
1121
    /*  <Data_Files>
1122
            <Data_File tile_R="1" tile_C="1">
1123
               <DATA_FILE_PATH href="IMG_foo_R1C1.TIF"/>
1124
            </Data_File>
1125
            <Data_File tile_R="2" tile_C="1">
1126
               <DATA_FILE_PATH href="IMG_foo_R2C1.TIF"/>
1127
            </Data_File>
1128
         </Data_Files>
1129
    */
1130
1131
0
    struct TileIdx
1132
0
    {
1133
0
        int nRow;
1134
0
        int nCol;
1135
0
        int nPart;  // typically 0.  But for VHR2020 0=RGB, 1=NED
1136
1137
0
        TileIdx(int nRowIn, int nColIn, int nPartIn = 0)
1138
0
            : nRow(nRowIn), nCol(nColIn), nPart(nPartIn)
1139
0
        {
1140
0
        }
1141
1142
0
        bool operator<(const TileIdx &other) const
1143
0
        {
1144
0
            if (nRow < other.nRow)
1145
0
                return true;
1146
0
            if (nRow > other.nRow)
1147
0
                return false;
1148
0
            if (nCol < other.nCol)
1149
0
                return true;
1150
0
            if (nCol > other.nCol)
1151
0
                return false;
1152
0
            return nPart < other.nPart;
1153
0
        }
1154
0
    };
1155
1156
0
    std::map<TileIdx, CPLString> oMapTileIdxToName;
1157
0
    int nImageDSRow = 1, nImageDSCol = 1;
1158
0
    if (psDataFiles)
1159
0
    {
1160
0
        const CPLString osPath = CPLGetPathSafe(osDIMAPFilename);
1161
0
        for (int nPart = 0; psDataFiles != nullptr;
1162
0
             psDataFiles = psDataFiles->psNext, nPart++)
1163
0
        {
1164
0
            for (CPLXMLNode *psDataFile = psDataFiles->psChild; psDataFile;
1165
0
                 psDataFile = psDataFile->psNext)
1166
0
            {
1167
0
                if (psDataFile->eType == CXT_Element &&
1168
0
                    strcmp(psDataFile->pszValue, "Data_File") == 0)
1169
0
                {
1170
0
                    const char *pszR =
1171
0
                        CPLGetXMLValue(psDataFile, "tile_R", nullptr);
1172
0
                    const char *pszC =
1173
0
                        CPLGetXMLValue(psDataFile, "tile_C", nullptr);
1174
0
                    const char *pszHref = CPLGetXMLValue(
1175
0
                        psDataFile, "DATA_FILE_PATH.href", nullptr);
1176
0
                    if (pszR && pszC && pszHref)
1177
0
                    {
1178
0
                        int nRow = atoi(pszR);
1179
0
                        int nCol = atoi(pszC);
1180
0
                        if (nRow < 0 || nCol < 0)
1181
0
                        {
1182
0
                            return false;
1183
0
                        }
1184
0
                        std::string osTileFilename(
1185
0
                            CPLFormCIFilenameSafe(osPath, pszHref, nullptr));
1186
0
                        if ((nRow == 1 && nCol == 1 && nPart == 0) ||
1187
0
                            osImageDSFilename.empty())
1188
0
                        {
1189
0
                            osImageDSFilename = osTileFilename;
1190
0
                            nImageDSRow = nRow;
1191
0
                            nImageDSCol = nCol;
1192
0
                        }
1193
0
                        oMapTileIdxToName[TileIdx(nRow, nCol, nPart)] =
1194
0
                            std::move(osTileFilename);
1195
0
                    }
1196
0
                }
1197
0
            }
1198
0
        }
1199
0
        if (nOverlapRow > 0 || nOverlapCol > 0)
1200
0
        {
1201
0
            CPLError(CE_Warning, CPLE_AppDefined,
1202
0
                     "Overlap between tiles is not handled currently. "
1203
0
                     "Only taking into account top left tile");
1204
0
            oMapTileIdxToName.clear();
1205
0
            oMapTileIdxToName[TileIdx(1, 1)] = osImageDSFilename;
1206
0
        }
1207
0
    }
1208
0
    else
1209
0
    {
1210
0
        oMapTileIdxToName[TileIdx(1, 1)] = osImageDSFilename;
1211
0
    }
1212
1213
0
    if (osImageDSFilename.empty())
1214
0
    {
1215
0
        CPLError(CE_Failure, CPLE_OpenFailed,
1216
0
                 "Failed to find <DATA_FILE_PATH> in document.");
1217
0
        return FALSE;
1218
0
    }
1219
1220
    /* -------------------------------------------------------------------- */
1221
    /*      Try and open the file.                                          */
1222
    /* -------------------------------------------------------------------- */
1223
0
    auto poImageDS = std::unique_ptr<GDALDataset>(GDALDataset::Open(
1224
0
        osImageDSFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
1225
0
    if (poImageDS == nullptr)
1226
0
    {
1227
0
        return FALSE;
1228
0
    }
1229
0
    if (bTwoDataFilesPerTile)
1230
0
    {
1231
0
        if (l_nBands != 6 || poImageDS->GetRasterCount() != 3)
1232
0
        {
1233
0
            CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent band count");
1234
0
            return FALSE;
1235
0
        }
1236
0
    }
1237
0
    else if (poImageDS->GetRasterCount() != l_nBands)
1238
0
    {
1239
0
        CPLError(CE_Failure, CPLE_AppDefined, "Inconsistent band count");
1240
0
        return FALSE;
1241
0
    }
1242
1243
0
    if (nTileWidth > 0 && nTileHeight > 0)
1244
0
    {
1245
        // ok
1246
0
    }
1247
0
    else if (oMapTileIdxToName.size() == 1 ||
1248
0
             (bTwoDataFilesPerTile && oMapTileIdxToName.size() == 2))
1249
0
    {
1250
0
        nTileWidth = poImageDS->GetRasterXSize();
1251
0
        nTileHeight = poImageDS->GetRasterYSize();
1252
0
    }
1253
1254
0
    if (!(nTileWidth > 0 && nTileHeight > 0))
1255
0
    {
1256
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot get tile dimension");
1257
0
        return FALSE;
1258
0
    }
1259
1260
    /* -------------------------------------------------------------------- */
1261
    /*      Create and initialize the corresponding VRT dataset used to     */
1262
    /*      manage the tiled data access.                                   */
1263
    /* -------------------------------------------------------------------- */
1264
0
    poVRTDS = new VRTDataset(nRasterXSize, nRasterYSize);
1265
1266
    // Don't try to write a VRT file.
1267
0
    poVRTDS->SetWritable(FALSE);
1268
1269
0
    for (int iBand = 0; iBand < l_nBands; iBand++)
1270
0
    {
1271
0
        auto poSrcBandFirstImage = poImageDS->GetRasterBand(
1272
0
            iBand < poImageDS->GetRasterCount() ? iBand + 1 : 1);
1273
0
        CPLStringList aosAddBandOptions;
1274
0
        int nSrcBlockXSize, nSrcBlockYSize;
1275
0
        poSrcBandFirstImage->GetBlockSize(&nSrcBlockXSize, &nSrcBlockYSize);
1276
0
        if (oMapTileIdxToName.size() == 1 ||
1277
0
            ((nTileWidth % nSrcBlockXSize) == 0 &&
1278
0
             (nTileHeight % nSrcBlockYSize) == 0))
1279
0
        {
1280
0
            aosAddBandOptions.SetNameValue("BLOCKXSIZE",
1281
0
                                           CPLSPrintf("%d", nSrcBlockXSize));
1282
0
            aosAddBandOptions.SetNameValue("BLOCKYSIZE",
1283
0
                                           CPLSPrintf("%d", nSrcBlockYSize));
1284
0
        }
1285
0
        poVRTDS->AddBand(poSrcBandFirstImage->GetRasterDataType(),
1286
0
                         aosAddBandOptions.List());
1287
1288
0
        VRTSourcedRasterBand *poVRTBand =
1289
0
            reinterpret_cast<VRTSourcedRasterBand *>(
1290
0
                poVRTDS->GetRasterBand(iBand + 1));
1291
0
        if (nBits > 0 && nBits != 8 && nBits != 16)
1292
0
        {
1293
0
            poVRTBand->SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
1294
0
                                       "IMAGE_STRUCTURE");
1295
0
        }
1296
1297
0
        for (const auto &oTileIdxNameTuple : oMapTileIdxToName)
1298
0
        {
1299
0
            const int nRow = oTileIdxNameTuple.first.nRow;
1300
0
            const int nCol = oTileIdxNameTuple.first.nCol;
1301
0
            if (static_cast<int64_t>(nRow - 1) * nTileHeight < nRasterYSize &&
1302
0
                static_cast<int64_t>(nCol - 1) * nTileWidth < nRasterXSize)
1303
0
            {
1304
0
                int nSrcBand;
1305
0
                if (bTwoDataFilesPerTile)
1306
0
                {
1307
0
                    const int nPart = oTileIdxNameTuple.first.nPart;
1308
0
                    if (nPart == 0 && iBand < 3)
1309
0
                    {
1310
0
                        nSrcBand = iBand + 1;
1311
0
                    }
1312
0
                    else if (nPart == 1 && iBand >= 3)
1313
0
                    {
1314
0
                        nSrcBand = iBand + 1 - 3;
1315
0
                    }
1316
0
                    else
1317
0
                    {
1318
0
                        continue;
1319
0
                    }
1320
0
                }
1321
0
                else
1322
0
                {
1323
0
                    nSrcBand = iBand + 1;
1324
0
                }
1325
1326
0
                int nHeight = nTileHeight;
1327
0
                if (static_cast<int64_t>(nRow) * nTileHeight > nRasterYSize)
1328
0
                {
1329
0
                    nHeight = nRasterYSize - (nRow - 1) * nTileHeight;
1330
0
                }
1331
0
                int nWidth = nTileWidth;
1332
0
                if (static_cast<int64_t>(nCol) * nTileWidth > nRasterXSize)
1333
0
                {
1334
0
                    nWidth = nRasterXSize - (nCol - 1) * nTileWidth;
1335
0
                }
1336
1337
0
                poVRTBand->AddSimpleSource(
1338
0
                    oTileIdxNameTuple.second, nSrcBand, 0, 0, nWidth, nHeight,
1339
0
                    (nCol - 1) * nTileWidth, (nRow - 1) * nTileHeight, nWidth,
1340
0
                    nHeight);
1341
0
            }
1342
0
        }
1343
0
    }
1344
1345
    /* -------------------------------------------------------------------- */
1346
    /*      Expose Overviews if available                                   */
1347
    /* -------------------------------------------------------------------- */
1348
0
    auto poSrcBandFirstImage = poImageDS->GetRasterBand(1);
1349
0
    const int nSrcOverviews =
1350
0
        std::min(30, poSrcBandFirstImage->GetOverviewCount());
1351
0
    if (nSrcOverviews > 0)
1352
0
    {
1353
0
        CPLConfigOptionSetter oSetter("VRT_VIRTUAL_OVERVIEWS", "YES", false);
1354
0
        std::unique_ptr<int[]> ovrLevels(new int[nSrcOverviews]);
1355
0
        int iLvl = 1;
1356
0
        for (int i = 0; i < nSrcOverviews; i++)
1357
0
        {
1358
0
            iLvl *= 2;
1359
0
            ovrLevels[i] = iLvl;
1360
0
        }
1361
0
        poVRTDS->IBuildOverviews("average", nSrcOverviews, ovrLevels.get(), 0,
1362
0
                                 nullptr, nullptr, nullptr, nullptr);
1363
0
    }
1364
1365
#ifdef DEBUG_VERBOSE
1366
    CPLDebug("DIMAP", "VRT XML: %s", poVRTDS->GetMetadata("xml:VRT")[0]);
1367
#endif
1368
1369
    /* -------------------------------------------------------------------- */
1370
    /*      Create band information objects.                                */
1371
    /* -------------------------------------------------------------------- */
1372
0
    for (int iBand = 1; iBand <= poVRTDS->GetRasterCount(); iBand++)
1373
0
    {
1374
0
        GDALRasterBand *poBand = new DIMAPRasterBand(
1375
0
            this, iBand,
1376
0
            static_cast<VRTSourcedRasterBand *>(poVRTDS->GetRasterBand(iBand)));
1377
0
        if (nBits > 0 && nBits != 8 && nBits != 16)
1378
0
        {
1379
0
            poBand->SetMetadataItem("NBITS", CPLSPrintf("%d", nBits),
1380
0
                                    "IMAGE_STRUCTURE");
1381
0
        }
1382
0
        if (bTwoDataFilesPerTile)
1383
0
        {
1384
0
            switch (iBand)
1385
0
            {
1386
0
                case 1:
1387
0
                {
1388
0
                    poBand->SetColorInterpretation(GCI_RedBand);
1389
0
                    poBand->SetDescription("Red");
1390
0
                    break;
1391
0
                }
1392
0
                case 2:
1393
0
                {
1394
0
                    poBand->SetColorInterpretation(GCI_GreenBand);
1395
0
                    poBand->SetDescription("Green");
1396
0
                    break;
1397
0
                }
1398
0
                case 3:
1399
0
                {
1400
0
                    poBand->SetColorInterpretation(GCI_BlueBand);
1401
0
                    poBand->SetDescription("Blue");
1402
0
                    break;
1403
0
                }
1404
0
                case 4:
1405
0
                {
1406
0
                    poBand->SetColorInterpretation(GCI_NIRBand);
1407
0
                    poBand->SetDescription("NIR");
1408
0
                    break;
1409
0
                }
1410
0
                case 5:
1411
0
                {
1412
0
                    poBand->SetColorInterpretation(GCI_RedEdgeBand);
1413
0
                    poBand->SetDescription("Red Edge");
1414
0
                    break;
1415
0
                }
1416
0
                case 6:
1417
0
                {
1418
0
                    poBand->SetColorInterpretation(GCI_CoastalBand);
1419
0
                    poBand->SetDescription("Deep Blue");
1420
0
                    break;
1421
0
                }
1422
0
                default:
1423
0
                    break;
1424
0
            }
1425
0
        }
1426
0
        else if (l_nBands == 1 && osSpectralProcessing == "PAN")
1427
0
        {
1428
0
            poBand->SetColorInterpretation(GCI_PanBand);
1429
0
            poBand->SetDescription("Panchromatic");
1430
0
        }
1431
0
        SetBand(iBand, poBand);
1432
0
    }
1433
1434
    /* -------------------------------------------------------------------- */
1435
    /*      Try to collect simple insertion point.                          */
1436
    /* -------------------------------------------------------------------- */
1437
0
    CPLXMLNode *psGeoLoc =
1438
0
        CPLGetXMLNode(psDoc, "Geoposition.Geoposition_Insert");
1439
1440
0
    if (psGeoLoc != nullptr)
1441
0
    {
1442
0
        bHaveGeoTransform = TRUE;
1443
0
        adfGeoTransform[0] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULXMAP", "0"));
1444
0
        adfGeoTransform[1] = CPLAtof(CPLGetXMLValue(psGeoLoc, "XDIM", "0"));
1445
0
        adfGeoTransform[2] = 0.0;
1446
0
        adfGeoTransform[3] = CPLAtof(CPLGetXMLValue(psGeoLoc, "ULYMAP", "0"));
1447
0
        adfGeoTransform[4] = 0.0;
1448
0
        adfGeoTransform[5] = -CPLAtof(CPLGetXMLValue(psGeoLoc, "YDIM", "0"));
1449
0
    }
1450
0
    else
1451
0
    {
1452
        // Try to get geotransform from underlying raster,
1453
        // but make sure it is a real geotransform.
1454
0
        if (poImageDS->GetGeoTransform(adfGeoTransform) == CE_None &&
1455
0
            !(adfGeoTransform[0] <= 1.5 && fabs(adfGeoTransform[3]) <= 1.5))
1456
0
        {
1457
0
            bHaveGeoTransform = TRUE;
1458
            // fix up the origin if we did not get the geotransform from the
1459
            // top-left tile
1460
0
            adfGeoTransform[0] -=
1461
0
                (nImageDSCol - 1) * adfGeoTransform[1] * nTileWidth +
1462
0
                (nImageDSRow - 1) * adfGeoTransform[2] * nTileHeight;
1463
0
            adfGeoTransform[3] -=
1464
0
                (nImageDSCol - 1) * adfGeoTransform[4] * nTileWidth +
1465
0
                (nImageDSRow - 1) * adfGeoTransform[5] * nTileHeight;
1466
0
        }
1467
0
    }
1468
1469
    /* -------------------------------------------------------------------- */
1470
    /*      Collect the CRS.  For now we look only for EPSG codes.          */
1471
    /* -------------------------------------------------------------------- */
1472
0
    const char *pszSRS = CPLGetXMLValue(
1473
0
        psDoc, "Coordinate_Reference_System.Projected_CRS.PROJECTED_CRS_CODE",
1474
0
        nullptr);
1475
0
    if (pszSRS == nullptr)
1476
0
        pszSRS = CPLGetXMLValue(
1477
0
            psDoc, "Coordinate_Reference_System.Geodetic_CRS.GEODETIC_CRS_CODE",
1478
0
            nullptr);
1479
1480
0
    if (pszSRS != nullptr)
1481
0
    {
1482
0
        if (bHaveGeoTransform)
1483
0
        {
1484
0
            OGRSpatialReference &oSRS = m_oSRS;
1485
0
            oSRS.SetFromUserInput(
1486
0
                pszSRS,
1487
0
                OGRSpatialReference::SET_FROM_USER_INPUT_LIMITATIONS_get());
1488
0
        }
1489
0
    }
1490
0
    else
1491
0
    {
1492
        // Check underlying raster for SRS. We have cases where
1493
        // HORIZONTAL_CS_CODE is empty and the underlying raster
1494
        // is georeferenced (rprinceley).
1495
0
        const auto poSRS = poImageDS->GetSpatialRef();
1496
0
        double adfGTTmp[6];
1497
0
        if (poSRS && poImageDS->GetGeoTransform(adfGTTmp) == CE_None)
1498
0
        {
1499
0
            m_oSRS = *poSRS;
1500
0
        }
1501
0
    }
1502
1503
    /* -------------------------------------------------------------------- */
1504
    /*      Translate other metadata of interest: DIM_<product_name>.XML    */
1505
    /* -------------------------------------------------------------------- */
1506
1507
0
    static const char *const apszMetadataTranslationDim[] = {
1508
0
        "Product_Information.Delivery_Identification",
1509
0
        "DATASET_",
1510
0
        "Product_Information.Producer_Information",
1511
0
        "DATASET_",
1512
0
        "Dataset_Sources.Source_Identification.Strip_Source",
1513
0
        "",
1514
0
        "Processing_Information.Production_Facility",
1515
0
        "FACILITY_",
1516
0
        "Processing_Information.Product_Settings",
1517
0
        "",
1518
0
        "Processing_Information.Product_Settings.Geometric_Settings",
1519
0
        "GEOMETRIC_",
1520
0
        "Processing_Information.Product_Settings.Radiometric_Settings",
1521
0
        "RADIOMETRIC_",
1522
0
        "Quality_Assessment.Imaging_Quality_Measurement",
1523
0
        "CLOUDCOVER_",
1524
0
        nullptr,
1525
0
        nullptr};
1526
1527
0
    SetMetadataFromXML(psProductDim, apszMetadataTranslationDim);
1528
1529
    /* -------------------------------------------------------------------- */
1530
    /*      Translate other metadata of interest: STRIP_<product_name>.XML    */
1531
    /* -------------------------------------------------------------------- */
1532
1533
0
    static const char *const apszMetadataTranslationStrip[] = {
1534
0
        "Catalog.Full_Strip.Notations.Cloud_And_Quality_Notation."
1535
0
        "Data_Strip_Notation",
1536
0
        "CLOUDCOVER_",
1537
0
        "Acquisition_Configuration.Platform_Configuration."
1538
0
        "Ephemeris_Configuration",
1539
0
        "EPHEMERIS_",
1540
0
        nullptr,
1541
0
        nullptr};
1542
1543
0
    if (psProductStrip != nullptr)
1544
0
        SetMetadataFromXML(psProductStrip, apszMetadataTranslationStrip);
1545
1546
0
    if (!osRPCFilename.empty())
1547
0
    {
1548
0
        GDALMDReaderPleiades *poReader =
1549
0
            GDALMDReaderPleiades::CreateReaderForRPC(osRPCFilename);
1550
0
        char **papszRPC = poReader->LoadRPCXmlFile(psDoc);
1551
0
        delete poReader;
1552
0
        if (papszRPC)
1553
0
            SetMetadata(papszRPC, "RPC");
1554
0
        CSLDestroy(papszRPC);
1555
0
    }
1556
1557
0
    CPLXMLNode *psLocatedUseAreaNode =
1558
0
        CPLGetXMLNode(psDoc, "Geometric_Data.Use_Area");
1559
0
    if (psLocatedUseAreaNode != nullptr)
1560
0
    {
1561
0
        CPLXMLNode *psLocatedGeometricValuesNode =
1562
0
            psLocatedUseAreaNode->psChild;
1563
0
        while (psLocatedGeometricValuesNode != nullptr)
1564
0
        {
1565
0
            CPLXMLNode *psLocationType =
1566
0
                CPLGetXMLNode(psLocatedGeometricValuesNode, "LOCATION_TYPE");
1567
0
            if (psLocationType == nullptr ||
1568
0
                psLocationType->psChild == nullptr ||
1569
0
                !EQUAL(psLocationType->psChild->pszValue, "center"))
1570
0
            {
1571
0
                psLocatedGeometricValuesNode =
1572
0
                    psLocatedGeometricValuesNode->psNext;
1573
0
                continue;
1574
0
            }
1575
0
            static const char *const apszLGVTranslationDim[] = {
1576
0
                "SATELLITE_ALTITUDE",
1577
0
                "",
1578
0
                "Acquisition_Angles",
1579
0
                "",
1580
0
                "Solar_Incidences",
1581
0
                "",
1582
0
                "Ground_Sample_Distance",
1583
0
                "",
1584
0
                nullptr,
1585
0
                nullptr};
1586
1587
0
            SetMetadataFromXML(psLocatedGeometricValuesNode,
1588
0
                               apszLGVTranslationDim, false);
1589
0
            break;
1590
0
        }
1591
0
    }
1592
1593
    /* -------------------------------------------------------------------- */
1594
    /*      Set Band metadata from the <Band_Radiance> and                  */
1595
    /*                                <Band_Spectral_Range> content         */
1596
    /* -------------------------------------------------------------------- */
1597
1598
0
    CPLXMLNode *psImageInterpretationNode = CPLGetXMLNode(
1599
0
        psDoc,
1600
0
        "Radiometric_Data.Radiometric_Calibration.Instrument_Calibration."
1601
0
        "Band_Measurement_List");
1602
0
    if (psImageInterpretationNode != nullptr)
1603
0
    {
1604
0
        CPLXMLNode *psSpectralBandInfoNode = psImageInterpretationNode->psChild;
1605
0
        while (psSpectralBandInfoNode != nullptr)
1606
0
        {
1607
0
            if (psSpectralBandInfoNode->eType == CXT_Element &&
1608
0
                (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance") ||
1609
0
                 EQUAL(psSpectralBandInfoNode->pszValue,
1610
0
                       "Band_Spectral_Range") ||
1611
0
                 EQUAL(psSpectralBandInfoNode->pszValue,
1612
0
                       "Band_Solar_Irradiance")))
1613
0
            {
1614
0
                CPLString osName;
1615
1616
0
                if (EQUAL(psSpectralBandInfoNode->pszValue, "Band_Radiance"))
1617
0
                    osName = "RADIANCE_";
1618
0
                else if (EQUAL(psSpectralBandInfoNode->pszValue,
1619
0
                               "Band_Spectral_Range"))
1620
0
                    osName = "SPECTRAL_RANGE_";
1621
0
                else if (EQUAL(psSpectralBandInfoNode->pszValue,
1622
0
                               "Band_Solar_Irradiance"))
1623
0
                    osName = "SOLAR_IRRADIANCE_";
1624
1625
0
                CPLXMLNode *psTag = psSpectralBandInfoNode->psChild;
1626
0
                int nBandIndex = 0;
1627
0
                while (psTag != nullptr)
1628
0
                {
1629
0
                    if (psTag->eType == CXT_Element &&
1630
0
                        psTag->psChild != nullptr &&
1631
0
                        psTag->pszValue != nullptr &&
1632
0
                        (psTag->psChild->eType == CXT_Text ||
1633
0
                         EQUAL(psTag->pszValue, "FWHM")))
1634
0
                    {
1635
0
                        if (EQUAL(psTag->pszValue, "BAND_ID"))
1636
0
                        {
1637
0
                            nBandIndex = 0;
1638
0
                            if (EQUAL(psTag->psChild->pszValue, "P") ||
1639
0
                                EQUAL(psTag->psChild->pszValue, "PAN") ||
1640
0
                                EQUAL(psTag->psChild->pszValue, "B0") ||
1641
0
                                EQUAL(psTag->psChild->pszValue, "R"))
1642
0
                                nBandIndex = 1;
1643
0
                            else if (EQUAL(psTag->psChild->pszValue, "B1") ||
1644
0
                                     EQUAL(psTag->psChild->pszValue, "G"))
1645
0
                                nBandIndex = 2;
1646
0
                            else if (EQUAL(psTag->psChild->pszValue, "B2") ||
1647
0
                                     EQUAL(psTag->psChild->pszValue, "B"))
1648
0
                                nBandIndex = 3;
1649
0
                            else if (EQUAL(psTag->psChild->pszValue, "B3") ||
1650
0
                                     EQUAL(psTag->psChild->pszValue, "NIR"))
1651
0
                                nBandIndex = 4;
1652
0
                            else if (EQUAL(psTag->psChild->pszValue, "RE"))
1653
0
                                nBandIndex = 5;
1654
0
                            else if (EQUAL(psTag->psChild->pszValue, "DB"))
1655
0
                                nBandIndex = 6;
1656
1657
0
                            if (nBandIndex <= 0 ||
1658
0
                                nBandIndex > GetRasterCount())
1659
0
                            {
1660
0
                                CPLError(CE_Warning, CPLE_AppDefined,
1661
0
                                         "Bad BAND_ID value : %s",
1662
0
                                         psTag->psChild->pszValue);
1663
0
                                nBandIndex = 0;
1664
0
                            }
1665
0
                        }
1666
0
                        else if (nBandIndex >= 1)
1667
0
                        {
1668
0
                            CPLString osMDName = osName;
1669
0
                            osMDName += psTag->pszValue;
1670
1671
0
                            auto poBand = GetRasterBand(nBandIndex);
1672
0
                            if (EQUAL(psTag->pszValue, "FWHM"))
1673
0
                            {
1674
0
                                if (const char *pszMIN =
1675
0
                                        CPLGetXMLValue(psTag, "MIN", nullptr))
1676
0
                                    poBand->SetMetadataItem(
1677
0
                                        (osMDName + "_MIN").c_str(), pszMIN);
1678
0
                                if (const char *pszMAX =
1679
0
                                        CPLGetXMLValue(psTag, "MAX", nullptr))
1680
0
                                    poBand->SetMetadataItem(
1681
0
                                        (osMDName + "_MAX").c_str(), pszMAX);
1682
0
                            }
1683
0
                            else
1684
0
                            {
1685
0
                                poBand->SetMetadataItem(
1686
0
                                    osMDName, psTag->psChild->pszValue);
1687
0
                            }
1688
0
                        }
1689
0
                    }
1690
0
                    psTag = psTag->psNext;
1691
0
                }
1692
0
            }
1693
0
            psSpectralBandInfoNode = psSpectralBandInfoNode->psNext;
1694
0
        }
1695
0
    }
1696
1697
    // Fill raster band IMAGERY metadata domain from FWHM metadata.
1698
0
    for (int i = 1; i <= nBands; ++i)
1699
0
    {
1700
0
        auto poBand = GetRasterBand(i);
1701
0
        const char *SPECTRAL_RANGE_MEASURE_UNIT =
1702
0
            poBand->GetMetadataItem("SPECTRAL_RANGE_MEASURE_UNIT");
1703
0
        const char *SPECTRAL_RANGE_FWHM_MIN =
1704
0
            poBand->GetMetadataItem("SPECTRAL_RANGE_FWHM_MIN");
1705
0
        const char *SPECTRAL_RANGE_FWHM_MAX =
1706
0
            poBand->GetMetadataItem("SPECTRAL_RANGE_FWHM_MAX");
1707
0
        if (SPECTRAL_RANGE_MEASURE_UNIT && SPECTRAL_RANGE_FWHM_MIN &&
1708
0
            SPECTRAL_RANGE_FWHM_MAX &&
1709
0
            (EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "nanometer") ||
1710
0
             EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "micrometer")))
1711
0
        {
1712
0
            const double dfFactorToMicrometer =
1713
0
                EQUAL(SPECTRAL_RANGE_MEASURE_UNIT, "nanometer") ? 1e-3 : 1.0;
1714
0
            const double dfMin =
1715
0
                CPLAtof(SPECTRAL_RANGE_FWHM_MIN) * dfFactorToMicrometer;
1716
0
            const double dfMax =
1717
0
                CPLAtof(SPECTRAL_RANGE_FWHM_MAX) * dfFactorToMicrometer;
1718
0
            poBand->SetMetadataItem("CENTRAL_WAVELENGTH_UM",
1719
0
                                    CPLSPrintf("%.3f", (dfMin + dfMax) / 2),
1720
0
                                    "IMAGERY");
1721
0
            poBand->SetMetadataItem(
1722
0
                "FWHM_UM", CPLSPrintf("%.3f", dfMax - dfMin), "IMAGERY");
1723
0
        }
1724
0
    }
1725
1726
    /* -------------------------------------------------------------------- */
1727
    /*      Initialize any PAM information.                                 */
1728
    /* -------------------------------------------------------------------- */
1729
0
    SetDescription(osMDFilename);
1730
0
    TryLoadXML();
1731
1732
    /* -------------------------------------------------------------------- */
1733
    /*      Check for overviews.                                            */
1734
    /* -------------------------------------------------------------------- */
1735
0
    oOvManager.Initialize(this, osMDFilename);
1736
1737
0
    return TRUE;
1738
0
}
1739
1740
/************************************************************************/
1741
/*                          SetMetadataFromXML()                        */
1742
/************************************************************************/
1743
1744
void DIMAPDataset::SetMetadataFromXML(
1745
    CPLXMLNode *psProductIn, const char *const apszMetadataTranslation[],
1746
    bool bKeysFromRoot)
1747
0
{
1748
0
    CPLXMLNode *psDoc = psProductIn;
1749
0
    if (bKeysFromRoot)
1750
0
    {
1751
0
        psDoc = CPLGetXMLNode(psProductIn, "=Dimap_Document");
1752
0
        if (psDoc == nullptr)
1753
0
        {
1754
0
            psDoc = CPLGetXMLNode(psProductIn, "=PHR_DIMAP_Document");
1755
0
        }
1756
0
    }
1757
1758
0
    bool bWarnedDiscarding = false;
1759
1760
0
    for (int iTrItem = 0; apszMetadataTranslation[iTrItem] != nullptr;
1761
0
         iTrItem += 2)
1762
0
    {
1763
0
        CPLXMLNode *psParent =
1764
0
            CPLGetXMLNode(psDoc, apszMetadataTranslation[iTrItem]);
1765
1766
0
        if (psParent == nullptr)
1767
0
            continue;
1768
1769
        // Logic to support directly access a name/value entry
1770
0
        if (psParent->psChild != nullptr &&
1771
0
            psParent->psChild->eType == CXT_Text)
1772
0
        {
1773
0
            CPLString osName = apszMetadataTranslation[iTrItem + 1];
1774
0
            osName += apszMetadataTranslation[iTrItem];
1775
            // Limit size to avoid perf issues when inserting
1776
            // in metadata list
1777
0
            if (osName.size() < 128)
1778
0
                SetMetadataItem(osName, psParent->psChild->pszValue);
1779
0
            else if (!bWarnedDiscarding)
1780
0
            {
1781
0
                bWarnedDiscarding = true;
1782
0
                CPLDebug("DIMAP", "Discarding too long metadata item");
1783
0
            }
1784
0
            continue;
1785
0
        }
1786
1787
        // Logic to support a parent element with many name/values.
1788
0
        CPLXMLNode *psTarget = psParent->psChild;
1789
0
        for (; psTarget != nullptr && psTarget != psParent;
1790
0
             psTarget = psTarget->psNext)
1791
0
        {
1792
0
            if (psTarget->eType == CXT_Element && psTarget->psChild != nullptr)
1793
0
            {
1794
0
                CPLString osName = apszMetadataTranslation[iTrItem + 1];
1795
1796
0
                if (psTarget->psChild->eType == CXT_Text)
1797
0
                {
1798
0
                    osName += psTarget->pszValue;
1799
                    // Limit size to avoid perf issues when inserting
1800
                    // in metadata list
1801
0
                    if (osName.size() < 128)
1802
0
                        SetMetadataItem(osName, psTarget->psChild->pszValue);
1803
0
                    else if (!bWarnedDiscarding)
1804
0
                    {
1805
0
                        bWarnedDiscarding = true;
1806
0
                        CPLDebug("DIMAP", "Discarding too long metadata item");
1807
0
                    }
1808
0
                }
1809
0
                else if (psTarget->psChild->eType == CXT_Attribute)
1810
0
                {
1811
                    // find the tag value, at the end of the attributes.
1812
0
                    for (CPLXMLNode *psNode = psTarget->psChild;
1813
0
                         psNode != nullptr; psNode = psNode->psNext)
1814
0
                    {
1815
0
                        if (psNode->eType == CXT_Attribute)
1816
0
                            continue;
1817
0
                        else if (psNode->eType == CXT_Text)
1818
0
                        {
1819
0
                            osName += psTarget->pszValue;
1820
                            // Limit size to avoid perf issues when inserting
1821
                            // in metadata list
1822
0
                            if (osName.size() < 128)
1823
0
                                SetMetadataItem(osName, psNode->pszValue);
1824
0
                            else if (!bWarnedDiscarding)
1825
0
                            {
1826
0
                                bWarnedDiscarding = true;
1827
0
                                CPLDebug("DIMAP",
1828
0
                                         "Discarding too long metadata item");
1829
0
                            }
1830
0
                        }
1831
0
                    }
1832
0
                }
1833
0
            }
1834
0
        }
1835
0
    }
1836
0
}
1837
1838
/************************************************************************/
1839
/*                            GetGCPCount()                             */
1840
/************************************************************************/
1841
1842
int DIMAPDataset::GetGCPCount()
1843
1844
0
{
1845
0
    return nGCPCount;
1846
0
}
1847
1848
/************************************************************************/
1849
/*                          GetGCPSpatialRef()                          */
1850
/************************************************************************/
1851
1852
const OGRSpatialReference *DIMAPDataset::GetGCPSpatialRef() const
1853
1854
0
{
1855
0
    return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
1856
0
}
1857
1858
/************************************************************************/
1859
/*                               GetGCPs()                              */
1860
/************************************************************************/
1861
1862
const GDAL_GCP *DIMAPDataset::GetGCPs()
1863
1864
0
{
1865
0
    return pasGCPList;
1866
0
}
1867
1868
/************************************************************************/
1869
/*                         GDALRegister_DIMAP()                         */
1870
/************************************************************************/
1871
1872
void GDALRegister_DIMAP()
1873
1874
2
{
1875
2
    if (GDALGetDriverByName("DIMAP") != nullptr)
1876
0
        return;
1877
1878
2
    GDALDriver *poDriver = new GDALDriver();
1879
1880
2
    poDriver->SetDescription("DIMAP");
1881
2
    poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
1882
2
    poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "SPOT DIMAP");
1883
2
    poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/dimap.html");
1884
2
    poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
1885
2
    poDriver->SetMetadataItem(GDAL_DMD_SUBDATASETS, "YES");
1886
1887
2
    poDriver->pfnOpen = DIMAPDataset::Open;
1888
2
    poDriver->pfnIdentify = DIMAPDataset::Identify;
1889
1890
2
    GetGDALDriverManager()->RegisterDriver(poDriver);
1891
2
}