LCOV - code coverage report
Current view: top level - frmts/til - tildataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 142 169 84.0 %
Date: 2025-09-10 17:48:50 Functions: 10 11 90.9 %

          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             : }

Generated by: LCOV version 1.14