/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 | } |