Line data Source code
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 5 : TILRasterBand::TILRasterBand(TILDataset *poTILDS, int nBandIn,
75 5 : VRTSourcedRasterBand *poVRTBandIn)
76 :
77 : {
78 5 : poDS = poTILDS;
79 5 : poVRTBand = poVRTBandIn;
80 5 : nBand = nBandIn;
81 5 : eDataType = poVRTBandIn->GetRasterDataType();
82 :
83 5 : poVRTBandIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
84 5 : }
85 :
86 : /************************************************************************/
87 : /* IReadBlock() */
88 : /************************************************************************/
89 :
90 0 : CPLErr TILRasterBand::IReadBlock(int iBlockX, int iBlockY, void *pBuffer)
91 :
92 : {
93 0 : return poVRTBand->ReadBlock(iBlockX, iBlockY, pBuffer);
94 : }
95 :
96 : /************************************************************************/
97 : /* IRasterIO() */
98 : /************************************************************************/
99 :
100 2 : 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 : {
108 2 : if (GetOverviewCount() > 0)
109 : {
110 0 : return GDALPamRasterBand::IRasterIO(
111 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
112 0 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
113 : }
114 :
115 : // If not exist TIL overviews, try to use band source overviews.
116 2 : return poVRTBand->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
117 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
118 2 : nLineSpace, psExtraArg);
119 : }
120 :
121 : /************************************************************************/
122 : /* ==================================================================== */
123 : /* TILDataset */
124 : /* ==================================================================== */
125 : /************************************************************************/
126 :
127 : /************************************************************************/
128 : /* TILDataset() */
129 : /************************************************************************/
130 :
131 5 : TILDataset::TILDataset() : poVRTDS(nullptr), papszMetadataFiles(nullptr)
132 : {
133 5 : }
134 :
135 : /************************************************************************/
136 : /* ~TILDataset() */
137 : /************************************************************************/
138 :
139 10 : TILDataset::~TILDataset()
140 :
141 : {
142 5 : TILDataset::CloseDependentDatasets();
143 5 : CSLDestroy(papszMetadataFiles);
144 10 : }
145 :
146 : /************************************************************************/
147 : /* CloseDependentDatasets() */
148 : /************************************************************************/
149 :
150 17 : int TILDataset::CloseDependentDatasets()
151 : {
152 17 : int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
153 :
154 17 : if (poVRTDS)
155 : {
156 5 : bHasDroppedRef = TRUE;
157 5 : delete poVRTDS;
158 5 : poVRTDS = nullptr;
159 : }
160 :
161 17 : return bHasDroppedRef;
162 : }
163 :
164 : /************************************************************************/
165 : /* Identify() */
166 : /************************************************************************/
167 :
168 60205 : int TILDataset::Identify(GDALOpenInfo *poOpenInfo)
169 :
170 : {
171 64667 : if (poOpenInfo->nHeaderBytes < 200 ||
172 4462 : !poOpenInfo->IsExtensionEqualToCI("TIL"))
173 60195 : return FALSE;
174 :
175 10 : if (strstr((const char *)poOpenInfo->pabyHeader, "numTiles") == nullptr)
176 0 : return FALSE;
177 :
178 10 : return TRUE;
179 : }
180 :
181 : /************************************************************************/
182 : /* Open() */
183 : /************************************************************************/
184 :
185 5 : GDALDataset *TILDataset::Open(GDALOpenInfo *poOpenInfo)
186 :
187 : {
188 5 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
189 0 : return nullptr;
190 :
191 : /* -------------------------------------------------------------------- */
192 : /* Confirm the requested access is supported. */
193 : /* -------------------------------------------------------------------- */
194 5 : if (poOpenInfo->eAccess == GA_Update)
195 : {
196 0 : ReportUpdateNotSupportedByDriver("TIL");
197 0 : return nullptr;
198 : }
199 :
200 10 : CPLString osDirname = CPLGetDirnameSafe(poOpenInfo->pszFilename);
201 :
202 : // get metadata reader
203 :
204 10 : GDALMDReaderManager mdreadermanager;
205 10 : GDALMDReaderBase *mdreader = mdreadermanager.GetReader(
206 5 : poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles(), MDR_DG);
207 :
208 5 : if (nullptr == mdreader)
209 : {
210 0 : CPLError(CE_Failure, CPLE_OpenFailed,
211 : "Unable to open .TIL dataset due to missing metadata file.");
212 0 : return nullptr;
213 : }
214 : /* -------------------------------------------------------------------- */
215 : /* Try to find the corresponding .IMD file. */
216 : /* -------------------------------------------------------------------- */
217 5 : char **papszIMD = mdreader->GetMetadataDomain(MD_DOMAIN_IMD);
218 :
219 5 : if (papszIMD == nullptr)
220 : {
221 0 : CPLError(CE_Failure, CPLE_OpenFailed,
222 : "Unable to open .TIL dataset due to missing .IMD file.");
223 0 : return nullptr;
224 : }
225 :
226 5 : if (CSLFetchNameValue(papszIMD, "numRows") == nullptr ||
227 10 : CSLFetchNameValue(papszIMD, "numColumns") == nullptr ||
228 5 : CSLFetchNameValue(papszIMD, "bitsPerPixel") == nullptr)
229 : {
230 0 : CPLError(CE_Failure, CPLE_OpenFailed,
231 : "Missing a required field in the .IMD file.");
232 0 : return nullptr;
233 : }
234 :
235 : /* -------------------------------------------------------------------- */
236 : /* Try to load and parse the .TIL file. */
237 : /* -------------------------------------------------------------------- */
238 5 : VSILFILE *fp = poOpenInfo->fpL;
239 5 : poOpenInfo->fpL = nullptr;
240 :
241 10 : CPLKeywordParser oParser;
242 :
243 5 : if (!oParser.Ingest(fp))
244 : {
245 0 : VSIFCloseL(fp);
246 0 : return nullptr;
247 : }
248 :
249 5 : VSIFCloseL(fp);
250 :
251 5 : char **papszTIL = oParser.GetAllKeywords();
252 :
253 : /* -------------------------------------------------------------------- */
254 : /* Create a corresponding GDALDataset. */
255 : /* -------------------------------------------------------------------- */
256 10 : auto poDS = std::make_unique<TILDataset>();
257 5 : poDS->papszMetadataFiles = mdreader->GetMetadataFiles();
258 5 : mdreader->FillMetadata(&poDS->oMDMD);
259 10 : poDS->nRasterXSize =
260 5 : atoi(CSLFetchNameValueDef(papszIMD, "numColumns", "0"));
261 5 : poDS->nRasterYSize = atoi(CSLFetchNameValueDef(papszIMD, "numRows", "0"));
262 5 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
263 : {
264 0 : return nullptr;
265 : }
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 5 : const char *pszFilename = CSLFetchNameValue(papszTIL, "TILE_1.filename");
272 5 : if (pszFilename == nullptr)
273 : {
274 0 : CPLError(CE_Failure, CPLE_AppDefined,
275 : "Missing TILE_1.filename in .TIL file.");
276 0 : return nullptr;
277 : }
278 :
279 : // trim double quotes.
280 5 : if (pszFilename[0] == '"')
281 5 : pszFilename++;
282 5 : if (pszFilename[strlen(pszFilename) - 1] == '"')
283 5 : const_cast<char *>(pszFilename)[strlen(pszFilename) - 1] = '\0';
284 5 : if (CPLHasPathTraversal(pszFilename))
285 : {
286 0 : CPLError(CE_Failure, CPLE_NotSupported, "Path traversal detected in %s",
287 : pszFilename);
288 0 : return nullptr;
289 : }
290 :
291 10 : CPLString osFilename = CPLFormFilenameSafe(osDirname, pszFilename, nullptr);
292 : auto poTemplateDS = std::unique_ptr<GDALDataset>(
293 10 : GDALDataset::Open(osFilename, GDAL_OF_RASTER | GDAL_OF_VERBOSE_ERROR));
294 5 : if (poTemplateDS == nullptr || poTemplateDS->GetRasterCount() == 0)
295 : {
296 0 : return nullptr;
297 : }
298 :
299 5 : GDALRasterBand *poTemplateBand = poTemplateDS->GetRasterBand(1);
300 5 : const GDALDataType eDT = poTemplateBand->GetRasterDataType();
301 5 : const int nBandCount = poTemplateDS->GetRasterCount();
302 :
303 : // we suppose the first tile have the same projection as others (usually so)
304 10 : CPLString pszProjection(poTemplateDS->GetProjectionRef());
305 5 : if (!pszProjection.empty())
306 5 : poDS->SetProjection(pszProjection);
307 :
308 : // we suppose the first tile have the same GeoTransform as others (usually
309 : // so)
310 5 : GDALGeoTransform gt;
311 5 : if (poTemplateDS->GetGeoTransform(gt) == CE_None)
312 : {
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 5 : gt[0] = CPLAtof(CSLFetchNameValueDef(
318 5 : papszIMD, "MAP_PROJECTED_PRODUCT.ULX", "0")) -
319 5 : gt[1] / 2;
320 5 : gt[3] = CPLAtof(CSLFetchNameValueDef(
321 5 : papszIMD, "MAP_PROJECTED_PRODUCT.ULY", "0")) -
322 5 : gt[5] / 2;
323 5 : poDS->SetGeoTransform(gt);
324 : }
325 :
326 5 : poTemplateBand = nullptr;
327 5 : poTemplateDS.reset();
328 :
329 : /* -------------------------------------------------------------------- */
330 : /* Create and initialize the corresponding VRT dataset used to */
331 : /* manage the tiled data access. */
332 : /* -------------------------------------------------------------------- */
333 5 : poDS->poVRTDS = new VRTDataset(poDS->nRasterXSize, poDS->nRasterYSize);
334 :
335 10 : for (int iBand = 0; iBand < nBandCount; iBand++)
336 5 : poDS->poVRTDS->AddBand(eDT, nullptr);
337 :
338 : /* Don't try to write a VRT file */
339 5 : poDS->poVRTDS->SetWritable(FALSE);
340 :
341 : /* -------------------------------------------------------------------- */
342 : /* Create band information objects. */
343 : /* -------------------------------------------------------------------- */
344 10 : for (int iBand = 1; iBand <= nBandCount; iBand++)
345 10 : poDS->SetBand(
346 5 : iBand, new TILRasterBand(poDS.get(), iBand,
347 : reinterpret_cast<VRTSourcedRasterBand *>(
348 5 : poDS->poVRTDS->GetRasterBand(iBand))));
349 :
350 : /* -------------------------------------------------------------------- */
351 : /* Add tiles as sources for each band. */
352 : /* -------------------------------------------------------------------- */
353 : const int nTileCount =
354 5 : atoi(CSLFetchNameValueDef(papszTIL, "numTiles", "0"));
355 5 : int iTile = 0;
356 :
357 10 : for (iTile = 1; iTile <= nTileCount; iTile++)
358 : {
359 5 : CPLString osKey;
360 5 : osKey.Printf("TILE_%d.filename", iTile);
361 5 : pszFilename = CSLFetchNameValue(papszTIL, osKey);
362 5 : if (pszFilename == nullptr)
363 : {
364 0 : CPLError(CE_Failure, CPLE_AppDefined,
365 : "Missing TILE_%d.filename in .TIL file.", iTile);
366 0 : return nullptr;
367 : }
368 :
369 : // trim double quotes.
370 5 : if (pszFilename[0] == '"')
371 5 : pszFilename++;
372 5 : if (pszFilename[strlen(pszFilename) - 1] == '"')
373 0 : const_cast<char *>(pszFilename)[strlen(pszFilename) - 1] = '\0';
374 5 : if (CPLHasPathTraversal(pszFilename))
375 : {
376 0 : CPLError(CE_Failure, CPLE_NotSupported,
377 : "Path traversal detected in %s", pszFilename);
378 0 : return nullptr;
379 : }
380 :
381 5 : osFilename = CPLFormFilenameSafe(osDirname, pszFilename, nullptr);
382 5 : poDS->m_aosFilenames.push_back(osFilename);
383 :
384 5 : osKey.Printf("TILE_%d.ULColOffset", iTile);
385 5 : const int nULX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
386 :
387 5 : osKey.Printf("TILE_%d.ULRowOffset", iTile);
388 5 : const int nULY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
389 :
390 5 : osKey.Printf("TILE_%d.LRColOffset", iTile);
391 5 : const int nLRX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
392 :
393 5 : osKey.Printf("TILE_%d.LRRowOffset", iTile);
394 5 : const int nLRY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
395 :
396 10 : for (int iBand = 1; iBand <= nBandCount; iBand++)
397 : {
398 : VRTSourcedRasterBand *poVRTBand =
399 : reinterpret_cast<VRTSourcedRasterBand *>(
400 5 : poDS->poVRTDS->GetRasterBand(iBand));
401 :
402 5 : poVRTBand->AddSimpleSource(osFilename, iBand, 0, 0, nLRX - nULX + 1,
403 5 : nLRY - nULY + 1, nULX, nULY,
404 5 : nLRX - nULX + 1, nLRY - nULY + 1);
405 : }
406 : }
407 :
408 : /* -------------------------------------------------------------------- */
409 : /* Initialize any PAM information. */
410 : /* -------------------------------------------------------------------- */
411 5 : poDS->SetDescription(poOpenInfo->pszFilename);
412 5 : poDS->TryLoadXML();
413 :
414 : /* -------------------------------------------------------------------- */
415 : /* Check for overviews. */
416 : /* -------------------------------------------------------------------- */
417 5 : poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
418 :
419 5 : return poDS.release();
420 : }
421 :
422 : /************************************************************************/
423 : /* GetFileList() */
424 : /************************************************************************/
425 :
426 3 : char **TILDataset::GetFileList()
427 :
428 : {
429 3 : char **papszFileList = GDALPamDataset::GetFileList();
430 :
431 6 : for (const auto &osFilename : m_aosFilenames)
432 3 : papszFileList = CSLAddString(papszFileList, osFilename.c_str());
433 :
434 3 : if (nullptr != papszMetadataFiles)
435 : {
436 6 : for (int i = 0; papszMetadataFiles[i] != nullptr; i++)
437 : {
438 3 : papszFileList = CSLAddString(papszFileList, papszMetadataFiles[i]);
439 : }
440 : }
441 :
442 3 : return papszFileList;
443 : }
444 :
445 : /************************************************************************/
446 : /* GDALRegister_TIL() */
447 : /************************************************************************/
448 :
449 2024 : void GDALRegister_TIL()
450 :
451 : {
452 2024 : if (GDALGetDriverByName("TIL") != nullptr)
453 283 : return;
454 :
455 1741 : GDALDriver *poDriver = new GDALDriver();
456 :
457 1741 : poDriver->SetDescription("TIL");
458 1741 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
459 1741 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "EarthWatch .TIL");
460 1741 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/til.html");
461 1741 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
462 :
463 1741 : poDriver->pfnOpen = TILDataset::Open;
464 1741 : poDriver->pfnIdentify = TILDataset::Identify;
465 :
466 1741 : GetGDALDriverManager()->RegisterDriver(poDriver);
467 : }
|