LCOV - code coverage report
Current view: top level - frmts/aigrid - aigdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 191 400 47.8 %
Date: 2025-10-24 00:53:13 Functions: 16 22 72.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Arc/Info Binary Grid Driver
       4             :  * Purpose:  Implements GDAL interface to underlying library.
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      10             :  *
      11             :  * SPDX-License-Identifier: MIT
      12             :  ****************************************************************************/
      13             : 
      14             : #include "aigrid.h"
      15             : #include "avc.h"
      16             : #include "cpl_string.h"
      17             : #include "gdal_frmts.h"
      18             : #include "gdal_pam.h"
      19             : #include "gdal_colortable.h"
      20             : #include "gdal_driver.h"
      21             : #include "gdal_drivermanager.h"
      22             : #include "gdal_openinfo.h"
      23             : #include "gdal_cpp_functions.h"
      24             : #include "gdal_rat.h"
      25             : #include "ogr_spatialref.h"
      26             : 
      27             : #include <vector>
      28             : 
      29             : static CPLString OSR_GDS(char **papszNV, const char *pszField,
      30             :                          const char *pszDefaultValue);
      31             : 
      32             : /************************************************************************/
      33             : /* ==================================================================== */
      34             : /*                              AIGDataset                              */
      35             : /* ==================================================================== */
      36             : /************************************************************************/
      37             : 
      38             : class AIGRasterBand;
      39             : 
      40             : class AIGDataset final : public GDALPamDataset
      41             : {
      42             :     friend class AIGRasterBand;
      43             : 
      44             :     AIGInfo_t *psInfo;
      45             : 
      46             :     char **papszPrj;
      47             :     OGRSpatialReference m_oSRS{};
      48             : 
      49             :     GDALColorTable *poCT;
      50             :     bool bHasReadRat;
      51             : 
      52             :     void TranslateColorTable(const char *);
      53             : 
      54             :     void ReadRAT();
      55             :     GDALRasterAttributeTable *poRAT;
      56             : 
      57             :   public:
      58             :     AIGDataset();
      59             :     ~AIGDataset() override;
      60             : 
      61             :     static GDALDataset *Open(GDALOpenInfo *);
      62             : 
      63             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
      64             :     const OGRSpatialReference *GetSpatialRef() const override;
      65             :     char **GetFileList(void) override;
      66             : };
      67             : 
      68             : /************************************************************************/
      69             : /* ==================================================================== */
      70             : /*                            AIGRasterBand                             */
      71             : /* ==================================================================== */
      72             : /************************************************************************/
      73             : 
      74             : class AIGRasterBand final : public GDALPamRasterBand
      75             : 
      76             : {
      77             :     friend class AIGDataset;
      78             : 
      79             :   public:
      80             :     AIGRasterBand(AIGDataset *, int);
      81             : 
      82             :     CPLErr IReadBlock(int, int, void *) override;
      83             :     double GetMinimum(int *pbSuccess) override;
      84             :     double GetMaximum(int *pbSuccess) override;
      85             :     double GetNoDataValue(int *pbSuccess) override;
      86             : 
      87             :     GDALColorInterp GetColorInterpretation() override;
      88             :     GDALColorTable *GetColorTable() override;
      89             :     GDALRasterAttributeTable *GetDefaultRAT() override;
      90             : };
      91             : 
      92             : /************************************************************************/
      93             : /*                           AIGRasterBand()                            */
      94             : /************************************************************************/
      95             : 
      96           9 : AIGRasterBand::AIGRasterBand(AIGDataset *poDSIn, int nBandIn)
      97             : 
      98             : {
      99           9 :     poDS = poDSIn;
     100           9 :     nBand = nBandIn;
     101             : 
     102           9 :     nBlockXSize = poDSIn->psInfo->nBlockXSize;
     103           9 :     nBlockYSize = poDSIn->psInfo->nBlockYSize;
     104             : 
     105           9 :     if (poDSIn->psInfo->nCellType == AIG_CELLTYPE_INT &&
     106           9 :         poDSIn->psInfo->dfMin >= 0.0 && poDSIn->psInfo->dfMax <= 254.0)
     107             :     {
     108           9 :         eDataType = GDT_Byte;
     109             :     }
     110           0 :     else if (poDSIn->psInfo->nCellType == AIG_CELLTYPE_INT &&
     111           0 :              poDSIn->psInfo->dfMin >= -32767 && poDSIn->psInfo->dfMax <= 32767)
     112             :     {
     113           0 :         eDataType = GDT_Int16;
     114             :     }
     115           0 :     else if (poDSIn->psInfo->nCellType == AIG_CELLTYPE_INT)
     116             :     {
     117           0 :         eDataType = GDT_Int32;
     118             :     }
     119             :     else
     120             :     {
     121           0 :         eDataType = GDT_Float32;
     122             :     }
     123           9 : }
     124             : 
     125             : /************************************************************************/
     126             : /*                             IReadBlock()                             */
     127             : /************************************************************************/
     128             : 
     129           4 : CPLErr AIGRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff, void *pImage)
     130             : 
     131             : {
     132           4 :     AIGDataset *poODS = cpl::down_cast<AIGDataset *>(poDS);
     133             :     GInt32 *panGridRaster;
     134             : 
     135           4 :     if (poODS->psInfo->nCellType == AIG_CELLTYPE_INT)
     136             :     {
     137           4 :         panGridRaster = (GInt32 *)VSIMalloc3(4, nBlockXSize, nBlockYSize);
     138           8 :         if (panGridRaster == nullptr ||
     139           4 :             AIGReadTile(poODS->psInfo, nBlockXOff, nBlockYOff, panGridRaster) !=
     140             :                 CE_None)
     141             :         {
     142           2 :             CPLFree(panGridRaster);
     143           2 :             return CE_Failure;
     144             :         }
     145             : 
     146           2 :         if (eDataType == GDT_Byte)
     147             :         {
     148        2050 :             for (int i = 0; i < nBlockXSize * nBlockYSize; i++)
     149             :             {
     150        2048 :                 if (panGridRaster[i] == ESRI_GRID_NO_DATA)
     151        2042 :                     ((GByte *)pImage)[i] = 255;
     152             :                 else
     153           6 :                     ((GByte *)pImage)[i] = (GByte)panGridRaster[i];
     154             :             }
     155             :         }
     156           0 :         else if (eDataType == GDT_Int16)
     157             :         {
     158           0 :             for (int i = 0; i < nBlockXSize * nBlockYSize; i++)
     159             :             {
     160           0 :                 if (panGridRaster[i] == ESRI_GRID_NO_DATA)
     161           0 :                     ((GInt16 *)pImage)[i] = -32768;
     162             :                 else
     163           0 :                     ((GInt16 *)pImage)[i] = (GInt16)panGridRaster[i];
     164             :             }
     165             :         }
     166             :         else
     167             :         {
     168           0 :             for (int i = 0; i < nBlockXSize * nBlockYSize; i++)
     169           0 :                 ((GInt32 *)pImage)[i] = panGridRaster[i];
     170             :         }
     171             : 
     172           2 :         CPLFree(panGridRaster);
     173             : 
     174           2 :         return CE_None;
     175             :     }
     176             :     else
     177             :     {
     178           0 :         return AIGReadFloatTile(poODS->psInfo, nBlockXOff, nBlockYOff,
     179           0 :                                 (float *)pImage);
     180             :     }
     181             : }
     182             : 
     183             : /************************************************************************/
     184             : /*                           GetDefaultRAT()                            */
     185             : /************************************************************************/
     186             : 
     187           0 : GDALRasterAttributeTable *AIGRasterBand::GetDefaultRAT()
     188             : 
     189             : {
     190           0 :     AIGDataset *poODS = cpl::down_cast<AIGDataset *>(poDS);
     191             : 
     192             :     /* -------------------------------------------------------------------- */
     193             :     /*      Read info raster attribute table, if present.                   */
     194             :     /* -------------------------------------------------------------------- */
     195           0 :     if (!poODS->bHasReadRat)
     196             :     {
     197           0 :         poODS->ReadRAT();
     198           0 :         poODS->bHasReadRat = true;
     199             :     }
     200             : 
     201           0 :     if (poODS->poRAT)
     202           0 :         return poODS->poRAT;
     203             :     else
     204           0 :         return GDALPamRasterBand::GetDefaultRAT();
     205             : }
     206             : 
     207             : /************************************************************************/
     208             : /*                             GetMinimum()                             */
     209             : /************************************************************************/
     210             : 
     211           1 : double AIGRasterBand::GetMinimum(int *pbSuccess)
     212             : 
     213             : {
     214           1 :     AIGDataset *poODS = cpl::down_cast<AIGDataset *>(poDS);
     215             : 
     216           1 :     if (pbSuccess != nullptr)
     217           1 :         *pbSuccess = TRUE;
     218             : 
     219           1 :     return poODS->psInfo->dfMin;
     220             : }
     221             : 
     222             : /************************************************************************/
     223             : /*                             GetMaximum()                             */
     224             : /************************************************************************/
     225             : 
     226           1 : double AIGRasterBand::GetMaximum(int *pbSuccess)
     227             : 
     228             : {
     229           1 :     AIGDataset *poODS = cpl::down_cast<AIGDataset *>(poDS);
     230             : 
     231           1 :     if (pbSuccess != nullptr)
     232           1 :         *pbSuccess = TRUE;
     233             : 
     234           1 :     return poODS->psInfo->dfMax;
     235             : }
     236             : 
     237             : /************************************************************************/
     238             : /*                           GetNoDataValue()                           */
     239             : /************************************************************************/
     240             : 
     241           3 : double AIGRasterBand::GetNoDataValue(int *pbSuccess)
     242             : 
     243             : {
     244           3 :     if (pbSuccess != nullptr)
     245           3 :         *pbSuccess = TRUE;
     246             : 
     247           3 :     if (eDataType == GDT_Float32)
     248           0 :         return ESRI_GRID_FLOAT_NO_DATA;
     249             : 
     250           3 :     if (eDataType == GDT_Int16)
     251           0 :         return -32768;
     252             : 
     253           3 :     if (eDataType == GDT_Byte)
     254           3 :         return 255;
     255             : 
     256           0 :     return ESRI_GRID_NO_DATA;
     257             : }
     258             : 
     259             : /************************************************************************/
     260             : /*                       GetColorInterpretation()                       */
     261             : /************************************************************************/
     262             : 
     263           0 : GDALColorInterp AIGRasterBand::GetColorInterpretation()
     264             : 
     265             : {
     266           0 :     AIGDataset *poODS = cpl::down_cast<AIGDataset *>(poDS);
     267             : 
     268           0 :     if (poODS->poCT != nullptr)
     269           0 :         return GCI_PaletteIndex;
     270             : 
     271           0 :     return GDALPamRasterBand::GetColorInterpretation();
     272             : }
     273             : 
     274             : /************************************************************************/
     275             : /*                           GetColorTable()                            */
     276             : /************************************************************************/
     277             : 
     278           2 : GDALColorTable *AIGRasterBand::GetColorTable()
     279             : 
     280             : {
     281           2 :     AIGDataset *poODS = cpl::down_cast<AIGDataset *>(poDS);
     282             : 
     283           2 :     if (poODS->poCT != nullptr)
     284           2 :         return poODS->poCT;
     285             : 
     286           0 :     return GDALPamRasterBand::GetColorTable();
     287             : }
     288             : 
     289             : /************************************************************************/
     290             : /* ==================================================================== */
     291             : /*                            AIGDataset                               */
     292             : /* ==================================================================== */
     293             : /************************************************************************/
     294             : 
     295             : /************************************************************************/
     296             : /*                            AIGDataset()                            */
     297             : /************************************************************************/
     298             : 
     299           9 : AIGDataset::AIGDataset()
     300             :     : psInfo(nullptr), papszPrj(nullptr), poCT(nullptr), bHasReadRat(false),
     301           9 :       poRAT(nullptr)
     302             : {
     303           9 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     304           9 : }
     305             : 
     306             : /************************************************************************/
     307             : /*                           ~AIGDataset()                            */
     308             : /************************************************************************/
     309             : 
     310          18 : AIGDataset::~AIGDataset()
     311             : 
     312             : {
     313           9 :     FlushCache(true);
     314           9 :     CSLDestroy(papszPrj);
     315           9 :     if (psInfo != nullptr)
     316           9 :         AIGClose(psInfo);
     317             : 
     318           9 :     if (poCT != nullptr)
     319           7 :         delete poCT;
     320             : 
     321           9 :     if (poRAT != nullptr)
     322           0 :         delete poRAT;
     323          18 : }
     324             : 
     325             : /************************************************************************/
     326             : /*                            GetFileList()                             */
     327             : /************************************************************************/
     328             : 
     329           2 : char **AIGDataset::GetFileList()
     330             : 
     331             : {
     332           2 :     char **papszFileList = GDALPamDataset::GetFileList();
     333             : 
     334             :     // Add in all files in the cover directory.
     335           2 :     char **papszCoverFiles = VSIReadDir(GetDescription());
     336             : 
     337          21 :     for (int i = 0; papszCoverFiles != nullptr && papszCoverFiles[i] != nullptr;
     338             :          i++)
     339             :     {
     340          19 :         if (EQUAL(papszCoverFiles[i], ".") || EQUAL(papszCoverFiles[i], ".."))
     341           4 :             continue;
     342             : 
     343          15 :         papszFileList = CSLAddString(
     344             :             papszFileList,
     345          30 :             CPLFormFilenameSafe(GetDescription(), papszCoverFiles[i], nullptr)
     346             :                 .c_str());
     347             :     }
     348           2 :     CSLDestroy(papszCoverFiles);
     349             : 
     350           2 :     return papszFileList;
     351             : }
     352             : 
     353             : /************************************************************************/
     354             : /*                          AIGErrorHandlerVATOpen()                    */
     355             : /************************************************************************/
     356             : 
     357             : class AIGErrorDescription
     358             : {
     359             :   public:
     360             :     CPLErr eErr;
     361             :     CPLErrorNum no;
     362             :     std::string osMsg;
     363             : };
     364             : 
     365           0 : static void CPL_STDCALL AIGErrorHandlerVATOpen(CPLErr eErr, CPLErrorNum no,
     366             :                                                const char *msg)
     367             : {
     368             :     std::vector<AIGErrorDescription> *paoErrors =
     369           0 :         (std::vector<AIGErrorDescription> *)CPLGetErrorHandlerUserData();
     370           0 :     if (STARTS_WITH_CI(msg, "EOF encountered in") &&
     371           0 :         strstr(msg, "../info/arc.dir") != nullptr)
     372           0 :         return;
     373           0 :     if (STARTS_WITH_CI(msg, "Failed to open table "))
     374           0 :         return;
     375           0 :     AIGErrorDescription oError;
     376           0 :     oError.eErr = eErr;
     377           0 :     oError.no = no;
     378           0 :     oError.osMsg = msg;
     379           0 :     paoErrors->push_back(std::move(oError));
     380             : }
     381             : 
     382             : /************************************************************************/
     383             : /*                              ReadRAT()                               */
     384             : /************************************************************************/
     385             : 
     386           0 : void AIGDataset::ReadRAT()
     387             : 
     388             : {
     389             :     /* -------------------------------------------------------------------- */
     390             :     /*      Check if we have an associated info directory.  If not          */
     391             :     /*      return quietly.                                                 */
     392             :     /* -------------------------------------------------------------------- */
     393           0 :     CPLString osInfoPath, osTableName;
     394             :     VSIStatBufL sStatBuf;
     395             : 
     396           0 :     osInfoPath = psInfo->pszCoverName;
     397           0 :     osInfoPath += "/../info";
     398             : 
     399           0 :     if (VSIStatL(osInfoPath, &sStatBuf) != 0)
     400             :     {
     401           0 :         CPLDebug("AIG", "No associated info directory at: %s, skip RAT.",
     402             :                  osInfoPath.c_str());
     403           0 :         return;
     404             :     }
     405             : 
     406           0 :     osInfoPath += "/";
     407             : 
     408             :     /* -------------------------------------------------------------------- */
     409             :     /*      Attempt to open the VAT table associated with this coverage.    */
     410             :     /* -------------------------------------------------------------------- */
     411           0 :     osTableName = CPLGetFilename(psInfo->pszCoverName);
     412           0 :     osTableName += ".VAT";
     413             : 
     414             :     /* Turn off errors that can be triggered if the info has no VAT */
     415             :     /* table related with this coverage */
     416           0 :     std::vector<AIGErrorDescription> aoErrors;
     417           0 :     CPLPushErrorHandlerEx(AIGErrorHandlerVATOpen, &aoErrors);
     418             : 
     419           0 :     AVCBinFile *psFile = AVCBinReadOpen(
     420             :         osInfoPath, osTableName, AVCCoverTypeUnknown, AVCFileTABLE, nullptr);
     421           0 :     CPLPopErrorHandler();
     422             : 
     423             :     /* Emit other errors */
     424           0 :     std::vector<AIGErrorDescription>::const_iterator oIter;
     425           0 :     for (oIter = aoErrors.begin(); oIter != aoErrors.end(); ++oIter)
     426             :     {
     427           0 :         const AIGErrorDescription &oError = *oIter;
     428           0 :         CPLError(oError.eErr, oError.no, "%s", oError.osMsg.c_str());
     429             :     }
     430             : 
     431           0 :     CPLErrorReset();
     432           0 :     if (psFile == nullptr)
     433           0 :         return;
     434             : 
     435           0 :     AVCTableDef *psTableDef = psFile->hdr.psTableDef;
     436             : 
     437             :     /* -------------------------------------------------------------------- */
     438             :     /*      Setup columns in corresponding RAT.                             */
     439             :     /* -------------------------------------------------------------------- */
     440           0 :     poRAT = new GDALDefaultRasterAttributeTable();
     441             : 
     442           0 :     for (int iField = 0; iField < psTableDef->numFields; iField++)
     443             :     {
     444           0 :         AVCFieldInfo *psFDef = psTableDef->pasFieldDef + iField;
     445           0 :         GDALRATFieldUsage eFUsage = GFU_Generic;
     446           0 :         GDALRATFieldType eFType = GFT_String;
     447             : 
     448           0 :         CPLString osFName = psFDef->szName;
     449           0 :         osFName.Trim();
     450             : 
     451           0 :         if (EQUAL(osFName, "VALUE"))
     452           0 :             eFUsage = GFU_MinMax;
     453           0 :         else if (EQUAL(osFName, "COUNT"))
     454           0 :             eFUsage = GFU_PixelCount;
     455             : 
     456           0 :         if (psFDef->nType1 * 10 == AVC_FT_BININT)
     457           0 :             eFType = GFT_Integer;
     458           0 :         else if (psFDef->nType1 * 10 == AVC_FT_BINFLOAT)
     459           0 :             eFType = GFT_Real;
     460             : 
     461           0 :         poRAT->CreateColumn(osFName, eFType, eFUsage);
     462             :     }
     463             : 
     464             :     /* -------------------------------------------------------------------- */
     465             :     /*      Process all records into RAT.                                   */
     466             :     /* -------------------------------------------------------------------- */
     467           0 :     AVCField *pasFields = nullptr;
     468           0 :     int iRecord = 0;
     469             : 
     470           0 :     while ((pasFields = AVCBinReadNextTableRec(psFile)) != nullptr)
     471             :     {
     472           0 :         iRecord++;
     473             : 
     474           0 :         for (int iField = 0; iField < psTableDef->numFields; iField++)
     475             :         {
     476           0 :             switch (psTableDef->pasFieldDef[iField].nType1 * 10)
     477             :             {
     478           0 :                 case AVC_FT_DATE:
     479             :                 case AVC_FT_FIXINT:
     480             :                 case AVC_FT_CHAR:
     481             :                 case AVC_FT_FIXNUM:
     482             :                 {
     483             :                     // XXX - I bet mloskot would like to see const_cast +
     484             :                     // static_cast :-)
     485           0 :                     const char *pszTmp =
     486           0 :                         (const char *)(pasFields[iField].pszStr);
     487           0 :                     CPLString osStrValue(pszTmp);
     488           0 :                     poRAT->SetValue(iRecord - 1, iField,
     489           0 :                                     osStrValue.Trim().c_str());
     490             :                 }
     491           0 :                 break;
     492             : 
     493           0 :                 case AVC_FT_BININT:
     494           0 :                     if (psTableDef->pasFieldDef[iField].nSize == 4)
     495           0 :                         poRAT->SetValue(iRecord - 1, iField,
     496           0 :                                         pasFields[iField].nInt32);
     497             :                     else
     498           0 :                         poRAT->SetValue(iRecord - 1, iField,
     499           0 :                                         pasFields[iField].nInt16);
     500           0 :                     break;
     501             : 
     502           0 :                 case AVC_FT_BINFLOAT:
     503           0 :                     if (psTableDef->pasFieldDef[iField].nSize == 4)
     504           0 :                         poRAT->SetValue(iRecord - 1, iField,
     505           0 :                                         pasFields[iField].fFloat);
     506             :                     else
     507           0 :                         poRAT->SetValue(iRecord - 1, iField,
     508           0 :                                         pasFields[iField].dDouble);
     509           0 :                     break;
     510             :             }
     511             :         }
     512             :     }
     513             : 
     514             :     /* -------------------------------------------------------------------- */
     515             :     /*      Cleanup                                                         */
     516             :     /* -------------------------------------------------------------------- */
     517             : 
     518           0 :     AVCBinReadClose(psFile);
     519             : 
     520             :     /* Workaround against #2447 and #3031, to avoid binding languages */
     521             :     /* not being able to open the dataset */
     522           0 :     CPLErrorReset();
     523             : }
     524             : 
     525             : /************************************************************************/
     526             : /*                                Open()                                */
     527             : /************************************************************************/
     528             : 
     529       39370 : GDALDataset *AIGDataset::Open(GDALOpenInfo *poOpenInfo)
     530             : 
     531             : {
     532             :     /* -------------------------------------------------------------------- */
     533             :     /*      If the pass name ends in .adf assume a file within the          */
     534             :     /*      coverage has been selected, and strip that off the coverage     */
     535             :     /*      name.                                                           */
     536             :     /* -------------------------------------------------------------------- */
     537       78740 :     CPLString osCoverName;
     538             : 
     539       39370 :     osCoverName = poOpenInfo->pszFilename;
     540       77947 :     if (osCoverName.size() > 4 &&
     541       38579 :         EQUAL(osCoverName.c_str() + osCoverName.size() - 4, ".adf"))
     542             :     {
     543           1 :         osCoverName = CPLGetDirnameSafe(poOpenInfo->pszFilename);
     544           1 :         if (osCoverName == "")
     545           0 :             osCoverName = ".";
     546             :     }
     547             : 
     548             :     /* -------------------------------------------------------------------- */
     549             :     /*      Otherwise verify we were already given a directory.             */
     550             :     /* -------------------------------------------------------------------- */
     551       39368 :     else if (!poOpenInfo->bIsDirectory)
     552             :     {
     553       38867 :         return nullptr;
     554             :     }
     555             : 
     556             :     /* -------------------------------------------------------------------- */
     557             :     /*      Verify that a few of the "standard" files are available.        */
     558             :     /* -------------------------------------------------------------------- */
     559             :     VSIStatBufL sStatBuf;
     560        1005 :     CPLString osTestName;
     561             : 
     562         503 :     osTestName.Printf("%s/hdr.adf", osCoverName.c_str());
     563         503 :     if (VSIStatL(osTestName, &sStatBuf) != 0)
     564             :     {
     565         497 :         osTestName.Printf("%s/HDR.ADF", osCoverName.c_str());
     566         497 :         if (VSIStatL(osTestName, &sStatBuf) != 0)
     567         494 :             return nullptr;
     568             :     }
     569             : 
     570             :     /* -------------------------------------------------------------------- */
     571             :     /*      Confirm we have at least one raster data file.  These can be    */
     572             :     /*      sparse so we don't require particular ones to exists but if     */
     573             :     /*      there are none this is likely not a grid.                       */
     574             :     /* -------------------------------------------------------------------- */
     575           9 :     char **papszFileList = VSIReadDir(osCoverName);
     576           9 :     bool bGotOne = false;
     577             : 
     578           9 :     if (papszFileList == nullptr)
     579             :     {
     580             :         /* Useful when reading from /vsicurl/ on servers that don't */
     581             :         /* return a file list */
     582             :         /* such as
     583             :          * /vsicurl/http://eros.usgs.gov/archive/nslrsda/GeoTowns/NLCD/89110458
     584             :          */
     585             :         do
     586             :         {
     587           0 :             osTestName.Printf("%s/W001001.ADF", osCoverName.c_str());
     588           0 :             if (VSIStatL(osTestName, &sStatBuf) == 0)
     589             :             {
     590           0 :                 bGotOne = true;
     591           0 :                 break;
     592             :             }
     593             : 
     594           0 :             osTestName.Printf("%s/w001001.adf", osCoverName.c_str());
     595           0 :             if (VSIStatL(osTestName, &sStatBuf) == 0)
     596             :             {
     597           0 :                 bGotOne = true;
     598           0 :                 break;
     599             :             }
     600             :         } while (false);
     601             :     }
     602             : 
     603          55 :     for (int iFile = 0; papszFileList != nullptr &&
     604          55 :                         papszFileList[iFile] != nullptr && !bGotOne;
     605             :          iFile++)
     606             :     {
     607          46 :         if (strlen(papszFileList[iFile]) != 11)
     608          37 :             continue;
     609             : 
     610             :         // looking for something like w001001.adf or z001013.adf
     611           9 :         if (papszFileList[iFile][0] != 'w' && papszFileList[iFile][0] != 'W' &&
     612           0 :             papszFileList[iFile][0] != 'z' && papszFileList[iFile][0] != 'Z')
     613           0 :             continue;
     614             : 
     615           9 :         if (!STARTS_WITH(papszFileList[iFile] + 1, "0010"))
     616           0 :             continue;
     617             : 
     618           9 :         if (!EQUAL(papszFileList[iFile] + 7, ".adf"))
     619           0 :             continue;
     620             : 
     621           9 :         bGotOne = true;
     622             :     }
     623           9 :     CSLDestroy(papszFileList);
     624             : 
     625           9 :     if (!bGotOne)
     626           0 :         return nullptr;
     627             : 
     628             :     /* -------------------------------------------------------------------- */
     629             :     /*      Open the file.                                                  */
     630             :     /* -------------------------------------------------------------------- */
     631           9 :     AIGInfo_t *psInfo = AIGOpen(osCoverName.c_str(), "r");
     632             : 
     633           9 :     if (psInfo == nullptr)
     634             :     {
     635           0 :         CPLErrorReset();
     636           0 :         return nullptr;
     637             :     }
     638             : 
     639             :     /* -------------------------------------------------------------------- */
     640             :     /*      Confirm the requested access is supported.                      */
     641             :     /* -------------------------------------------------------------------- */
     642           9 :     if (poOpenInfo->eAccess == GA_Update)
     643             :     {
     644           0 :         AIGClose(psInfo);
     645           0 :         ReportUpdateNotSupportedByDriver("AIG");
     646           0 :         return nullptr;
     647             :     }
     648             :     /* -------------------------------------------------------------------- */
     649             :     /*      Create a corresponding GDALDataset.                             */
     650             :     /* -------------------------------------------------------------------- */
     651           9 :     AIGDataset *poDS = new AIGDataset();
     652             : 
     653           9 :     poDS->psInfo = psInfo;
     654             : 
     655             :     /* -------------------------------------------------------------------- */
     656             :     /*      Try to read a color table (.clr).  It seems it is legal to      */
     657             :     /*      have more than one so we just use the first one found.          */
     658             :     /* -------------------------------------------------------------------- */
     659           9 :     char **papszFiles = VSIReadDir(psInfo->pszCoverName);
     660          18 :     CPLString osClrFilename;
     661          18 :     CPLString osCleanPath = CPLCleanTrailingSlashSafe(psInfo->pszCoverName);
     662             : 
     663             :     // first check for any .clr in coverage dir.
     664          47 :     for (int iFile = 0; papszFiles != nullptr && papszFiles[iFile] != nullptr;
     665             :          iFile++)
     666             :     {
     667          43 :         const std::string osExt = CPLGetExtensionSafe(papszFiles[iFile]);
     668          43 :         if (!EQUAL(osExt.c_str(), "clr") && !EQUAL(osExt.c_str(), "CLR"))
     669          38 :             continue;
     670             : 
     671           0 :         osClrFilename = CPLFormFilenameSafe(psInfo->pszCoverName,
     672           5 :                                             papszFiles[iFile], nullptr);
     673           5 :         break;
     674             :     }
     675             : 
     676           9 :     CSLDestroy(papszFiles);
     677             : 
     678             :     // Look in parent if we don't find a .clr in the coverage dir.
     679           9 :     if (osClrFilename.empty())
     680             :     {
     681           8 :         CPLString osTestClrFilename;
     682             :         osTestClrFilename.Printf("%s/../%s.clr", psInfo->pszCoverName,
     683           4 :                                  CPLGetFilename(osCleanPath));
     684             : 
     685           4 :         if (VSIStatL(osTestClrFilename, &sStatBuf) != 0)
     686             :         {
     687             :             osTestClrFilename.Printf("%s/../%s.CLR", psInfo->pszCoverName,
     688           4 :                                      CPLGetFilename(osCleanPath));
     689             : 
     690           4 :             if (!VSIStatL(osTestClrFilename, &sStatBuf))
     691           2 :                 osClrFilename = std::move(osTestClrFilename);
     692             :         }
     693             :         else
     694           0 :             osClrFilename = std::move(osTestClrFilename);
     695             :     }
     696             : 
     697           9 :     if (!osClrFilename.empty())
     698           7 :         poDS->TranslateColorTable(osClrFilename);
     699             : 
     700             :     /* -------------------------------------------------------------------- */
     701             :     /*      Establish raster info.                                          */
     702             :     /* -------------------------------------------------------------------- */
     703           9 :     poDS->nRasterXSize = psInfo->nPixels;
     704           9 :     poDS->nRasterYSize = psInfo->nLines;
     705           9 :     poDS->nBands = 1;
     706             : 
     707             :     /* -------------------------------------------------------------------- */
     708             :     /*      Create band information objects.                                */
     709             :     /* -------------------------------------------------------------------- */
     710           9 :     poDS->SetBand(1, new AIGRasterBand(poDS, 1));
     711             : 
     712             :     /* -------------------------------------------------------------------- */
     713             :     /*      Try to read projection file.                                    */
     714             :     /* -------------------------------------------------------------------- */
     715             :     const std::string osPrjFilename =
     716           9 :         CPLFormCIFilenameSafe(psInfo->pszCoverName, "prj", "adf");
     717           9 :     if (VSIStatL(osPrjFilename.c_str(), &sStatBuf) == 0)
     718             :     {
     719          18 :         OGRSpatialReference oSRS;
     720           9 :         oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     721             : 
     722           9 :         poDS->papszPrj = CSLLoad(osPrjFilename.c_str());
     723             : 
     724           9 :         if (oSRS.importFromESRI(poDS->papszPrj) == OGRERR_NONE)
     725             :         {
     726             :             // If geographic values are in seconds, we must transform.
     727             :             // Is there a code for minutes too?
     728          10 :             if (oSRS.IsGeographic() &&
     729          10 :                 EQUAL(OSR_GDS(poDS->papszPrj, "Units", ""), "DS"))
     730             :             {
     731           0 :                 psInfo->dfLLX /= 3600.0;
     732           0 :                 psInfo->dfURY /= 3600.0;
     733           0 :                 psInfo->dfCellSizeX /= 3600.0;
     734           0 :                 psInfo->dfCellSizeY /= 3600.0;
     735             :             }
     736             : 
     737           9 :             poDS->m_oSRS = std::move(oSRS);
     738             :         }
     739             :     }
     740             : 
     741             :     /* -------------------------------------------------------------------- */
     742             :     /*      Initialize any PAM information.                                 */
     743             :     /* -------------------------------------------------------------------- */
     744           9 :     poDS->SetDescription(psInfo->pszCoverName);
     745           9 :     poDS->TryLoadXML();
     746             : 
     747             :     /* -------------------------------------------------------------------- */
     748             :     /*      Open overviews.                                                 */
     749             :     /* -------------------------------------------------------------------- */
     750           9 :     poDS->oOvManager.Initialize(poDS, psInfo->pszCoverName);
     751             : 
     752           9 :     return poDS;
     753             : }
     754             : 
     755             : /************************************************************************/
     756             : /*                          GetGeoTransform()                           */
     757             : /************************************************************************/
     758             : 
     759           1 : CPLErr AIGDataset::GetGeoTransform(GDALGeoTransform &gt) const
     760             : 
     761             : {
     762           1 :     gt[0] = psInfo->dfLLX;
     763           1 :     gt[1] = psInfo->dfCellSizeX;
     764           1 :     gt[2] = 0;
     765             : 
     766           1 :     gt[3] = psInfo->dfURY;
     767           1 :     gt[4] = 0;
     768           1 :     gt[5] = -psInfo->dfCellSizeY;
     769             : 
     770           1 :     return CE_None;
     771             : }
     772             : 
     773             : /************************************************************************/
     774             : /*                          GetSpatialRef()                             */
     775             : /************************************************************************/
     776             : 
     777           1 : const OGRSpatialReference *AIGDataset::GetSpatialRef() const
     778             : 
     779             : {
     780           1 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     781             : }
     782             : 
     783             : /************************************************************************/
     784             : /*                        TranslateColorTable()                         */
     785             : /************************************************************************/
     786             : 
     787           7 : void AIGDataset::TranslateColorTable(const char *pszClrFilename)
     788             : 
     789             : {
     790           7 :     char **papszClrLines = CSLLoad(pszClrFilename);
     791           7 :     if (papszClrLines == nullptr)
     792           0 :         return;
     793             : 
     794           7 :     poCT = new GDALColorTable();
     795             : 
     796        1813 :     for (int iLine = 0; papszClrLines[iLine] != nullptr; iLine++)
     797             :     {
     798        1806 :         char **papszTokens = CSLTokenizeString(papszClrLines[iLine]);
     799             : 
     800        1806 :         if (CSLCount(papszTokens) >= 4 && papszTokens[0][0] != '#')
     801             :         {
     802             :             int nIndex;
     803             :             GDALColorEntry sEntry;
     804             : 
     805        1792 :             nIndex = atoi(papszTokens[0]);
     806        1792 :             sEntry.c1 = (short)atoi(papszTokens[1]);
     807        1792 :             sEntry.c2 = (short)atoi(papszTokens[2]);
     808        1792 :             sEntry.c3 = (short)atoi(papszTokens[3]);
     809        1792 :             sEntry.c4 = 255;
     810             : 
     811        1792 :             if ((nIndex < 0 || nIndex > 33000) ||
     812        1792 :                 (sEntry.c1 < 0 || sEntry.c1 > 255) ||
     813        1792 :                 (sEntry.c2 < 0 || sEntry.c2 > 255) ||
     814        1792 :                 (sEntry.c3 < 0 || sEntry.c3 > 255))
     815             :             {
     816           0 :                 CSLDestroy(papszTokens);
     817           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     818             :                          "Color table entry appears to be corrupt, skipping "
     819             :                          "the rest. ");
     820           0 :                 break;
     821             :             }
     822             : 
     823        1792 :             poCT->SetColorEntry(nIndex, &sEntry);
     824             :         }
     825             : 
     826        1806 :         CSLDestroy(papszTokens);
     827             :     }
     828             : 
     829           7 :     CSLDestroy(papszClrLines);
     830             : }
     831             : 
     832             : /************************************************************************/
     833             : /*                              OSR_GDS()                               */
     834             : /************************************************************************/
     835             : 
     836           1 : static CPLString OSR_GDS(char **papszNV, const char *pszField,
     837             :                          const char *pszDefaultValue)
     838             : 
     839             : {
     840           1 :     if (papszNV == nullptr || papszNV[0] == nullptr)
     841           0 :         return pszDefaultValue;
     842             : 
     843           1 :     int iLine = 0;
     844           4 :     for (; papszNV[iLine] != nullptr &&
     845           4 :            !EQUALN(papszNV[iLine], pszField, strlen(pszField));
     846             :          iLine++)
     847             :     {
     848             :     }
     849             : 
     850           1 :     if (papszNV[iLine] == nullptr)
     851           0 :         return pszDefaultValue;
     852             :     else
     853             :     {
     854           2 :         CPLString osResult;
     855           1 :         char **papszTokens = CSLTokenizeString(papszNV[iLine]);
     856             : 
     857           1 :         if (CSLCount(papszTokens) > 1)
     858           1 :             osResult = papszTokens[1];
     859             :         else
     860           0 :             osResult = pszDefaultValue;
     861             : 
     862           1 :         CSLDestroy(papszTokens);
     863           1 :         return osResult;
     864             :     }
     865             : }
     866             : 
     867             : /************************************************************************/
     868             : /*                             AIGRename()                              */
     869             : /*                                                                      */
     870             : /*      Custom renamer for AIG dataset.                                 */
     871             : /************************************************************************/
     872             : 
     873           0 : static CPLErr AIGRename(const char *pszNewName, const char *pszOldName)
     874             : 
     875             : {
     876             :     /* -------------------------------------------------------------------- */
     877             :     /*      Make sure we are talking about paths to the coverage            */
     878             :     /*      directory.                                                      */
     879             :     /* -------------------------------------------------------------------- */
     880           0 :     CPLString osOldPath, osNewPath;
     881             : 
     882           0 :     if (!CPLGetExtensionSafe(pszNewName).empty())
     883           0 :         osNewPath = CPLGetPathSafe(pszNewName);
     884             :     else
     885           0 :         osNewPath = pszNewName;
     886             : 
     887           0 :     if (!CPLGetExtensionSafe(pszOldName).empty())
     888           0 :         osOldPath = CPLGetPathSafe(pszOldName);
     889             :     else
     890           0 :         osOldPath = pszOldName;
     891             : 
     892             :     /* -------------------------------------------------------------------- */
     893             :     /*      Get file list.                                                  */
     894             :     /* -------------------------------------------------------------------- */
     895             : 
     896           0 :     GDALDatasetH hDS = GDALOpen(osOldPath, GA_ReadOnly);
     897           0 :     if (hDS == nullptr)
     898           0 :         return CE_Failure;
     899             : 
     900           0 :     char **papszFileList = GDALGetFileList(hDS);
     901           0 :     GDALClose(hDS);
     902             : 
     903           0 :     if (papszFileList == nullptr)
     904           0 :         return CE_Failure;
     905             : 
     906             :     /* -------------------------------------------------------------------- */
     907             :     /*      Work out the corresponding new names.                           */
     908             :     /* -------------------------------------------------------------------- */
     909           0 :     char **papszNewFileList = nullptr;
     910             : 
     911           0 :     for (int i = 0; papszFileList[i] != nullptr; i++)
     912             :     {
     913           0 :         CPLString osNewFilename;
     914             : 
     915           0 :         if (!EQUALN(papszFileList[i], osOldPath, osOldPath.size()))
     916             :         {
     917           0 :             CPLAssert(false);
     918             :             return CE_Failure;
     919             :         }
     920             : 
     921           0 :         osNewFilename = osNewPath + (papszFileList[i] + osOldPath.size());
     922             : 
     923           0 :         papszNewFileList = CSLAddString(papszNewFileList, osNewFilename);
     924             :     }
     925             : 
     926             :     /* -------------------------------------------------------------------- */
     927             :     /*      Try renaming the directory.                                     */
     928             :     /* -------------------------------------------------------------------- */
     929           0 :     if (VSIRename(osNewPath, osOldPath) != 0)
     930             :     {
     931           0 :         if (VSIMkdir(osNewPath, 0777) != 0)
     932             :         {
     933           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     934             :                      "Unable to create directory %s:\n%s", osNewPath.c_str(),
     935           0 :                      VSIStrerror(errno));
     936           0 :             CSLDestroy(papszNewFileList);
     937           0 :             return CE_Failure;
     938             :         }
     939             :     }
     940             : 
     941             :     /* -------------------------------------------------------------------- */
     942             :     /*      Copy/rename any remaining files.                                */
     943             :     /* -------------------------------------------------------------------- */
     944             :     VSIStatBufL sStatBuf;
     945             : 
     946           0 :     for (int i = 0; papszFileList[i] != nullptr; i++)
     947             :     {
     948           0 :         if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
     949           0 :             VSI_ISREG(sStatBuf.st_mode))
     950             :         {
     951           0 :             if (CPLMoveFile(papszNewFileList[i], papszFileList[i]) != 0)
     952             :             {
     953           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     954           0 :                          "Unable to move %s to %s:\n%s", papszFileList[i],
     955           0 :                          papszNewFileList[i], VSIStrerror(errno));
     956           0 :                 CSLDestroy(papszNewFileList);
     957           0 :                 return CE_Failure;
     958             :             }
     959             :         }
     960             :     }
     961             : 
     962           0 :     if (VSIStatL(osOldPath, &sStatBuf) == 0)
     963             :     {
     964           0 :         if (CPLUnlinkTree(osOldPath) != 0)
     965             :         {
     966           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     967             :                      "Unable to cleanup old path.");
     968             :         }
     969             :     }
     970             : 
     971           0 :     CSLDestroy(papszFileList);
     972           0 :     CSLDestroy(papszNewFileList);
     973           0 :     return CE_None;
     974             : }
     975             : 
     976             : /************************************************************************/
     977             : /*                             AIGDelete()                              */
     978             : /*                                                                      */
     979             : /*      Custom dataset deleter for AIG dataset.                         */
     980             : /************************************************************************/
     981             : 
     982           0 : static CPLErr AIGDelete(const char *pszDatasetname)
     983             : 
     984             : {
     985             :     /* -------------------------------------------------------------------- */
     986             :     /*      Get file list.                                                  */
     987             :     /* -------------------------------------------------------------------- */
     988           0 :     GDALDatasetH hDS = GDALOpen(pszDatasetname, GA_ReadOnly);
     989           0 :     if (hDS == nullptr)
     990           0 :         return CE_Failure;
     991             : 
     992           0 :     char **papszFileList = GDALGetFileList(hDS);
     993           0 :     GDALClose(hDS);
     994             : 
     995           0 :     if (papszFileList == nullptr)
     996           0 :         return CE_Failure;
     997             : 
     998             :     /* -------------------------------------------------------------------- */
     999             :     /*      Delete all regular files.                                       */
    1000             :     /* -------------------------------------------------------------------- */
    1001           0 :     for (int i = 0; papszFileList[i] != nullptr; i++)
    1002             :     {
    1003             :         VSIStatBufL sStatBuf;
    1004           0 :         if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
    1005           0 :             VSI_ISREG(sStatBuf.st_mode))
    1006             :         {
    1007           0 :             if (VSIUnlink(papszFileList[i]) != 0)
    1008             :             {
    1009           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1010           0 :                          "Unable to delete '%s':\n%s", papszFileList[i],
    1011           0 :                          VSIStrerror(errno));
    1012           0 :                 return CE_Failure;
    1013             :             }
    1014             :         }
    1015             :     }
    1016             : 
    1017             :     /* -------------------------------------------------------------------- */
    1018             :     /*      Delete directories.                                             */
    1019             :     /* -------------------------------------------------------------------- */
    1020           0 :     for (int i = 0; papszFileList[i] != nullptr; i++)
    1021             :     {
    1022             :         VSIStatBufL sStatBuf;
    1023           0 :         if (VSIStatL(papszFileList[i], &sStatBuf) == 0 &&
    1024           0 :             VSI_ISDIR(sStatBuf.st_mode))
    1025             :         {
    1026           0 :             if (CPLUnlinkTree(papszFileList[i]) != 0)
    1027           0 :                 return CE_Failure;
    1028             :         }
    1029             :     }
    1030             : 
    1031           0 :     return CE_None;
    1032             : }
    1033             : 
    1034             : /************************************************************************/
    1035             : /*                          GDALRegister_AIG()                          */
    1036             : /************************************************************************/
    1037             : 
    1038        2038 : void GDALRegister_AIGrid()
    1039             : 
    1040             : {
    1041        2038 :     if (GDALGetDriverByName("AIG") != nullptr)
    1042         283 :         return;
    1043             : 
    1044        1755 :     GDALDriver *poDriver = new GDALDriver();
    1045             : 
    1046        1755 :     poDriver->SetDescription("AIG");
    1047        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1048        1755 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "Arc/Info Binary Grid");
    1049        1755 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/aig.html");
    1050        1755 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1051             : 
    1052        1755 :     poDriver->pfnOpen = AIGDataset::Open;
    1053             : 
    1054        1755 :     poDriver->pfnRename = AIGRename;
    1055        1755 :     poDriver->pfnDelete = AIGDelete;
    1056             : 
    1057        1755 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1058             : }

Generated by: LCOV version 1.14