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 * Permission is hereby granted, free of charge, to any person obtaining a
12 * copy of this software and associated documentation files (the "Software"),
13 * to deal in the Software without restriction, including without limitation
14 * the rights to use, copy, modify, merge, publish, distribute, sublicense,
15 * and/or sell copies of the Software, and to permit persons to whom the
16 * Software is furnished to do so, subject to the following conditions:
17 *
18 * The above copyright notice and this permission notice shall be included
19 * in all copies or substantial portions of the Software.
20 *
21 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
22 * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
23 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
24 * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
25 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
27 * DEALINGS IN THE SOFTWARE.
28 ****************************************************************************/
29
30 #include "cpl_multiproc.h"
31 #include "cpl_string.h"
32 #include "cplkeywordparser.h"
33 #include "gdal_mdreader.h"
34 #include "gdal_frmts.h"
35 #include "gdal_pam.h"
36 #include "gdal_proxy.h"
37 #include "ogr_spatialref.h"
38 #include "vrtdataset.h"
39
40 CPL_CVSID("$Id: tildataset.cpp a5d5ed208537a05de4437e97b6a09b7ba44f76c9 2020-03-24 08:27:48 +0100 Kai Pastor $")
41
42 /************************************************************************/
43 /* ==================================================================== */
44 /* TILDataset */
45 /* ==================================================================== */
46 /************************************************************************/
47
48 class TILDataset final : public GDALPamDataset
49 {
50 VRTDataset *poVRTDS;
51 std::vector<GDALDataset *> apoTileDS;
52
53 char **papszMetadataFiles;
54
55 protected:
56 virtual int CloseDependentDatasets() override;
57
58 public:
59 TILDataset();
60 virtual ~TILDataset();
61
62 virtual char **GetFileList(void) override;
63
64 static GDALDataset *Open( GDALOpenInfo * );
65 static int Identify( GDALOpenInfo *poOpenInfo );
66 };
67
68 /************************************************************************/
69 /* ==================================================================== */
70 /* TILRasterBand */
71 /* ==================================================================== */
72 /************************************************************************/
73
74 class TILRasterBand final: public GDALPamRasterBand
75 {
76 friend class TILDataset;
77
78 VRTSourcedRasterBand *poVRTBand;
79
80 public:
81 TILRasterBand( TILDataset *, int, VRTSourcedRasterBand * );
~TILRasterBand()82 virtual ~TILRasterBand() {}
83
84 virtual CPLErr IReadBlock( int, int, void * ) override;
85 virtual CPLErr IRasterIO( GDALRWFlag, int, int, int, int,
86 void *, int, int, GDALDataType,
87 GSpacing nPixelSpace, GSpacing nLineSpace,
88 GDALRasterIOExtraArg* psExtraArg ) override;
89 };
90
91 /************************************************************************/
92 /* TILRasterBand() */
93 /************************************************************************/
94
TILRasterBand(TILDataset * poTILDS,int nBandIn,VRTSourcedRasterBand * poVRTBandIn)95 TILRasterBand::TILRasterBand( TILDataset *poTILDS, int nBandIn,
96 VRTSourcedRasterBand *poVRTBandIn )
97
98 {
99 poDS = poTILDS;
100 poVRTBand = poVRTBandIn;
101 nBand = nBandIn;
102 eDataType = poVRTBandIn->GetRasterDataType();
103
104 poVRTBandIn->GetBlockSize( &nBlockXSize, &nBlockYSize );
105 }
106
107 /************************************************************************/
108 /* IReadBlock() */
109 /************************************************************************/
110
IReadBlock(int iBlockX,int iBlockY,void * pBuffer)111 CPLErr TILRasterBand::IReadBlock( int iBlockX, int iBlockY, void *pBuffer )
112
113 {
114 return poVRTBand->ReadBlock( iBlockX, iBlockY, pBuffer );
115 }
116
117 /************************************************************************/
118 /* IRasterIO() */
119 /************************************************************************/
120
IRasterIO(GDALRWFlag eRWFlag,int nXOff,int nYOff,int nXSize,int nYSize,void * pData,int nBufXSize,int nBufYSize,GDALDataType eBufType,GSpacing nPixelSpace,GSpacing nLineSpace,GDALRasterIOExtraArg * psExtraArg)121 CPLErr TILRasterBand::IRasterIO( GDALRWFlag eRWFlag,
122 int nXOff, int nYOff, int nXSize, int nYSize,
123 void * pData, int nBufXSize, int nBufYSize,
124 GDALDataType eBufType,
125 GSpacing nPixelSpace, GSpacing nLineSpace,
126 GDALRasterIOExtraArg* psExtraArg )
127
128 {
129 if(GetOverviewCount() > 0)
130 {
131 return GDALPamRasterBand::IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
132 pData, nBufXSize, nBufYSize, eBufType,
133 nPixelSpace, nLineSpace, psExtraArg );
134 }
135
136 // If not exist TIL overviews, try to use band source overviews.
137 return poVRTBand->IRasterIO( eRWFlag, nXOff, nYOff, nXSize, nYSize,
138 pData, nBufXSize, nBufYSize, eBufType,
139 nPixelSpace, nLineSpace, psExtraArg );
140 }
141
142 /************************************************************************/
143 /* ==================================================================== */
144 /* TILDataset */
145 /* ==================================================================== */
146 /************************************************************************/
147
148 /************************************************************************/
149 /* TILDataset() */
150 /************************************************************************/
151
TILDataset()152 TILDataset::TILDataset() :
153 poVRTDS(nullptr),
154 papszMetadataFiles(nullptr)
155 {}
156
157 /************************************************************************/
158 /* ~TILDataset() */
159 /************************************************************************/
160
~TILDataset()161 TILDataset::~TILDataset()
162
163 {
164 TILDataset::CloseDependentDatasets();
165 CSLDestroy(papszMetadataFiles);
166 }
167
168 /************************************************************************/
169 /* CloseDependentDatasets() */
170 /************************************************************************/
171
CloseDependentDatasets()172 int TILDataset::CloseDependentDatasets()
173 {
174 int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
175
176 if( poVRTDS )
177 {
178 bHasDroppedRef = TRUE;
179 delete poVRTDS;
180 poVRTDS = nullptr;
181 }
182
183 while( !apoTileDS.empty() )
184 {
185 GDALClose( (GDALDatasetH) apoTileDS.back() );
186 apoTileDS.pop_back();
187 }
188
189 return bHasDroppedRef;
190 }
191
192 /************************************************************************/
193 /* Identify() */
194 /************************************************************************/
195
Identify(GDALOpenInfo * poOpenInfo)196 int TILDataset::Identify( GDALOpenInfo *poOpenInfo )
197
198 {
199 if( poOpenInfo->nHeaderBytes < 200
200 || !EQUAL(CPLGetExtension(poOpenInfo->pszFilename),"TIL") )
201 return FALSE;
202
203 if( strstr((const char *) poOpenInfo->pabyHeader,"numTiles") == nullptr )
204 return FALSE;
205
206 return TRUE;
207 }
208
209 /************************************************************************/
210 /* Open() */
211 /************************************************************************/
212
Open(GDALOpenInfo * poOpenInfo)213 GDALDataset *TILDataset::Open( GDALOpenInfo * poOpenInfo )
214
215 {
216 if( !Identify( poOpenInfo ) || poOpenInfo->fpL == nullptr )
217 return nullptr;
218
219 /* -------------------------------------------------------------------- */
220 /* Confirm the requested access is supported. */
221 /* -------------------------------------------------------------------- */
222 if( poOpenInfo->eAccess == GA_Update )
223 {
224 CPLError( CE_Failure, CPLE_NotSupported,
225 "The TIL driver does not support update access to existing"
226 " datasets.\n" );
227 return nullptr;
228 }
229
230 CPLString osDirname = CPLGetDirname(poOpenInfo->pszFilename);
231
232 // get metadata reader
233
234 GDALMDReaderManager mdreadermanager;
235 GDALMDReaderBase* mdreader = mdreadermanager.GetReader(poOpenInfo->pszFilename,
236 poOpenInfo->GetSiblingFiles(), MDR_DG);
237
238 if(nullptr == mdreader)
239 {
240 CPLError( CE_Failure, CPLE_OpenFailed,
241 "Unable to open .TIL dataset due to missing metadata file." );
242 return nullptr;
243 }
244 /* -------------------------------------------------------------------- */
245 /* Try to find the corresponding .IMD file. */
246 /* -------------------------------------------------------------------- */
247 char **papszIMD = mdreader->GetMetadataDomain(MD_DOMAIN_IMD);
248
249 if( papszIMD == nullptr )
250 {
251 CPLError( CE_Failure, CPLE_OpenFailed,
252 "Unable to open .TIL dataset due to missing .IMD file." );
253 return nullptr;
254 }
255
256 if( CSLFetchNameValue( papszIMD, "numRows" ) == nullptr
257 || CSLFetchNameValue( papszIMD, "numColumns" ) == nullptr
258 || CSLFetchNameValue( papszIMD, "bitsPerPixel" ) == nullptr )
259 {
260 CPLError( CE_Failure, CPLE_OpenFailed,
261 "Missing a required field in the .IMD file." );
262 return nullptr;
263 }
264
265 /* -------------------------------------------------------------------- */
266 /* Try to load and parse the .TIL file. */
267 /* -------------------------------------------------------------------- */
268 VSILFILE *fp = poOpenInfo->fpL;
269 poOpenInfo->fpL = nullptr;
270
271 CPLKeywordParser oParser;
272
273 if( !oParser.Ingest( fp ) )
274 {
275 VSIFCloseL( fp );
276 return nullptr;
277 }
278
279 VSIFCloseL( fp );
280
281 char **papszTIL = oParser.GetAllKeywords();
282
283 /* -------------------------------------------------------------------- */
284 /* Create a corresponding GDALDataset. */
285 /* -------------------------------------------------------------------- */
286 TILDataset *poDS = new TILDataset();
287 poDS->papszMetadataFiles = mdreader->GetMetadataFiles();
288 mdreader->FillMetadata(&poDS->oMDMD);
289 poDS->nRasterXSize = atoi(CSLFetchNameValueDef(papszIMD,"numColumns","0"));
290 poDS->nRasterYSize = atoi(CSLFetchNameValueDef(papszIMD,"numRows","0"));
291 if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize))
292 {
293 delete poDS;
294 return nullptr;
295 }
296
297 /* -------------------------------------------------------------------- */
298 /* We need to open one of the images in order to establish */
299 /* details like the band count and types. */
300 /* -------------------------------------------------------------------- */
301 const char *pszFilename = CSLFetchNameValue( papszTIL, "TILE_1.filename" );
302 if( pszFilename == nullptr )
303 {
304 CPLError( CE_Failure, CPLE_AppDefined,
305 "Missing TILE_1.filename in .TIL file." );
306 delete poDS;
307 return nullptr;
308 }
309
310 // trim double quotes.
311 if( pszFilename[0] == '"' )
312 pszFilename++;
313 if( pszFilename[strlen(pszFilename)-1] == '"' )
314 const_cast<char *>( pszFilename )[strlen(pszFilename)-1] = '\0';
315
316 CPLString osFilename = CPLFormFilename(osDirname, pszFilename, nullptr);
317 GDALDataset *poTemplateDS = reinterpret_cast<GDALDataset *>(
318 GDALOpen( osFilename, GA_ReadOnly ) );
319 if( poTemplateDS == nullptr || poTemplateDS->GetRasterCount() == 0)
320 {
321 delete poDS;
322 if (poTemplateDS != nullptr)
323 GDALClose( poTemplateDS );
324 return nullptr;
325 }
326
327 GDALRasterBand *poTemplateBand = poTemplateDS->GetRasterBand(1);
328 const GDALDataType eDT = poTemplateBand->GetRasterDataType();
329 const int nBandCount = poTemplateDS->GetRasterCount();
330
331 //we suppose the first tile have the same projection as others (usually so)
332 CPLString pszProjection(poTemplateDS->GetProjectionRef());
333 if(!pszProjection.empty())
334 poDS->SetProjection(pszProjection);
335
336 //we suppose the first tile have the same GeoTransform as others (usually so)
337 double adfGeoTransform[6];
338 if( poTemplateDS->GetGeoTransform( adfGeoTransform ) == CE_None )
339 {
340 // According to https://www.digitalglobe.com/sites/default/files/ISD_External.pdf, ulx=originX and
341 // is "Easting of the center of the upper left pixel of the image."
342 adfGeoTransform[0] = CPLAtof(CSLFetchNameValueDef(papszIMD,"MAP_PROJECTED_PRODUCT.ULX","0")) - adfGeoTransform[1] / 2;
343 adfGeoTransform[3] = CPLAtof(CSLFetchNameValueDef(papszIMD,"MAP_PROJECTED_PRODUCT.ULY","0")) - adfGeoTransform[5] / 2;
344 poDS->SetGeoTransform(adfGeoTransform);
345 }
346
347 poTemplateBand = nullptr;
348 GDALClose( poTemplateDS );
349
350 /* -------------------------------------------------------------------- */
351 /* Create and initialize the corresponding VRT dataset used to */
352 /* manage the tiled data access. */
353 /* -------------------------------------------------------------------- */
354 poDS->poVRTDS = new VRTDataset(poDS->nRasterXSize,poDS->nRasterYSize);
355
356 for( int iBand = 0; iBand < nBandCount; iBand++ )
357 poDS->poVRTDS->AddBand( eDT, nullptr );
358
359 /* Don't try to write a VRT file */
360 poDS->poVRTDS->SetWritable(FALSE);
361
362 /* -------------------------------------------------------------------- */
363 /* Create band information objects. */
364 /* -------------------------------------------------------------------- */
365 for( int iBand = 1; iBand <= nBandCount; iBand++ )
366 poDS->SetBand(
367 iBand,
368 new TILRasterBand(
369 poDS,
370 iBand,
371 reinterpret_cast<VRTSourcedRasterBand *>(
372 poDS->poVRTDS->GetRasterBand(iBand) ) ) );
373
374 /* -------------------------------------------------------------------- */
375 /* Add tiles as sources for each band. */
376 /* -------------------------------------------------------------------- */
377 const int nTileCount = atoi(CSLFetchNameValueDef(papszTIL,"numTiles","0"));
378 int iTile = 0;
379
380 for( iTile = 1; iTile <= nTileCount; iTile++ )
381 {
382 CPLString osKey;
383 osKey.Printf( "TILE_%d.filename", iTile );
384 pszFilename = CSLFetchNameValue( papszTIL, osKey );
385 if( pszFilename == nullptr )
386 {
387 CPLError( CE_Failure, CPLE_AppDefined,
388 "Missing TILE_%d.filename in .TIL file.", iTile );
389 delete poDS;
390 return nullptr;
391 }
392
393 // trim double quotes.
394 if( pszFilename[0] == '"' )
395 pszFilename++;
396 if( pszFilename[strlen(pszFilename)-1] == '"' )
397 const_cast<char *>( pszFilename )[strlen(pszFilename)-1] = '\0';
398 osFilename = CPLFormFilename(osDirname, pszFilename, nullptr);
399
400 osKey.Printf( "TILE_%d.ULColOffset", iTile );
401 const int nULX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
402
403 osKey.Printf( "TILE_%d.ULRowOffset", iTile );
404 const int nULY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
405
406 osKey.Printf( "TILE_%d.LRColOffset", iTile );
407 const int nLRX = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
408
409 osKey.Printf( "TILE_%d.LRRowOffset", iTile );
410 const int nLRY = atoi(CSLFetchNameValueDef(papszTIL, osKey, "0"));
411
412 GDALDataset *poTileDS =
413 new GDALProxyPoolDataset( osFilename,
414 nLRX - nULX + 1, nLRY - nULY + 1 );
415 if( poTileDS == nullptr )
416 continue;
417
418 poDS->apoTileDS.push_back( poTileDS );
419
420 for( int iBand = 1; iBand <= nBandCount; iBand++ )
421 {
422 reinterpret_cast<GDALProxyPoolDataset *>( poTileDS )->
423 AddSrcBandDescription( eDT, nLRX - nULX + 1, 1 );
424
425 GDALRasterBand *poSrcBand = poTileDS->GetRasterBand(iBand);
426
427 VRTSourcedRasterBand *poVRTBand =
428 reinterpret_cast<VRTSourcedRasterBand *>(
429 poDS->poVRTDS->GetRasterBand(iBand) );
430
431 poVRTBand->AddSimpleSource( poSrcBand,
432 0, 0,
433 nLRX - nULX + 1, nLRY - nULY + 1,
434 nULX, nULY,
435 nLRX - nULX + 1, nLRY - nULY + 1 );
436 }
437 }
438
439 /* -------------------------------------------------------------------- */
440 /* Initialize any PAM information. */
441 /* -------------------------------------------------------------------- */
442 poDS->SetDescription( poOpenInfo->pszFilename );
443 poDS->TryLoadXML();
444
445 /* -------------------------------------------------------------------- */
446 /* Check for overviews. */
447 /* -------------------------------------------------------------------- */
448 poDS->oOvManager.Initialize( poDS, poOpenInfo->pszFilename );
449
450 return poDS;
451 }
452
453 /************************************************************************/
454 /* GetFileList() */
455 /************************************************************************/
456
GetFileList()457 char **TILDataset::GetFileList()
458
459 {
460 char **papszFileList = GDALPamDataset::GetFileList();
461
462 for( unsigned int i = 0; i < apoTileDS.size(); i++ )
463 papszFileList = CSLAddString( papszFileList,
464 apoTileDS[i]->GetDescription() );
465
466 if(nullptr != papszMetadataFiles)
467 {
468 for( int i = 0; papszMetadataFiles[i] != nullptr; i++ )
469 {
470 papszFileList = CSLAddString( papszFileList, papszMetadataFiles[i] );
471 }
472 }
473
474 return papszFileList;
475 }
476
477 /************************************************************************/
478 /* GDALRegister_TIL() */
479 /************************************************************************/
480
GDALRegister_TIL()481 void GDALRegister_TIL()
482
483 {
484 if( GDALGetDriverByName( "TIL" ) != nullptr )
485 return;
486
487 GDALDriver *poDriver = new GDALDriver();
488
489 poDriver->SetDescription( "TIL" );
490 poDriver->SetMetadataItem( GDAL_DCAP_RASTER, "YES" );
491 poDriver->SetMetadataItem( GDAL_DMD_LONGNAME, "EarthWatch .TIL" );
492 poDriver->SetMetadataItem( GDAL_DMD_HELPTOPIC, "drivers/raster/til.html" );
493 poDriver->SetMetadataItem( GDAL_DCAP_VIRTUALIO, "YES" );
494
495 poDriver->pfnOpen = TILDataset::Open;
496 poDriver->pfnIdentify = TILDataset::Identify;
497
498 GetGDALDriverManager()->RegisterDriver( poDriver );
499 }
500