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 : virtual int CloseDependentDatasets() override;
38 :
39 : public:
40 : TILDataset();
41 : virtual ~TILDataset();
42 :
43 : virtual 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 10 : virtual ~TILRasterBand()
65 5 : {
66 10 : }
67 :
68 : virtual CPLErr IReadBlock(int, int, void *) override;
69 : virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
70 : GDALDataType, GSpacing nPixelSpace,
71 : GSpacing nLineSpace,
72 : GDALRasterIOExtraArg *psExtraArg) override;
73 : };
74 :
75 : /************************************************************************/
76 : /* TILRasterBand() */
77 : /************************************************************************/
78 :
79 5 : TILRasterBand::TILRasterBand(TILDataset *poTILDS, int nBandIn,
80 5 : VRTSourcedRasterBand *poVRTBandIn)
81 :
82 : {
83 5 : poDS = poTILDS;
84 5 : poVRTBand = poVRTBandIn;
85 5 : nBand = nBandIn;
86 5 : eDataType = poVRTBandIn->GetRasterDataType();
87 :
88 5 : poVRTBandIn->GetBlockSize(&nBlockXSize, &nBlockYSize);
89 5 : }
90 :
91 : /************************************************************************/
92 : /* IReadBlock() */
93 : /************************************************************************/
94 :
95 0 : CPLErr TILRasterBand::IReadBlock(int iBlockX, int iBlockY, void *pBuffer)
96 :
97 : {
98 0 : return poVRTBand->ReadBlock(iBlockX, iBlockY, pBuffer);
99 : }
100 :
101 : /************************************************************************/
102 : /* IRasterIO() */
103 : /************************************************************************/
104 :
105 2 : CPLErr TILRasterBand::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
106 : int nXSize, int nYSize, void *pData,
107 : int nBufXSize, int nBufYSize,
108 : GDALDataType eBufType, GSpacing nPixelSpace,
109 : GSpacing nLineSpace,
110 : GDALRasterIOExtraArg *psExtraArg)
111 :
112 : {
113 2 : if (GetOverviewCount() > 0)
114 : {
115 0 : return GDALPamRasterBand::IRasterIO(
116 : eRWFlag, nXOff, nYOff, nXSize, nYSize, pData, nBufXSize, nBufYSize,
117 0 : eBufType, nPixelSpace, nLineSpace, psExtraArg);
118 : }
119 :
120 : // If not exist TIL overviews, try to use band source overviews.
121 2 : return poVRTBand->IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
122 : nBufXSize, nBufYSize, eBufType, nPixelSpace,
123 2 : nLineSpace, psExtraArg);
124 : }
125 :
126 : /************************************************************************/
127 : /* ==================================================================== */
128 : /* TILDataset */
129 : /* ==================================================================== */
130 : /************************************************************************/
131 :
132 : /************************************************************************/
133 : /* TILDataset() */
134 : /************************************************************************/
135 :
136 5 : TILDataset::TILDataset() : poVRTDS(nullptr), papszMetadataFiles(nullptr)
137 : {
138 5 : }
139 :
140 : /************************************************************************/
141 : /* ~TILDataset() */
142 : /************************************************************************/
143 :
144 10 : TILDataset::~TILDataset()
145 :
146 : {
147 5 : TILDataset::CloseDependentDatasets();
148 5 : CSLDestroy(papszMetadataFiles);
149 10 : }
150 :
151 : /************************************************************************/
152 : /* CloseDependentDatasets() */
153 : /************************************************************************/
154 :
155 18 : int TILDataset::CloseDependentDatasets()
156 : {
157 18 : int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
158 :
159 18 : if (poVRTDS)
160 : {
161 5 : bHasDroppedRef = TRUE;
162 5 : delete poVRTDS;
163 5 : poVRTDS = nullptr;
164 : }
165 :
166 18 : return bHasDroppedRef;
167 : }
168 :
169 : /************************************************************************/
170 : /* Identify() */
171 : /************************************************************************/
172 :
173 53847 : int TILDataset::Identify(GDALOpenInfo *poOpenInfo)
174 :
175 : {
176 58268 : if (poOpenInfo->nHeaderBytes < 200 ||
177 4421 : !EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "TIL"))
178 53837 : return FALSE;
179 :
180 10 : if (strstr((const char *)poOpenInfo->pabyHeader, "numTiles") == nullptr)
181 0 : return FALSE;
182 :
183 10 : return TRUE;
184 : }
185 :
186 : /************************************************************************/
187 : /* Open() */
188 : /************************************************************************/
189 :
190 5 : GDALDataset *TILDataset::Open(GDALOpenInfo *poOpenInfo)
191 :
192 : {
193 5 : if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
194 0 : return nullptr;
195 :
196 : /* -------------------------------------------------------------------- */
197 : /* Confirm the requested access is supported. */
198 : /* -------------------------------------------------------------------- */
199 5 : if (poOpenInfo->eAccess == GA_Update)
200 : {
201 0 : CPLError(CE_Failure, CPLE_NotSupported,
202 : "The TIL driver does not support update access to existing"
203 : " datasets.\n");
204 0 : return nullptr;
205 : }
206 :
207 10 : CPLString osDirname = CPLGetDirname(poOpenInfo->pszFilename);
208 :
209 : // get metadata reader
210 :
211 10 : GDALMDReaderManager mdreadermanager;
212 5 : GDALMDReaderBase *mdreader = mdreadermanager.GetReader(
213 5 : poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles(), MDR_DG);
214 :
215 5 : if (nullptr == mdreader)
216 : {
217 0 : CPLError(CE_Failure, CPLE_OpenFailed,
218 : "Unable to open .TIL dataset due to missing metadata file.");
219 0 : return nullptr;
220 : }
221 : /* -------------------------------------------------------------------- */
222 : /* Try to find the corresponding .IMD file. */
223 : /* -------------------------------------------------------------------- */
224 5 : char **papszIMD = mdreader->GetMetadataDomain(MD_DOMAIN_IMD);
225 :
226 5 : if (papszIMD == nullptr)
227 : {
228 0 : CPLError(CE_Failure, CPLE_OpenFailed,
229 : "Unable to open .TIL dataset due to missing .IMD file.");
230 0 : return nullptr;
231 : }
232 :
233 5 : if (CSLFetchNameValue(papszIMD, "numRows") == nullptr ||
234 10 : CSLFetchNameValue(papszIMD, "numColumns") == nullptr ||
235 5 : CSLFetchNameValue(papszIMD, "bitsPerPixel") == nullptr)
236 : {
237 0 : CPLError(CE_Failure, CPLE_OpenFailed,
238 : "Missing a required field in the .IMD file.");
239 0 : return nullptr;
240 : }
241 :
242 : /* -------------------------------------------------------------------- */
243 : /* Try to load and parse the .TIL file. */
244 : /* -------------------------------------------------------------------- */
245 5 : VSILFILE *fp = poOpenInfo->fpL;
246 5 : poOpenInfo->fpL = nullptr;
247 :
248 10 : CPLKeywordParser oParser;
249 :
250 5 : if (!oParser.Ingest(fp))
251 : {
252 0 : VSIFCloseL(fp);
253 0 : return nullptr;
254 : }
255 :
256 5 : VSIFCloseL(fp);
257 :
258 5 : char **papszTIL = oParser.GetAllKeywords();
259 :
260 : /* -------------------------------------------------------------------- */
261 : /* Create a corresponding GDALDataset. */
262 : /* -------------------------------------------------------------------- */
263 5 : TILDataset *poDS = new TILDataset();
264 5 : poDS->papszMetadataFiles = mdreader->GetMetadataFiles();
265 5 : mdreader->FillMetadata(&poDS->oMDMD);
266 5 : poDS->nRasterXSize =
267 5 : atoi(CSLFetchNameValueDef(papszIMD, "numColumns", "0"));
268 5 : poDS->nRasterYSize = atoi(CSLFetchNameValueDef(papszIMD, "numRows", "0"));
269 5 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
270 : {
271 0 : delete poDS;
272 0 : return nullptr;
273 : }
274 :
275 : /* -------------------------------------------------------------------- */
276 : /* We need to open one of the images in order to establish */
277 : /* details like the band count and types. */
278 : /* -------------------------------------------------------------------- */
279 5 : const char *pszFilename = CSLFetchNameValue(papszTIL, "TILE_1.filename");
280 5 : if (pszFilename == nullptr)
281 : {
282 0 : CPLError(CE_Failure, CPLE_AppDefined,
283 : "Missing TILE_1.filename in .TIL file.");
284 0 : delete poDS;
285 0 : return nullptr;
286 : }
287 :
288 : // trim double quotes.
289 5 : if (pszFilename[0] == '"')
290 5 : pszFilename++;
291 5 : if (pszFilename[strlen(pszFilename) - 1] == '"')
292 5 : const_cast<char *>(pszFilename)[strlen(pszFilename) - 1] = '\0';
293 :
294 10 : CPLString osFilename = CPLFormFilename(osDirname, pszFilename, nullptr);
295 : GDALDataset *poTemplateDS =
296 5 : GDALDataset::FromHandle(GDALOpen(osFilename, GA_ReadOnly));
297 5 : if (poTemplateDS == nullptr || poTemplateDS->GetRasterCount() == 0)
298 : {
299 0 : delete poDS;
300 0 : if (poTemplateDS != nullptr)
301 0 : GDALClose(poTemplateDS);
302 0 : return nullptr;
303 : }
304 :
305 5 : GDALRasterBand *poTemplateBand = poTemplateDS->GetRasterBand(1);
306 5 : const GDALDataType eDT = poTemplateBand->GetRasterDataType();
307 5 : const int nBandCount = poTemplateDS->GetRasterCount();
308 :
309 : // we suppose the first tile have the same projection as others (usually so)
310 10 : CPLString pszProjection(poTemplateDS->GetProjectionRef());
311 5 : if (!pszProjection.empty())
312 5 : poDS->SetProjection(pszProjection);
313 :
314 : // we suppose the first tile have the same GeoTransform as others (usually
315 : // so)
316 : double adfGeoTransform[6];
317 5 : if (poTemplateDS->GetGeoTransform(adfGeoTransform) == CE_None)
318 : {
319 : // According to
320 : // https://www.digitalglobe.com/sites/default/files/ISD_External.pdf,
321 : // ulx=originX and is "Easting of the center of the upper left pixel of
322 : // the image."
323 5 : adfGeoTransform[0] = CPLAtof(CSLFetchNameValueDef(
324 5 : papszIMD, "MAP_PROJECTED_PRODUCT.ULX", "0")) -
325 5 : adfGeoTransform[1] / 2;
326 5 : adfGeoTransform[3] = CPLAtof(CSLFetchNameValueDef(
327 5 : papszIMD, "MAP_PROJECTED_PRODUCT.ULY", "0")) -
328 5 : adfGeoTransform[5] / 2;
329 5 : poDS->SetGeoTransform(adfGeoTransform);
330 : }
331 :
332 5 : poTemplateBand = nullptr;
333 5 : GDALClose(poTemplateDS);
334 :
335 : /* -------------------------------------------------------------------- */
336 : /* Create and initialize the corresponding VRT dataset used to */
337 : /* manage the tiled data access. */
338 : /* -------------------------------------------------------------------- */
339 5 : poDS->poVRTDS = new VRTDataset(poDS->nRasterXSize, poDS->nRasterYSize);
340 :
341 10 : for (int iBand = 0; iBand < nBandCount; iBand++)
342 5 : poDS->poVRTDS->AddBand(eDT, nullptr);
343 :
344 : /* Don't try to write a VRT file */
345 5 : poDS->poVRTDS->SetWritable(FALSE);
346 :
347 : /* -------------------------------------------------------------------- */
348 : /* Create band information objects. */
349 : /* -------------------------------------------------------------------- */
350 10 : for (int iBand = 1; iBand <= nBandCount; iBand++)
351 5 : poDS->SetBand(
352 : iBand, new TILRasterBand(poDS, iBand,
353 : reinterpret_cast<VRTSourcedRasterBand *>(
354 5 : poDS->poVRTDS->GetRasterBand(iBand))));
355 :
356 : /* -------------------------------------------------------------------- */
357 : /* Add tiles as sources for each band. */
358 : /* -------------------------------------------------------------------- */
359 : const int nTileCount =
360 5 : atoi(CSLFetchNameValueDef(papszTIL, "numTiles", "0"));
361 5 : int iTile = 0;
362 :
363 10 : for (iTile = 1; iTile <= nTileCount; iTile++)
364 : {
365 5 : CPLString osKey;
366 5 : osKey.Printf("TILE_%d.filename", iTile);
367 5 : pszFilename = CSLFetchNameValue(papszTIL, osKey);
368 5 : if (pszFilename == nullptr)
369 : {
370 0 : CPLError(CE_Failure, CPLE_AppDefined,
371 : "Missing TILE_%d.filename in .TIL file.", iTile);
372 0 : delete poDS;
373 0 : return nullptr;
374 : }
375 :
376 : // trim double quotes.
377 5 : if (pszFilename[0] == '"')
378 5 : pszFilename++;
379 5 : if (pszFilename[strlen(pszFilename) - 1] == '"')
380 0 : const_cast<char *>(pszFilename)[strlen(pszFilename) - 1] = '\0';
381 5 : osFilename = CPLFormFilename(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, poOpenInfo->pszFilename);
418 :
419 5 : return poDS;
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 1595 : void GDALRegister_TIL()
450 :
451 : {
452 1595 : if (GDALGetDriverByName("TIL") != nullptr)
453 302 : return;
454 :
455 1293 : GDALDriver *poDriver = new GDALDriver();
456 :
457 1293 : poDriver->SetDescription("TIL");
458 1293 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
459 1293 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "EarthWatch .TIL");
460 1293 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/til.html");
461 1293 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
462 :
463 1293 : poDriver->pfnOpen = TILDataset::Open;
464 1293 : poDriver->pfnIdentify = TILDataset::Identify;
465 :
466 1293 : GetGDALDriverManager()->RegisterDriver(poDriver);
467 : }
|