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