/src/gdal/frmts/til/tildataset.cpp
Line | Count | Source |
1 | | /****************************************************************************** |
2 | | * |
3 | | * Project: EarthWatch .TIL Driver |
4 | | * Purpose: Implementation of the TILDataset class. |
5 | | * Author: Frank Warmerdam, warmerdam@pobox.com |
6 | | * |
7 | | ****************************************************************************** |
8 | | * Copyright (c) 2009, Frank Warmerdam |
9 | | * Copyright (c) 2009-2011, Even Rouault <even dot rouault at spatialys.com> |
10 | | * |
11 | | * SPDX-License-Identifier: MIT |
12 | | ****************************************************************************/ |
13 | | |
14 | | #include "cpl_multiproc.h" |
15 | | #include "cpl_string.h" |
16 | | #include "cplkeywordparser.h" |
17 | | #include "gdal_mdreader.h" |
18 | | #include "gdal_frmts.h" |
19 | | #include "gdal_pam.h" |
20 | | #include "ogr_spatialref.h" |
21 | | #include "vrtdataset.h" |
22 | | |
23 | | /************************************************************************/ |
24 | | /* ==================================================================== */ |
25 | | /* TILDataset */ |
26 | | /* ==================================================================== */ |
27 | | /************************************************************************/ |
28 | | |
29 | | class TILDataset final : public GDALPamDataset |
30 | | { |
31 | | VRTDataset *poVRTDS; |
32 | | std::vector<std::string> m_aosFilenames; |
33 | | |
34 | | char **papszMetadataFiles; |
35 | | |
36 | | protected: |
37 | | int CloseDependentDatasets() override; |
38 | | |
39 | | public: |
40 | | TILDataset(); |
41 | | ~TILDataset() override; |
42 | | |
43 | | char **GetFileList(void) override; |
44 | | |
45 | | static GDALDataset *Open(GDALOpenInfo *); |
46 | | static int Identify(GDALOpenInfo *poOpenInfo); |
47 | | }; |
48 | | |
49 | | /************************************************************************/ |
50 | | /* ==================================================================== */ |
51 | | /* TILRasterBand */ |
52 | | /* ==================================================================== */ |
53 | | /************************************************************************/ |
54 | | |
55 | | class TILRasterBand final : public GDALPamRasterBand |
56 | | { |
57 | | friend class TILDataset; |
58 | | |
59 | | VRTSourcedRasterBand *poVRTBand; |
60 | | |
61 | | public: |
62 | | TILRasterBand(TILDataset *, int, VRTSourcedRasterBand *); |
63 | | |
64 | | CPLErr IReadBlock(int, int, void *) override; |
65 | | CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int, |
66 | | GDALDataType, GSpacing nPixelSpace, GSpacing nLineSpace, |
67 | | GDALRasterIOExtraArg *psExtraArg) override; |
68 | | }; |
69 | | |
70 | | /************************************************************************/ |
71 | | /* TILRasterBand() */ |
72 | | /************************************************************************/ |
73 | | |
74 | | TILRasterBand::TILRasterBand(TILDataset *poTILDS, int nBandIn, |
75 | | VRTSourcedRasterBand *poVRTBandIn) |
76 | | |
77 | 0 | { |
78 | 0 | poDS = poTILDS; |
79 | 0 | poVRTBand = poVRTBandIn; |
80 | 0 | nBand = nBandIn; |
81 | 0 | eDataType = poVRTBandIn->GetRasterDataType(); |
82 | |
|
83 | 0 | poVRTBandIn->GetBlockSize(&nBlockXSize, &nBlockYSize); |
84 | 0 | } |
85 | | |
86 | | /************************************************************************/ |
87 | | /* IReadBlock() */ |
88 | | /************************************************************************/ |
89 | | |
90 | | CPLErr TILRasterBand::IReadBlock(int iBlockX, int iBlockY, void *pBuffer) |
91 | | |
92 | 0 | { |
93 | 0 | return poVRTBand->ReadBlock(iBlockX, iBlockY, pBuffer); |
94 | 0 | } |
95 | | |
96 | | /************************************************************************/ |
97 | | /* IRasterIO() */ |
98 | | /************************************************************************/ |
99 | | |
100 | | CPLErr TILRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff, |
101 | | int nXSize, int nYSize, void *pData, |
102 | | int nBufXSize, int nBufYSize, |
103 | | GDALDataType eBufType, GSpacing nPixelSpace, |
104 | | GSpacing nLineSpace, |
105 | | GDALRasterIOExtraArg *psExtraArg) |
106 | | |
107 | 0 | { |
108 | 0 | if (GetOverviewCount() > 0) |
109 | 0 | { |
110 | 0 | return GDALPamRasterBand::IRasterIO( |
111 | 0 | eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize, |
112 | 0 | eBufType, nPixelSpace, nLineSpace, psExtraArg); |
113 | 0 | } |
114 | | |
115 | | // If not exist TIL overviews, try to use band source overviews. |
116 | 0 | return poVRTBand->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, |
117 | 0 | nBufXSize, nBufYSize, eBufType, nPixelSpace, |
118 | 0 | nLineSpace, psExtraArg); |
119 | 0 | } |
120 | | |
121 | | /************************************************************************/ |
122 | | /* ==================================================================== */ |
123 | | /* TILDataset */ |
124 | | /* ==================================================================== */ |
125 | | /************************************************************************/ |
126 | | |
127 | | /************************************************************************/ |
128 | | /* TILDataset() */ |
129 | | /************************************************************************/ |
130 | | |
131 | 0 | TILDataset::TILDataset() : poVRTDS(nullptr), papszMetadataFiles(nullptr) |
132 | 0 | { |
133 | 0 | } |
134 | | |
135 | | /************************************************************************/ |
136 | | /* ~TILDataset() */ |
137 | | /************************************************************************/ |
138 | | |
139 | | TILDataset::~TILDataset() |
140 | | |
141 | 0 | { |
142 | 0 | TILDataset::CloseDependentDatasets(); |
143 | 0 | CSLDestroy(papszMetadataFiles); |
144 | 0 | } |
145 | | |
146 | | /************************************************************************/ |
147 | | /* CloseDependentDatasets() */ |
148 | | /************************************************************************/ |
149 | | |
150 | | int TILDataset::CloseDependentDatasets() |
151 | 0 | { |
152 | 0 | int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets(); |
153 | |
|
154 | 0 | if (poVRTDS) |
155 | 0 | { |
156 | 0 | bHasDroppedRef = TRUE; |
157 | 0 | delete poVRTDS; |
158 | 0 | poVRTDS = nullptr; |
159 | 0 | } |
160 | |
|
161 | 0 | return bHasDroppedRef; |
162 | 0 | } |
163 | | |
164 | | /************************************************************************/ |
165 | | /* Identify() */ |
166 | | /************************************************************************/ |
167 | | |
168 | | int TILDataset::Identify(GDALOpenInfo *poOpenInfo) |
169 | | |
170 | 558k | { |
171 | 558k | if (poOpenInfo->nHeaderBytes < 200 || |
172 | 103k | !poOpenInfo->IsExtensionEqualToCI("TIL")) |
173 | 558k | return FALSE; |
174 | | |
175 | 1 | if (strstr((const char *)poOpenInfo->pabyHeader, "numTiles") == nullptr) |
176 | 1 | return FALSE; |
177 | | |
178 | 0 | return TRUE; |
179 | 1 | } |
180 | | |
181 | | /************************************************************************/ |
182 | | /* Open() */ |
183 | | /************************************************************************/ |
184 | | |
185 | | GDALDataset *TILDataset::Open(GDALOpenInfo *poOpenInfo) |
186 | | |
187 | 0 | { |
188 | 0 | if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr) |
189 | 0 | return nullptr; |
190 | | |
191 | | /* -------------------------------------------------------------------- */ |
192 | | /* Confirm the requested access is supported. */ |
193 | | /* -------------------------------------------------------------------- */ |
194 | 0 | if (poOpenInfo->eAccess == GA_Update) |
195 | 0 | { |
196 | 0 | ReportUpdateNotSupportedByDriver("TIL"); |
197 | 0 | return nullptr; |
198 | 0 | } |
199 | | |
200 | 0 | CPLString osDirname = CPLGetDirnameSafe(poOpenInfo->pszFilename); |
201 | | |
202 | | // get metadata reader |
203 | |
|
204 | 0 | GDALMDReaderManager mdreadermanager; |
205 | 0 | GDALMDReaderBase *mdreader = mdreadermanager.GetReader( |
206 | 0 | poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles(), MDR_DG); |
207 | |
|
208 | 0 | if (nullptr == mdreader) |
209 | 0 | { |
210 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
211 | 0 | "Unable to open .TIL dataset due to missing metadata file."); |
212 | 0 | return nullptr; |
213 | 0 | } |
214 | | /* -------------------------------------------------------------------- */ |
215 | | /* Try to find the corresponding .IMD file. */ |
216 | | /* -------------------------------------------------------------------- */ |
217 | 0 | char **papszIMD = mdreader->GetMetadataDomain(GDAL_MDD_IMD); |
218 | |
|
219 | 0 | if (papszIMD == nullptr) |
220 | 0 | { |
221 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
222 | 0 | "Unable to open .TIL dataset due to missing .IMD file."); |
223 | 0 | return nullptr; |
224 | 0 | } |
225 | | |
226 | 0 | if (CSLFetchNameValue(papszIMD, "numRows") == nullptr || |
227 | 0 | CSLFetchNameValue(papszIMD, "numColumns") == nullptr || |
228 | 0 | CSLFetchNameValue(papszIMD, "bitsPerPixel") == nullptr) |
229 | 0 | { |
230 | 0 | CPLError(CE_Failure, CPLE_OpenFailed, |
231 | 0 | "Missing a required field in the .IMD file."); |
232 | 0 | return nullptr; |
233 | 0 | } |
234 | | |
235 | | /* -------------------------------------------------------------------- */ |
236 | | /* Try to load and parse the .TIL file. */ |
237 | | /* -------------------------------------------------------------------- */ |
238 | 0 | VSILFILE *fp = poOpenInfo->fpL; |
239 | 0 | poOpenInfo->fpL = nullptr; |
240 | |
|
241 | 0 | CPLKeywordParser oParser; |
242 | |
|
243 | 0 | if (!oParser.Ingest(fp)) |
244 | 0 | { |
245 | 0 | VSIFCloseL(fp); |
246 | 0 | return nullptr; |
247 | 0 | } |
248 | | |
249 | 0 | VSIFCloseL(fp); |
250 | |
|
251 | 0 | char **papszTIL = oParser.GetAllKeywords(); |
252 | | |
253 | | /* -------------------------------------------------------------------- */ |
254 | | /* Create a corresponding GDALDataset. */ |
255 | | /* -------------------------------------------------------------------- */ |
256 | 0 | auto poDS = std::make_unique<TILDataset>(); |
257 | 0 | poDS->papszMetadataFiles = mdreader->GetMetadataFiles(); |
258 | 0 | mdreader->FillMetadata(&poDS->oMDMD); |
259 | 0 | poDS->nRasterXSize = |
260 | 0 | atoi(CSLFetchNameValueDef(papszIMD, "numColumns", "0")); |
261 | 0 | poDS->nRasterYSize = atoi(CSLFetchNameValueDef(papszIMD, "numRows", "0")); |
262 | 0 | if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize)) |
263 | 0 | { |
264 | 0 | return nullptr; |
265 | 0 | } |
266 | | |
267 | | /* -------------------------------------------------------------------- */ |
268 | | /* We need to open one of the images in order to establish */ |
269 | | /* details like the band count and types. */ |
270 | | /* -------------------------------------------------------------------- */ |
271 | 0 | const char *pszFilename = CSLFetchNameValue(papszTIL, "TILE_1.filename"); |
272 | 0 | if (pszFilename == nullptr) |
273 | 0 | { |
274 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
275 | 0 | "Missing TILE_1.filename in .TIL file."); |
276 | 0 | return nullptr; |
277 | 0 | } |
278 | | |
279 | | // trim double quotes. |
280 | 0 | if (pszFilename[0] == '"') |
281 | 0 | pszFilename++; |
282 | 0 | if (pszFilename[strlen(pszFilename) - 1] == '"') |
283 | 0 | const_cast<char *>(pszFilename)[strlen(pszFilename) - 1] = '\0'; |
284 | 0 | if (CPLHasPathTraversal(pszFilename)) |
285 | 0 | { |
286 | 0 | CPLError(CE_Failure, CPLE_NotSupported, "Path traversal detected in %s", |
287 | 0 | pszFilename); |
288 | 0 | return nullptr; |
289 | 0 | } |
290 | | |
291 | 0 | CPLString osFilename = CPLFormFilenameSafe(osDirname, pszFilename, nullptr); |
292 | 0 | auto poTemplateDS = std::unique_ptr<GDALDataset>( |
293 | 0 | GDALDataset::Open(osFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR)); |
294 | 0 | if (poTemplateDS == nullptr || poTemplateDS->GetRasterCount() == 0) |
295 | 0 | { |
296 | 0 | return nullptr; |
297 | 0 | } |
298 | | |
299 | 0 | GDALRasterBand *poTemplateBand = poTemplateDS->GetRasterBand(1); |
300 | 0 | const GDALDataType eDT = poTemplateBand->GetRasterDataType(); |
301 | 0 | const int nBandCount = poTemplateDS->GetRasterCount(); |
302 | | |
303 | | // we suppose the first tile have the same projection as others (usually so) |
304 | 0 | CPLString pszProjection(poTemplateDS->GetProjectionRef()); |
305 | 0 | if (!pszProjection.empty()) |
306 | 0 | poDS->SetProjection(pszProjection); |
307 | | |
308 | | // we suppose the first tile have the same GeoTransform as others (usually |
309 | | // so) |
310 | 0 | GDALGeoTransform gt; |
311 | 0 | if (poTemplateDS->GetGeoTransform(gt) == CE_None) |
312 | 0 | { |
313 | | // According to |
314 | | // https://www.digitalglobe.com/sites/default/files/ISD_External.pdf, |
315 | | // ulx=originX and is "Easting of the center of the upper left pixel of |
316 | | // the image." |
317 | 0 | gt.xorig = CPLAtof(CSLFetchNameValueDef( |
318 | 0 | papszIMD, "MAP_PROJECTED_PRODUCT.ULX", "0")) - |
319 | 0 | gt.xscale / 2; |
320 | 0 | gt.yorig = CPLAtof(CSLFetchNameValueDef( |
321 | 0 | papszIMD, "MAP_PROJECTED_PRODUCT.ULY", "0")) - |
322 | 0 | gt.yscale / 2; |
323 | 0 | poDS->SetGeoTransform(gt); |
324 | 0 | } |
325 | |
|
326 | 0 | poTemplateBand = nullptr; |
327 | 0 | poTemplateDS.reset(); |
328 | | |
329 | | /* -------------------------------------------------------------------- */ |
330 | | /* Create and initialize the corresponding VRT dataset used to */ |
331 | | /* manage the tiled data access. */ |
332 | | /* -------------------------------------------------------------------- */ |
333 | 0 | poDS->poVRTDS = new VRTDataset(poDS->nRasterXSize, poDS->nRasterYSize); |
334 | |
|
335 | 0 | for (int iBand = 0; iBand < nBandCount; iBand++) |
336 | 0 | poDS->poVRTDS->AddBand(eDT, nullptr); |
337 | | |
338 | | /* Don't try to write a VRT file */ |
339 | 0 | poDS->poVRTDS->SetWritable(FALSE); |
340 | | |
341 | | /* -------------------------------------------------------------------- */ |
342 | | /* Create band information objects. */ |
343 | | /* -------------------------------------------------------------------- */ |
344 | 0 | for (int iBand = 1; iBand <= nBandCount; iBand++) |
345 | 0 | poDS->SetBand( |
346 | 0 | iBand, new TILRasterBand(poDS.get(), iBand, |
347 | 0 | reinterpret_cast<VRTSourcedRasterBand *>( |
348 | 0 | poDS->poVRTDS->GetRasterBand(iBand)))); |
349 | | |
350 | | /* -------------------------------------------------------------------- */ |
351 | | /* Add tiles as sources for each band. */ |
352 | | /* -------------------------------------------------------------------- */ |
353 | 0 | const int nTileCount = |
354 | 0 | atoi(CSLFetchNameValueDef(papszTIL, "numTiles", "0")); |
355 | 0 | int iTile = 0; |
356 | |
|
357 | 0 | for (iTile = 1; iTile <= nTileCount; iTile++) |
358 | 0 | { |
359 | 0 | CPLString osKey; |
360 | 0 | osKey.Printf("TILE_%d.filename", iTile); |
361 | 0 | pszFilename = CSLFetchNameValue(papszTIL, osKey); |
362 | 0 | if (pszFilename == nullptr) |
363 | 0 | { |
364 | 0 | CPLError(CE_Failure, CPLE_AppDefined, |
365 | 0 | "Missing TILE_%d.filename in .TIL file.", iTile); |
366 | 0 | return nullptr; |
367 | 0 | } |
368 | | |
369 | | // trim double quotes. |
370 | 0 | if (pszFilename[0] == '"') |
371 | 0 | pszFilename++; |
372 | 0 | if (pszFilename[strlen(pszFilename) - 1] == '"') |
373 | 0 | const_cast<char *>(pszFilename)[strlen(pszFilename) - 1] = '\0'; |
374 | 0 | if (CPLHasPathTraversal(pszFilename)) |
375 | 0 | { |
376 | 0 | CPLError(CE_Failure, CPLE_NotSupported, |
377 | 0 | "Path traversal detected in %s", pszFilename); |
378 | 0 | return nullptr; |
379 | 0 | } |
380 | | |
381 | 0 | osFilename = CPLFormFilenameSafe(osDirname, pszFilename, nullptr); |
382 | 0 | poDS->m_aosFilenames.push_back(osFilename); |
383 | |
|
384 | 0 | osKey.Printf("TILE_%d.ULColOffset", iTile); |
385 | 0 | const int nULX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0")); |
386 | |
|
387 | 0 | osKey.Printf("TILE_%d.ULRowOffset", iTile); |
388 | 0 | const int nULY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0")); |
389 | |
|
390 | 0 | osKey.Printf("TILE_%d.LRColOffset", iTile); |
391 | 0 | const int nLRX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0")); |
392 | |
|
393 | 0 | osKey.Printf("TILE_%d.LRRowOffset", iTile); |
394 | 0 | const int nLRY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0")); |
395 | |
|
396 | 0 | for (int iBand = 1; iBand <= nBandCount; iBand++) |
397 | 0 | { |
398 | 0 | VRTSourcedRasterBand *poVRTBand = |
399 | 0 | reinterpret_cast<VRTSourcedRasterBand *>( |
400 | 0 | poDS->poVRTDS->GetRasterBand(iBand)); |
401 | |
|
402 | 0 | poVRTBand->AddSimpleSource(osFilename, iBand, 0, 0, nLRX - nULX + 1, |
403 | 0 | nLRY - nULY + 1, nULX, nULY, |
404 | 0 | nLRX - nULX + 1, nLRY - nULY + 1); |
405 | 0 | } |
406 | 0 | } |
407 | | |
408 | | /* -------------------------------------------------------------------- */ |
409 | | /* Initialize any PAM information. */ |
410 | | /* -------------------------------------------------------------------- */ |
411 | 0 | poDS->SetDescription(poOpenInfo->pszFilename); |
412 | 0 | poDS->TryLoadXML(); |
413 | | |
414 | | /* -------------------------------------------------------------------- */ |
415 | | /* Check for overviews. */ |
416 | | /* -------------------------------------------------------------------- */ |
417 | 0 | poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename); |
418 | |
|
419 | 0 | return poDS.release(); |
420 | 0 | } |
421 | | |
422 | | /************************************************************************/ |
423 | | /* GetFileList() */ |
424 | | /************************************************************************/ |
425 | | |
426 | | char **TILDataset::GetFileList() |
427 | | |
428 | 0 | { |
429 | 0 | char **papszFileList = GDALPamDataset::GetFileList(); |
430 | |
|
431 | 0 | for (const auto &osFilename : m_aosFilenames) |
432 | 0 | papszFileList = CSLAddString(papszFileList, osFilename.c_str()); |
433 | |
|
434 | 0 | if (nullptr != papszMetadataFiles) |
435 | 0 | { |
436 | 0 | for (int i = 0; papszMetadataFiles[i] != nullptr; i++) |
437 | 0 | { |
438 | 0 | papszFileList = CSLAddString(papszFileList, papszMetadataFiles[i]); |
439 | 0 | } |
440 | 0 | } |
441 | |
|
442 | 0 | return papszFileList; |
443 | 0 | } |
444 | | |
445 | | /************************************************************************/ |
446 | | /* GDALRegister_TIL() */ |
447 | | /************************************************************************/ |
448 | | |
449 | | void GDALRegister_TIL() |
450 | | |
451 | 22 | { |
452 | 22 | if (GDALGetDriverByName("TIL") != nullptr) |
453 | 0 | return; |
454 | | |
455 | 22 | GDALDriver *poDriver = new GDALDriver(); |
456 | | |
457 | 22 | poDriver->SetDescription("TIL"); |
458 | 22 | poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES"); |
459 | 22 | poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "EarthWatch .TIL"); |
460 | 22 | poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/til.html"); |
461 | 22 | poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES"); |
462 | | |
463 | 22 | poDriver->pfnOpen = TILDataset::Open; |
464 | 22 | poDriver->pfnIdentify = TILDataset::Identify; |
465 | | |
466 | 22 | GetGDALDriverManager()->RegisterDriver(poDriver); |
467 | 22 | } |