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 24 : int TILDataset::CloseDependentDatasets()
156 : {
157 24 : int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
158 :
159 24 : if (poVRTDS)
160 : {
161 5 : bHasDroppedRef = TRUE;
162 5 : delete poVRTDS;
163 5 : poVRTDS = nullptr;
164 : }
165 :
166 24 : return bHasDroppedRef;
167 : }
168 :
169 : /************************************************************************/
170 : /* Identify() */
171 : /************************************************************************/
172 :
173 54821 : int TILDataset::Identify(GDALOpenInfo *poOpenInfo)
174 :
175 : {
176 59282 : if (poOpenInfo->nHeaderBytes < 200 ||
177 4461 : !poOpenInfo->IsExtensionEqualToCI("TIL"))
178 54810 : return FALSE;
179 :
180 11 : if (strstr((const char *)poOpenInfo->pabyHeader, "numTiles") == nullptr)
181 0 : return FALSE;
182 :
183 11 : 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 : ReportUpdateNotSupportedByDriver("TIL");
202 0 : return nullptr;
203 : }
204 :
205 10 : CPLString osDirname = CPLGetDirnameSafe(poOpenInfo->pszFilename);
206 :
207 : // get metadata reader
208 :
209 10 : GDALMDReaderManager mdreadermanager;
210 5 : GDALMDReaderBase *mdreader = mdreadermanager.GetReader(
211 5 : poOpenInfo->pszFilename, poOpenInfo->GetSiblingFiles(), MDR_DG);
212 :
213 5 : if (nullptr == mdreader)
214 : {
215 0 : CPLError(CE_Failure, CPLE_OpenFailed,
216 : "Unable to open .TIL dataset due to missing metadata file.");
217 0 : return nullptr;
218 : }
219 : /* -------------------------------------------------------------------- */
220 : /* Try to find the corresponding .IMD file. */
221 : /* -------------------------------------------------------------------- */
222 5 : char **papszIMD = mdreader->GetMetadataDomain(MD_DOMAIN_IMD);
223 :
224 5 : if (papszIMD == nullptr)
225 : {
226 0 : CPLError(CE_Failure, CPLE_OpenFailed,
227 : "Unable to open .TIL dataset due to missing .IMD file.");
228 0 : return nullptr;
229 : }
230 :
231 5 : if (CSLFetchNameValue(papszIMD, "numRows") == nullptr ||
232 10 : CSLFetchNameValue(papszIMD, "numColumns") == nullptr ||
233 5 : CSLFetchNameValue(papszIMD, "bitsPerPixel") == nullptr)
234 : {
235 0 : CPLError(CE_Failure, CPLE_OpenFailed,
236 : "Missing a required field in the .IMD file.");
237 0 : return nullptr;
238 : }
239 :
240 : /* -------------------------------------------------------------------- */
241 : /* Try to load and parse the .TIL file. */
242 : /* -------------------------------------------------------------------- */
243 5 : VSILFILE *fp = poOpenInfo->fpL;
244 5 : poOpenInfo->fpL = nullptr;
245 :
246 10 : CPLKeywordParser oParser;
247 :
248 5 : if (!oParser.Ingest(fp))
249 : {
250 0 : VSIFCloseL(fp);
251 0 : return nullptr;
252 : }
253 :
254 5 : VSIFCloseL(fp);
255 :
256 5 : char **papszTIL = oParser.GetAllKeywords();
257 :
258 : /* -------------------------------------------------------------------- */
259 : /* Create a corresponding GDALDataset. */
260 : /* -------------------------------------------------------------------- */
261 5 : TILDataset *poDS = new TILDataset();
262 5 : poDS->papszMetadataFiles = mdreader->GetMetadataFiles();
263 5 : mdreader->FillMetadata(&poDS->oMDMD);
264 5 : poDS->nRasterXSize =
265 5 : atoi(CSLFetchNameValueDef(papszIMD, "numColumns", "0"));
266 5 : poDS->nRasterYSize = atoi(CSLFetchNameValueDef(papszIMD, "numRows", "0"));
267 5 : if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
268 : {
269 0 : delete poDS;
270 0 : return nullptr;
271 : }
272 :
273 : /* -------------------------------------------------------------------- */
274 : /* We need to open one of the images in order to establish */
275 : /* details like the band count and types. */
276 : /* -------------------------------------------------------------------- */
277 5 : const char *pszFilename = CSLFetchNameValue(papszTIL, "TILE_1.filename");
278 5 : if (pszFilename == nullptr)
279 : {
280 0 : CPLError(CE_Failure, CPLE_AppDefined,
281 : "Missing TILE_1.filename in .TIL file.");
282 0 : delete poDS;
283 0 : return nullptr;
284 : }
285 :
286 : // trim double quotes.
287 5 : if (pszFilename[0] == '"')
288 5 : pszFilename++;
289 5 : if (pszFilename[strlen(pszFilename) - 1] == '"')
290 5 : const_cast<char *>(pszFilename)[strlen(pszFilename) - 1] = '\0';
291 :
292 10 : CPLString osFilename = CPLFormFilenameSafe(osDirname, pszFilename, nullptr);
293 : GDALDataset *poTemplateDS =
294 5 : GDALDataset::FromHandle(GDALOpen(osFilename, GA_ReadOnly));
295 5 : if (poTemplateDS == nullptr || poTemplateDS->GetRasterCount() == 0)
296 : {
297 0 : delete poDS;
298 0 : if (poTemplateDS != nullptr)
299 0 : GDALClose(poTemplateDS);
300 0 : return nullptr;
301 : }
302 :
303 5 : GDALRasterBand *poTemplateBand = poTemplateDS->GetRasterBand(1);
304 5 : const GDALDataType eDT = poTemplateBand->GetRasterDataType();
305 5 : const int nBandCount = poTemplateDS->GetRasterCount();
306 :
307 : // we suppose the first tile have the same projection as others (usually so)
308 10 : CPLString pszProjection(poTemplateDS->GetProjectionRef());
309 5 : if (!pszProjection.empty())
310 5 : poDS->SetProjection(pszProjection);
311 :
312 : // we suppose the first tile have the same GeoTransform as others (usually
313 : // so)
314 : double adfGeoTransform[6];
315 5 : if (poTemplateDS->GetGeoTransform(adfGeoTransform) == CE_None)
316 : {
317 : // According to
318 : // https://www.digitalglobe.com/sites/default/files/ISD_External.pdf,
319 : // ulx=originX and is "Easting of the center of the upper left pixel of
320 : // the image."
321 5 : adfGeoTransform[0] = CPLAtof(CSLFetchNameValueDef(
322 5 : papszIMD, "MAP_PROJECTED_PRODUCT.ULX", "0")) -
323 5 : adfGeoTransform[1] / 2;
324 5 : adfGeoTransform[3] = CPLAtof(CSLFetchNameValueDef(
325 5 : papszIMD, "MAP_PROJECTED_PRODUCT.ULY", "0")) -
326 5 : adfGeoTransform[5] / 2;
327 5 : poDS->SetGeoTransform(adfGeoTransform);
328 : }
329 :
330 5 : poTemplateBand = nullptr;
331 5 : GDALClose(poTemplateDS);
332 :
333 : /* -------------------------------------------------------------------- */
334 : /* Create and initialize the corresponding VRT dataset used to */
335 : /* manage the tiled data access. */
336 : /* -------------------------------------------------------------------- */
337 5 : poDS->poVRTDS = new VRTDataset(poDS->nRasterXSize, poDS->nRasterYSize);
338 :
339 10 : for (int iBand = 0; iBand < nBandCount; iBand++)
340 5 : poDS->poVRTDS->AddBand(eDT, nullptr);
341 :
342 : /* Don't try to write a VRT file */
343 5 : poDS->poVRTDS->SetWritable(FALSE);
344 :
345 : /* -------------------------------------------------------------------- */
346 : /* Create band information objects. */
347 : /* -------------------------------------------------------------------- */
348 10 : for (int iBand = 1; iBand <= nBandCount; iBand++)
349 5 : poDS->SetBand(
350 : iBand, new TILRasterBand(poDS, iBand,
351 : reinterpret_cast<VRTSourcedRasterBand *>(
352 5 : poDS->poVRTDS->GetRasterBand(iBand))));
353 :
354 : /* -------------------------------------------------------------------- */
355 : /* Add tiles as sources for each band. */
356 : /* -------------------------------------------------------------------- */
357 : const int nTileCount =
358 5 : atoi(CSLFetchNameValueDef(papszTIL, "numTiles", "0"));
359 5 : int iTile = 0;
360 :
361 10 : for (iTile = 1; iTile <= nTileCount; iTile++)
362 : {
363 5 : CPLString osKey;
364 5 : osKey.Printf("TILE_%d.filename", iTile);
365 5 : pszFilename = CSLFetchNameValue(papszTIL, osKey);
366 5 : if (pszFilename == nullptr)
367 : {
368 0 : CPLError(CE_Failure, CPLE_AppDefined,
369 : "Missing TILE_%d.filename in .TIL file.", iTile);
370 0 : delete poDS;
371 0 : return nullptr;
372 : }
373 :
374 : // trim double quotes.
375 5 : if (pszFilename[0] == '"')
376 5 : pszFilename++;
377 5 : if (pszFilename[strlen(pszFilename) - 1] == '"')
378 0 : const_cast<char *>(pszFilename)[strlen(pszFilename) - 1] = '\0';
379 5 : osFilename = CPLFormFilenameSafe(osDirname, pszFilename, nullptr);
380 5 : poDS->m_aosFilenames.push_back(osFilename);
381 :
382 5 : osKey.Printf("TILE_%d.ULColOffset", iTile);
383 5 : const int nULX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
384 :
385 5 : osKey.Printf("TILE_%d.ULRowOffset", iTile);
386 5 : const int nULY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
387 :
388 5 : osKey.Printf("TILE_%d.LRColOffset", iTile);
389 5 : const int nLRX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
390 :
391 5 : osKey.Printf("TILE_%d.LRRowOffset", iTile);
392 5 : const int nLRY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
393 :
394 10 : for (int iBand = 1; iBand <= nBandCount; iBand++)
395 : {
396 : VRTSourcedRasterBand *poVRTBand =
397 : reinterpret_cast<VRTSourcedRasterBand *>(
398 5 : poDS->poVRTDS->GetRasterBand(iBand));
399 :
400 5 : poVRTBand->AddSimpleSource(osFilename, iBand, 0, 0, nLRX - nULX + 1,
401 5 : nLRY - nULY + 1, nULX, nULY,
402 5 : nLRX - nULX + 1, nLRY - nULY + 1);
403 : }
404 : }
405 :
406 : /* -------------------------------------------------------------------- */
407 : /* Initialize any PAM information. */
408 : /* -------------------------------------------------------------------- */
409 5 : poDS->SetDescription(poOpenInfo->pszFilename);
410 5 : poDS->TryLoadXML();
411 :
412 : /* -------------------------------------------------------------------- */
413 : /* Check for overviews. */
414 : /* -------------------------------------------------------------------- */
415 5 : poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
416 :
417 5 : return poDS;
418 : }
419 :
420 : /************************************************************************/
421 : /* GetFileList() */
422 : /************************************************************************/
423 :
424 3 : char **TILDataset::GetFileList()
425 :
426 : {
427 3 : char **papszFileList = GDALPamDataset::GetFileList();
428 :
429 6 : for (const auto &osFilename : m_aosFilenames)
430 3 : papszFileList = CSLAddString(papszFileList, osFilename.c_str());
431 :
432 3 : if (nullptr != papszMetadataFiles)
433 : {
434 6 : for (int i = 0; papszMetadataFiles[i] != nullptr; i++)
435 : {
436 3 : papszFileList = CSLAddString(papszFileList, papszMetadataFiles[i]);
437 : }
438 : }
439 :
440 3 : return papszFileList;
441 : }
442 :
443 : /************************************************************************/
444 : /* GDALRegister_TIL() */
445 : /************************************************************************/
446 :
447 1686 : void GDALRegister_TIL()
448 :
449 : {
450 1686 : if (GDALGetDriverByName("TIL") != nullptr)
451 302 : return;
452 :
453 1384 : GDALDriver *poDriver = new GDALDriver();
454 :
455 1384 : poDriver->SetDescription("TIL");
456 1384 : poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
457 1384 : poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "EarthWatch .TIL");
458 1384 : poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/til.html");
459 1384 : poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
460 :
461 1384 : poDriver->pfnOpen = TILDataset::Open;
462 1384 : poDriver->pfnIdentify = TILDataset::Identify;
463 :
464 1384 : GetGDALDriverManager()->RegisterDriver(poDriver);
465 : }
|