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