LCOV - code coverage report
Current view: top level - frmts/raw - pauxdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 301 408 73.8 %
Date: 2024-05-03 15:49:35 Functions: 18 25 72.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PCI .aux Driver
       4             :  * Purpose:  Implementation of PAuxDataset
       5             :  * Author:   Frank Warmerdam, warmerdam@pobox.com
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 1999, Frank Warmerdam
       9             :  * Copyright (c) 2008-2010, 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_string.h"
      31             : #include "gdal_frmts.h"
      32             : #include "ogr_spatialref.h"
      33             : #include "rawdataset.h"
      34             : 
      35             : #include <cmath>
      36             : 
      37             : /************************************************************************/
      38             : /* ==================================================================== */
      39             : /*                              PAuxDataset                             */
      40             : /* ==================================================================== */
      41             : /************************************************************************/
      42             : 
      43             : class PAuxRasterBand;
      44             : 
      45             : class PAuxDataset final : public RawDataset
      46             : {
      47             :     friend class PAuxRasterBand;
      48             : 
      49             :     VSILFILE *fpImage;  // Image data file.
      50             : 
      51             :     int nGCPCount;
      52             :     GDAL_GCP *pasGCPList;
      53             :     OGRSpatialReference m_oGCPSRS{};
      54             : 
      55             :     void ScanForGCPs();
      56             :     static OGRSpatialReference PCI2SRS(const char *pszGeosys,
      57             :                                        const char *pszProjParams);
      58             : 
      59             :     OGRSpatialReference m_oSRS{};
      60             : 
      61             :     CPL_DISALLOW_COPY_ASSIGN(PAuxDataset)
      62             : 
      63             :     CPLErr Close() override;
      64             : 
      65             :   public:
      66             :     PAuxDataset();
      67             :     ~PAuxDataset() override;
      68             : 
      69             :     // TODO(schwehr): Why are these public?
      70             :     char *pszAuxFilename;
      71             :     char **papszAuxLines;
      72             :     int bAuxUpdated;
      73             : 
      74           0 :     const OGRSpatialReference *GetSpatialRef() const override
      75             :     {
      76           0 :         return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
      77             :     }
      78             : 
      79             :     CPLErr GetGeoTransform(double *) override;
      80             :     CPLErr SetGeoTransform(double *) override;
      81             : 
      82             :     int GetGCPCount() override;
      83             : 
      84           0 :     const OGRSpatialReference *GetGCPSpatialRef() const override
      85             :     {
      86           0 :         return m_oGCPSRS.IsEmpty() ? nullptr : &m_oGCPSRS;
      87             :     }
      88             : 
      89             :     const GDAL_GCP *GetGCPs() override;
      90             : 
      91             :     char **GetFileList() override;
      92             : 
      93             :     static GDALDataset *Open(GDALOpenInfo *);
      94             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
      95             :                                int nBandsIn, GDALDataType eType,
      96             :                                char **papszParamList);
      97             : };
      98             : 
      99             : /************************************************************************/
     100             : /* ==================================================================== */
     101             : /*                           PAuxRasterBand                             */
     102             : /* ==================================================================== */
     103             : /************************************************************************/
     104             : 
     105             : class PAuxRasterBand final : public RawRasterBand
     106             : {
     107             :     CPL_DISALLOW_COPY_ASSIGN(PAuxRasterBand)
     108             : 
     109             :   public:
     110             :     PAuxRasterBand(GDALDataset *poDS, int nBand, VSILFILE *fpRaw,
     111             :                    vsi_l_offset nImgOffset, int nPixelOffset, int nLineOffset,
     112             :                    GDALDataType eDataType, int bNativeOrder);
     113             : 
     114             :     ~PAuxRasterBand() override;
     115             : 
     116             :     double GetNoDataValue(int *pbSuccess = nullptr) override;
     117             :     CPLErr SetNoDataValue(double) override;
     118             : 
     119             :     GDALColorTable *GetColorTable() override;
     120             :     GDALColorInterp GetColorInterpretation() override;
     121             : 
     122             :     void SetDescription(const char *pszNewDescription) override;
     123             : };
     124             : 
     125             : /************************************************************************/
     126             : /*                           PAuxRasterBand()                           */
     127             : /************************************************************************/
     128             : 
     129          84 : PAuxRasterBand::PAuxRasterBand(GDALDataset *poDSIn, int nBandIn,
     130             :                                VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
     131             :                                int nPixelOffsetIn, int nLineOffsetIn,
     132          84 :                                GDALDataType eDataTypeIn, int bNativeOrderIn)
     133             :     : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
     134             :                     nLineOffsetIn, eDataTypeIn, bNativeOrderIn,
     135          84 :                     RawRasterBand::OwnFP::NO)
     136             : {
     137          84 :     PAuxDataset *poPDS = reinterpret_cast<PAuxDataset *>(poDS);
     138             : 
     139             :     /* -------------------------------------------------------------------- */
     140             :     /*      Does this channel have a description?                           */
     141             :     /* -------------------------------------------------------------------- */
     142          84 :     char szTarget[128] = {'\0'};
     143             : 
     144          84 :     snprintf(szTarget, sizeof(szTarget), "ChanDesc-%d", nBand);
     145          84 :     if (CSLFetchNameValue(poPDS->papszAuxLines, szTarget) != nullptr)
     146           0 :         GDALRasterBand::SetDescription(
     147           0 :             CSLFetchNameValue(poPDS->papszAuxLines, szTarget));
     148             : 
     149             :     /* -------------------------------------------------------------------- */
     150             :     /*      See if we have colors.  Currently we must have color zero,      */
     151             :     /*      but this should not really be a limitation.                     */
     152             :     /* -------------------------------------------------------------------- */
     153          84 :     snprintf(szTarget, sizeof(szTarget), "METADATA_IMG_%d_Class_%d_Color",
     154             :              nBand, 0);
     155          84 :     if (CSLFetchNameValue(poPDS->papszAuxLines, szTarget) != nullptr)
     156             :     {
     157           0 :         poCT = new GDALColorTable();
     158             : 
     159           0 :         for (int i = 0; i < 256; i++)
     160             :         {
     161           0 :             snprintf(szTarget, sizeof(szTarget),
     162             :                      "METADATA_IMG_%d_Class_%d_Color", nBand, i);
     163             :             const char *pszLine =
     164           0 :                 CSLFetchNameValue(poPDS->papszAuxLines, szTarget);
     165           0 :             while (pszLine && *pszLine == ' ')
     166           0 :                 pszLine++;
     167             : 
     168           0 :             int nRed = 0;
     169           0 :             int nGreen = 0;
     170           0 :             int nBlue = 0;
     171             :             // TODO(schwehr): Replace sscanf with something safe.
     172           0 :             if (pszLine != nullptr && STARTS_WITH_CI(pszLine, "(RGB:") &&
     173           0 :                 sscanf(pszLine + 5, "%d %d %d", &nRed, &nGreen, &nBlue) == 3)
     174             :             {
     175           0 :                 GDALColorEntry oColor = {static_cast<short>(nRed),
     176             :                                          static_cast<short>(nGreen),
     177           0 :                                          static_cast<short>(nBlue), 255};
     178             : 
     179           0 :                 poCT->SetColorEntry(i, &oColor);
     180             :             }
     181             :         }
     182             :     }
     183          84 : }
     184             : 
     185             : /************************************************************************/
     186             : /*                          ~PAuxRasterBand()                           */
     187             : /************************************************************************/
     188             : 
     189         168 : PAuxRasterBand::~PAuxRasterBand()
     190             : 
     191             : {
     192         168 : }
     193             : 
     194             : /************************************************************************/
     195             : /*                           GetNoDataValue()                           */
     196             : /************************************************************************/
     197             : 
     198           8 : double PAuxRasterBand::GetNoDataValue(int *pbSuccess)
     199             : 
     200             : {
     201           8 :     char szTarget[128] = {'\0'};
     202           8 :     snprintf(szTarget, sizeof(szTarget), "METADATA_IMG_%d_NO_DATA_VALUE",
     203             :              nBand);
     204             : 
     205           8 :     PAuxDataset *poPDS = reinterpret_cast<PAuxDataset *>(poDS);
     206           8 :     const char *pszLine = CSLFetchNameValue(poPDS->papszAuxLines, szTarget);
     207             : 
     208           8 :     if (pbSuccess != nullptr)
     209           8 :         *pbSuccess = (pszLine != nullptr);
     210             : 
     211           8 :     if (pszLine == nullptr)
     212           8 :         return -1.0e8;
     213             : 
     214           0 :     return CPLAtof(pszLine);
     215             : }
     216             : 
     217             : /************************************************************************/
     218             : /*                           SetNoDataValue()                           */
     219             : /************************************************************************/
     220             : 
     221           0 : CPLErr PAuxRasterBand::SetNoDataValue(double dfNewValue)
     222             : 
     223             : {
     224           0 :     if (GetAccess() == GA_ReadOnly)
     225             :     {
     226           0 :         CPLError(CE_Failure, CPLE_NoWriteAccess,
     227             :                  "Can't update readonly dataset.");
     228           0 :         return CE_Failure;
     229             :     }
     230             : 
     231           0 :     char szTarget[128] = {'\0'};
     232           0 :     char szValue[128] = {'\0'};
     233           0 :     snprintf(szTarget, sizeof(szTarget), "METADATA_IMG_%d_NO_DATA_VALUE",
     234             :              nBand);
     235           0 :     CPLsnprintf(szValue, sizeof(szValue), "%24.12f", dfNewValue);
     236             : 
     237           0 :     PAuxDataset *poPDS = reinterpret_cast<PAuxDataset *>(poDS);
     238           0 :     poPDS->papszAuxLines =
     239           0 :         CSLSetNameValue(poPDS->papszAuxLines, szTarget, szValue);
     240             : 
     241           0 :     poPDS->bAuxUpdated = TRUE;
     242             : 
     243           0 :     return CE_None;
     244             : }
     245             : 
     246             : /************************************************************************/
     247             : /*                           SetDescription()                           */
     248             : /*                                                                      */
     249             : /*      We override the set description so we can mark the auxfile      */
     250             : /*      info as changed.                                                */
     251             : /************************************************************************/
     252             : 
     253           0 : void PAuxRasterBand::SetDescription(const char *pszNewDescription)
     254             : 
     255             : {
     256           0 :     if (GetAccess() == GA_Update)
     257             :     {
     258           0 :         char szTarget[128] = {'\0'};
     259           0 :         snprintf(szTarget, sizeof(szTarget), "ChanDesc-%d", nBand);
     260             : 
     261           0 :         PAuxDataset *poPDS = reinterpret_cast<PAuxDataset *>(poDS);
     262           0 :         poPDS->papszAuxLines =
     263           0 :             CSLSetNameValue(poPDS->papszAuxLines, szTarget, pszNewDescription);
     264             : 
     265           0 :         poPDS->bAuxUpdated = TRUE;
     266             :     }
     267             : 
     268           0 :     GDALRasterBand::SetDescription(pszNewDescription);
     269           0 : }
     270             : 
     271             : /************************************************************************/
     272             : /*                           GetColorTable()                            */
     273             : /************************************************************************/
     274             : 
     275           0 : GDALColorTable *PAuxRasterBand::GetColorTable()
     276             : 
     277             : {
     278           0 :     return poCT;
     279             : }
     280             : 
     281             : /************************************************************************/
     282             : /*                       GetColorInterpretation()                       */
     283             : /************************************************************************/
     284             : 
     285           2 : GDALColorInterp PAuxRasterBand::GetColorInterpretation()
     286             : 
     287             : {
     288           2 :     if (poCT == nullptr)
     289           2 :         return GCI_Undefined;
     290             : 
     291           0 :     return GCI_PaletteIndex;
     292             : }
     293             : 
     294             : /************************************************************************/
     295             : /* ==================================================================== */
     296             : /*                              PAuxDataset                             */
     297             : /* ==================================================================== */
     298             : /************************************************************************/
     299             : 
     300             : /************************************************************************/
     301             : /*                            PAuxDataset()                             */
     302             : /************************************************************************/
     303             : 
     304          46 : PAuxDataset::PAuxDataset()
     305             :     : fpImage(nullptr), nGCPCount(0), pasGCPList(nullptr),
     306          46 :       pszAuxFilename(nullptr), papszAuxLines(nullptr), bAuxUpdated(FALSE)
     307             : {
     308          46 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     309          46 :     m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     310          46 : }
     311             : 
     312             : /************************************************************************/
     313             : /*                            ~PAuxDataset()                            */
     314             : /************************************************************************/
     315             : 
     316          92 : PAuxDataset::~PAuxDataset()
     317             : 
     318             : {
     319          46 :     PAuxDataset::Close();
     320          92 : }
     321             : 
     322             : /************************************************************************/
     323             : /*                              Close()                                 */
     324             : /************************************************************************/
     325             : 
     326          84 : CPLErr PAuxDataset::Close()
     327             : {
     328          84 :     CPLErr eErr = CE_None;
     329          84 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     330             :     {
     331          46 :         if (PAuxDataset::FlushCache(true) != CE_None)
     332           2 :             eErr = CE_Failure;
     333             : 
     334          46 :         if (fpImage != nullptr)
     335             :         {
     336          42 :             if (VSIFCloseL(fpImage) != 0)
     337             :             {
     338           0 :                 CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     339           0 :                 eErr = CE_Failure;
     340             :             }
     341             :         }
     342             : 
     343          46 :         if (bAuxUpdated)
     344             :         {
     345          21 :             CSLSetNameValueSeparator(papszAuxLines, ": ");
     346          21 :             CSLSave(papszAuxLines, pszAuxFilename);
     347             :         }
     348             : 
     349          46 :         GDALDeinitGCPs(nGCPCount, pasGCPList);
     350          46 :         CPLFree(pasGCPList);
     351             : 
     352          46 :         CPLFree(pszAuxFilename);
     353          46 :         CSLDestroy(papszAuxLines);
     354             : 
     355          46 :         if (GDALPamDataset::Close() != CE_None)
     356           0 :             eErr = CE_Failure;
     357             :     }
     358          84 :     return eErr;
     359             : }
     360             : 
     361             : /************************************************************************/
     362             : /*                            GetFileList()                             */
     363             : /************************************************************************/
     364             : 
     365           1 : char **PAuxDataset::GetFileList()
     366             : 
     367             : {
     368           1 :     char **papszFileList = RawDataset::GetFileList();
     369           1 :     papszFileList = CSLAddString(papszFileList, pszAuxFilename);
     370           1 :     return papszFileList;
     371             : }
     372             : 
     373             : /************************************************************************/
     374             : /*                              PCI2SRS()                               */
     375             : /*                                                                      */
     376             : /*      Convert PCI coordinate system to WKT.  For now this is very     */
     377             : /*      incomplete, but can be filled out in the future.                */
     378             : /************************************************************************/
     379             : 
     380           8 : OGRSpatialReference PAuxDataset::PCI2SRS(const char *pszGeosys,
     381             :                                          const char *pszProjParams)
     382             : 
     383             : {
     384           8 :     while (*pszGeosys == ' ')
     385           4 :         pszGeosys++;
     386             : 
     387             :     /* -------------------------------------------------------------------- */
     388             :     /*      Parse projection parameters array.                              */
     389             :     /* -------------------------------------------------------------------- */
     390           4 :     double adfProjParams[16] = {0.0};
     391             : 
     392           4 :     if (pszProjParams != nullptr)
     393             :     {
     394           4 :         char **papszTokens = CSLTokenizeString(pszProjParams);
     395             : 
     396           4 :         for (int i = 0;
     397          68 :              i < 16 && papszTokens != nullptr && papszTokens[i] != nullptr; i++)
     398          64 :             adfProjParams[i] = CPLAtof(papszTokens[i]);
     399             : 
     400           4 :         CSLDestroy(papszTokens);
     401             :     }
     402             : 
     403             :     /* -------------------------------------------------------------------- */
     404             :     /*      Convert to SRS.                                                 */
     405             :     /* -------------------------------------------------------------------- */
     406           4 :     OGRSpatialReference oSRS;
     407           4 :     oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     408           4 :     if (oSRS.importFromPCI(pszGeosys, nullptr, adfProjParams) != OGRERR_NONE)
     409             :     {
     410           0 :         oSRS.Clear();
     411             :     }
     412             : 
     413           8 :     return oSRS;
     414             : }
     415             : 
     416             : /************************************************************************/
     417             : /*                            ScanForGCPs()                             */
     418             : /************************************************************************/
     419             : 
     420          42 : void PAuxDataset::ScanForGCPs()
     421             : 
     422             : {
     423          42 :     const int MAX_GCP = 256;
     424             : 
     425          42 :     nGCPCount = 0;
     426          42 :     CPLAssert(pasGCPList == nullptr);
     427          42 :     pasGCPList =
     428          42 :         reinterpret_cast<GDAL_GCP *>(CPLCalloc(sizeof(GDAL_GCP), MAX_GCP));
     429             : 
     430             :     /* -------------------------------------------------------------------- */
     431             :     /*      Get the GCP coordinate system.                                  */
     432             :     /* -------------------------------------------------------------------- */
     433             :     const char *pszMapUnits =
     434          42 :         CSLFetchNameValue(papszAuxLines, "GCP_1_MapUnits");
     435             :     const char *pszProjParams =
     436          42 :         CSLFetchNameValue(papszAuxLines, "GCP_1_ProjParms");
     437             : 
     438          42 :     if (pszMapUnits != nullptr)
     439           0 :         m_oGCPSRS = PCI2SRS(pszMapUnits, pszProjParams);
     440             : 
     441             :     /* -------------------------------------------------------------------- */
     442             :     /*      Collect standalone GCPs.  They look like:                       */
     443             :     /*                                                                      */
     444             :     /*      GCP_1_n = row, col, x, y [,z [,"id"[, "desc"]]]                 */
     445             :     /* -------------------------------------------------------------------- */
     446          42 :     for (int i = 0; nGCPCount < MAX_GCP; i++)
     447             :     {
     448          42 :         char szName[50] = {'\0'};
     449          42 :         snprintf(szName, sizeof(szName), "GCP_1_%d", i + 1);
     450          42 :         if (CSLFetchNameValue(papszAuxLines, szName) == nullptr)
     451          42 :             break;
     452             : 
     453           0 :         char **papszTokens = CSLTokenizeStringComplex(
     454           0 :             CSLFetchNameValue(papszAuxLines, szName), " ", TRUE, FALSE);
     455             : 
     456           0 :         if (CSLCount(papszTokens) >= 4)
     457             :         {
     458           0 :             GDALInitGCPs(1, pasGCPList + nGCPCount);
     459             : 
     460           0 :             pasGCPList[nGCPCount].dfGCPX = CPLAtof(papszTokens[2]);
     461           0 :             pasGCPList[nGCPCount].dfGCPY = CPLAtof(papszTokens[3]);
     462           0 :             pasGCPList[nGCPCount].dfGCPPixel = CPLAtof(papszTokens[0]);
     463           0 :             pasGCPList[nGCPCount].dfGCPLine = CPLAtof(papszTokens[1]);
     464             : 
     465           0 :             if (CSLCount(papszTokens) > 4)
     466           0 :                 pasGCPList[nGCPCount].dfGCPZ = CPLAtof(papszTokens[4]);
     467             : 
     468           0 :             CPLFree(pasGCPList[nGCPCount].pszId);
     469           0 :             if (CSLCount(papszTokens) > 5)
     470             :             {
     471           0 :                 pasGCPList[nGCPCount].pszId = CPLStrdup(papszTokens[5]);
     472             :             }
     473             :             else
     474             :             {
     475           0 :                 snprintf(szName, sizeof(szName), "GCP_%d", i + 1);
     476           0 :                 pasGCPList[nGCPCount].pszId = CPLStrdup(szName);
     477             :             }
     478             : 
     479           0 :             if (CSLCount(papszTokens) > 6)
     480             :             {
     481           0 :                 CPLFree(pasGCPList[nGCPCount].pszInfo);
     482           0 :                 pasGCPList[nGCPCount].pszInfo = CPLStrdup(papszTokens[6]);
     483             :             }
     484             : 
     485           0 :             nGCPCount++;
     486             :         }
     487             : 
     488           0 :         CSLDestroy(papszTokens);
     489             :     }
     490          42 : }
     491             : 
     492             : /************************************************************************/
     493             : /*                            GetGCPCount()                             */
     494             : /************************************************************************/
     495             : 
     496           0 : int PAuxDataset::GetGCPCount()
     497             : 
     498             : {
     499           0 :     return nGCPCount;
     500             : }
     501             : 
     502             : /************************************************************************/
     503             : /*                               GetGCP()                               */
     504             : /************************************************************************/
     505             : 
     506           0 : const GDAL_GCP *PAuxDataset::GetGCPs()
     507             : 
     508             : {
     509           0 :     return pasGCPList;
     510             : }
     511             : 
     512             : /************************************************************************/
     513             : /*                          GetGeoTransform()                           */
     514             : /************************************************************************/
     515             : 
     516          12 : CPLErr PAuxDataset::GetGeoTransform(double *padfGeoTransform)
     517             : 
     518             : {
     519          12 :     if (CSLFetchNameValue(papszAuxLines, "UpLeftX") == nullptr ||
     520          12 :         CSLFetchNameValue(papszAuxLines, "UpLeftY") == nullptr ||
     521          36 :         CSLFetchNameValue(papszAuxLines, "LoRightX") == nullptr ||
     522          12 :         CSLFetchNameValue(papszAuxLines, "LoRightY") == nullptr)
     523             :     {
     524           0 :         padfGeoTransform[0] = 0.0;
     525           0 :         padfGeoTransform[1] = 1.0;
     526           0 :         padfGeoTransform[2] = 0.0;
     527           0 :         padfGeoTransform[3] = 0.0;
     528           0 :         padfGeoTransform[4] = 0.0;
     529           0 :         padfGeoTransform[5] = 1.0;
     530             : 
     531           0 :         return CE_Failure;
     532             :     }
     533             : 
     534             :     const double dfUpLeftX =
     535          12 :         CPLAtof(CSLFetchNameValue(papszAuxLines, "UpLeftX"));
     536             :     const double dfUpLeftY =
     537          12 :         CPLAtof(CSLFetchNameValue(papszAuxLines, "UpLeftY"));
     538             :     const double dfLoRightX =
     539          12 :         CPLAtof(CSLFetchNameValue(papszAuxLines, "LoRightX"));
     540             :     const double dfLoRightY =
     541          12 :         CPLAtof(CSLFetchNameValue(papszAuxLines, "LoRightY"));
     542             : 
     543          12 :     padfGeoTransform[0] = dfUpLeftX;
     544          12 :     padfGeoTransform[1] = (dfLoRightX - dfUpLeftX) / GetRasterXSize();
     545          12 :     padfGeoTransform[2] = 0.0;
     546          12 :     padfGeoTransform[3] = dfUpLeftY;
     547          12 :     padfGeoTransform[4] = 0.0;
     548          12 :     padfGeoTransform[5] = (dfLoRightY - dfUpLeftY) / GetRasterYSize();
     549             : 
     550          12 :     return CE_None;
     551             : }
     552             : 
     553             : /************************************************************************/
     554             : /*                          SetGeoTransform()                           */
     555             : /************************************************************************/
     556             : 
     557          21 : CPLErr PAuxDataset::SetGeoTransform(double *padfGeoTransform)
     558             : 
     559             : {
     560          21 :     char szUpLeftX[128] = {'\0'};
     561          21 :     char szUpLeftY[128] = {'\0'};
     562          21 :     char szLoRightX[128] = {'\0'};
     563          21 :     char szLoRightY[128] = {'\0'};
     564             : 
     565          40 :     if (std::abs(padfGeoTransform[0]) < 181 &&
     566          19 :         std::abs(padfGeoTransform[1]) < 1)
     567             :     {
     568          19 :         CPLsnprintf(szUpLeftX, sizeof(szUpLeftX), "%.12f", padfGeoTransform[0]);
     569          19 :         CPLsnprintf(szUpLeftY, sizeof(szUpLeftY), "%.12f", padfGeoTransform[3]);
     570          19 :         CPLsnprintf(szLoRightX, sizeof(szLoRightX), "%.12f",
     571          19 :                     padfGeoTransform[0] +
     572          19 :                         padfGeoTransform[1] * GetRasterXSize());
     573          19 :         CPLsnprintf(szLoRightY, sizeof(szLoRightY), "%.12f",
     574          19 :                     padfGeoTransform[3] +
     575          19 :                         padfGeoTransform[5] * GetRasterYSize());
     576             :     }
     577             :     else
     578             :     {
     579           2 :         CPLsnprintf(szUpLeftX, sizeof(szUpLeftX), "%.3f", padfGeoTransform[0]);
     580           2 :         CPLsnprintf(szUpLeftY, sizeof(szUpLeftY), "%.3f", padfGeoTransform[3]);
     581           2 :         CPLsnprintf(szLoRightX, sizeof(szLoRightX), "%.3f",
     582           2 :                     padfGeoTransform[0] +
     583           2 :                         padfGeoTransform[1] * GetRasterXSize());
     584           2 :         CPLsnprintf(szLoRightY, sizeof(szLoRightY), "%.3f",
     585           2 :                     padfGeoTransform[3] +
     586           2 :                         padfGeoTransform[5] * GetRasterYSize());
     587             :     }
     588             : 
     589          21 :     papszAuxLines = CSLSetNameValue(papszAuxLines, "UpLeftX", szUpLeftX);
     590          21 :     papszAuxLines = CSLSetNameValue(papszAuxLines, "UpLeftY", szUpLeftY);
     591          21 :     papszAuxLines = CSLSetNameValue(papszAuxLines, "LoRightX", szLoRightX);
     592          21 :     papszAuxLines = CSLSetNameValue(papszAuxLines, "LoRightY", szLoRightY);
     593             : 
     594          21 :     bAuxUpdated = TRUE;
     595             : 
     596          21 :     return CE_None;
     597             : }
     598             : 
     599             : /************************************************************************/
     600             : /*                                Open()                                */
     601             : /************************************************************************/
     602             : 
     603       29338 : GDALDataset *PAuxDataset::Open(GDALOpenInfo *poOpenInfo)
     604             : 
     605             : {
     606       29338 :     if (poOpenInfo->nHeaderBytes < 1)
     607       25934 :         return nullptr;
     608             : 
     609             :     /* -------------------------------------------------------------------- */
     610             :     /*      If this is an .aux file, fetch out and form the name of the     */
     611             :     /*      file it references.                                             */
     612             :     /* -------------------------------------------------------------------- */
     613             : 
     614        6809 :     CPLString osTarget = poOpenInfo->pszFilename;
     615             : 
     616        3407 :     if (EQUAL(CPLGetExtension(poOpenInfo->pszFilename), "aux") &&
     617           2 :         STARTS_WITH_CI(reinterpret_cast<char *>(poOpenInfo->pabyHeader),
     618             :                        "AuxilaryTarget: "))
     619             :     {
     620           2 :         const char *pszSrc =
     621           2 :             reinterpret_cast<const char *>(poOpenInfo->pabyHeader + 16);
     622             : 
     623           2 :         char szAuxTarget[1024] = {'\0'};
     624           2 :         for (int i = 0; i < static_cast<int>(sizeof(szAuxTarget)) - 1 &&
     625          24 :                         pszSrc[i] != 10 && pszSrc[i] != 13 && pszSrc[i] != '\0';
     626             :              i++)
     627             :         {
     628          22 :             szAuxTarget[i] = pszSrc[i];
     629             :         }
     630           2 :         szAuxTarget[sizeof(szAuxTarget) - 1] = '\0';
     631             : 
     632           2 :         const std::string osPath(CPLGetPath(poOpenInfo->pszFilename));
     633           2 :         osTarget = CPLFormFilename(osPath.c_str(), szAuxTarget, nullptr);
     634             :     }
     635             : 
     636             :     /* -------------------------------------------------------------------- */
     637             :     /*      Now we need to tear apart the filename to form a .aux           */
     638             :     /*      filename.                                                       */
     639             :     /* -------------------------------------------------------------------- */
     640        6810 :     CPLString osAuxFilename = CPLResetExtension(osTarget, "aux");
     641             : 
     642             :     /* -------------------------------------------------------------------- */
     643             :     /*      Do we have a .aux file?                                         */
     644             :     /* -------------------------------------------------------------------- */
     645        3405 :     char **papszSiblingFiles = poOpenInfo->GetSiblingFiles();
     646        6775 :     if (papszSiblingFiles != nullptr &&
     647        3370 :         CSLFindString(papszSiblingFiles, CPLGetFilename(osAuxFilename)) == -1)
     648             :     {
     649        3321 :         return nullptr;
     650             :     }
     651             : 
     652          84 :     VSILFILE *fp = VSIFOpenL(osAuxFilename, "r");
     653          84 :     if (fp == nullptr)
     654             :     {
     655          35 :         osAuxFilename = CPLResetExtension(osTarget, "AUX");
     656          35 :         fp = VSIFOpenL(osAuxFilename, "r");
     657             :     }
     658             : 
     659          84 :     if (fp == nullptr)
     660          35 :         return nullptr;
     661             : 
     662             :     /* -------------------------------------------------------------------- */
     663             :     /*      Is this file a PCI .aux file?  Check the first line for the     */
     664             :     /*      telltale AuxilaryTarget keyword.                                */
     665             :     /*                                                                      */
     666             :     /*      At this point we should be verifying that it refers to our      */
     667             :     /*      binary file, but that is a pretty involved test.                */
     668             :     /* -------------------------------------------------------------------- */
     669          49 :     CPLPushErrorHandler(CPLQuietErrorHandler);
     670          49 :     const char *pszLine = CPLReadLine2L(fp, 1024, nullptr);
     671          49 :     CPLPopErrorHandler();
     672             : 
     673          49 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     674             : 
     675          49 :     if (pszLine == nullptr || (!STARTS_WITH_CI(pszLine, "AuxilaryTarget") &&
     676           1 :                                !STARTS_WITH_CI(pszLine, "AuxiliaryTarget")))
     677             :     {
     678           3 :         CPLErrorReset();
     679           3 :         return nullptr;
     680             :     }
     681             : 
     682             :     /* -------------------------------------------------------------------- */
     683             :     /*      Create a corresponding GDALDataset.                             */
     684             :     /* -------------------------------------------------------------------- */
     685          92 :     auto poDS = std::make_unique<PAuxDataset>();
     686             : 
     687             :     /* -------------------------------------------------------------------- */
     688             :     /*      Load the .aux file into a string list suitable to be            */
     689             :     /*      searched with CSLFetchNameValue().                              */
     690             :     /* -------------------------------------------------------------------- */
     691          46 :     poDS->papszAuxLines = CSLLoad2(osAuxFilename, 1024, 1024, nullptr);
     692          46 :     poDS->pszAuxFilename = CPLStrdup(osAuxFilename);
     693             : 
     694             :     /* -------------------------------------------------------------------- */
     695             :     /*      Find the RawDefinition line to establish overall parameters.    */
     696             :     /* -------------------------------------------------------------------- */
     697          46 :     pszLine = CSLFetchNameValue(poDS->papszAuxLines, "RawDefinition");
     698             : 
     699             :     // It seems PCI now writes out .aux files without RawDefinition in
     700             :     // some cases.  See bug 947.
     701          46 :     if (pszLine == nullptr)
     702             :     {
     703           2 :         return nullptr;
     704             :     }
     705             : 
     706          88 :     const CPLStringList aosTokens(CSLTokenizeString(pszLine));
     707             : 
     708          44 :     if (aosTokens.size() < 3)
     709             :     {
     710           0 :         CPLError(CE_Failure, CPLE_AppDefined,
     711             :                  "RawDefinition missing or corrupt in %s.",
     712             :                  poOpenInfo->pszFilename);
     713           0 :         return nullptr;
     714             :     }
     715             : 
     716          44 :     poDS->nRasterXSize = atoi(aosTokens[0]);
     717          44 :     poDS->nRasterYSize = atoi(aosTokens[1]);
     718          44 :     int l_nBands = atoi(aosTokens[2]);
     719          44 :     poDS->eAccess = poOpenInfo->eAccess;
     720             : 
     721          88 :     if (!GDALCheckDatasetDimensions(poDS->nRasterXSize, poDS->nRasterYSize) ||
     722          44 :         !GDALCheckBandCount(l_nBands, FALSE))
     723             :     {
     724           2 :         return nullptr;
     725             :     }
     726             : 
     727             :     /* -------------------------------------------------------------------- */
     728             :     /*      Open the file.                                                  */
     729             :     /* -------------------------------------------------------------------- */
     730          42 :     if (poOpenInfo->eAccess == GA_Update)
     731             :     {
     732          25 :         poDS->fpImage = VSIFOpenL(osTarget, "rb+");
     733             : 
     734          25 :         if (poDS->fpImage == nullptr)
     735             :         {
     736           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     737             :                      "File %s is missing or read-only, check permissions.",
     738             :                      osTarget.c_str());
     739           0 :             return nullptr;
     740             :         }
     741             :     }
     742             :     else
     743             :     {
     744          17 :         poDS->fpImage = VSIFOpenL(osTarget, "rb");
     745             : 
     746          17 :         if (poDS->fpImage == nullptr)
     747             :         {
     748           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
     749             :                      "File %s is missing or unreadable.", osTarget.c_str());
     750           0 :             return nullptr;
     751             :         }
     752             :     }
     753             : 
     754             :     /* -------------------------------------------------------------------- */
     755             :     /*      Collect raw definitions of each channel and create              */
     756             :     /*      corresponding bands.                                            */
     757             :     /* -------------------------------------------------------------------- */
     758         130 :     for (int i = 0; i < l_nBands; i++)
     759             :     {
     760          88 :         char szDefnName[32] = {'\0'};
     761          88 :         snprintf(szDefnName, sizeof(szDefnName), "ChanDefinition-%d", i + 1);
     762             : 
     763          88 :         pszLine = CSLFetchNameValue(poDS->papszAuxLines, szDefnName);
     764          88 :         if (pszLine == nullptr)
     765             :         {
     766           4 :             continue;
     767             :         }
     768             : 
     769          84 :         const CPLStringList aosTokensBand(CSLTokenizeString(pszLine));
     770          84 :         if (aosTokensBand.size() < 4)
     771             :         {
     772             :             // Skip the band with broken description
     773           0 :             continue;
     774             :         }
     775             : 
     776          84 :         GDALDataType eType = GDT_Unknown;
     777          84 :         if (EQUAL(aosTokensBand[0], "16U"))
     778          17 :             eType = GDT_UInt16;
     779          67 :         else if (EQUAL(aosTokensBand[0], "16S"))
     780           9 :             eType = GDT_Int16;
     781          58 :         else if (EQUAL(aosTokensBand[0], "32R"))
     782           9 :             eType = GDT_Float32;
     783             :         else
     784          49 :             eType = GDT_Byte;
     785             : 
     786          84 :         bool bNative = true;
     787          84 :         if (CSLCount(aosTokensBand) > 4)
     788             :         {
     789             : #ifdef CPL_LSB
     790          84 :             bNative = EQUAL(aosTokensBand[4], "Swapped");
     791             : #else
     792             :             bNative = EQUAL(aosTokensBand[4], "Unswapped");
     793             : #endif
     794             :         }
     795             : 
     796          84 :         const vsi_l_offset nBandOffset = CPLScanUIntBig(
     797          84 :             aosTokensBand[1], static_cast<int>(strlen(aosTokensBand[1])));
     798          84 :         const int nPixelOffset = atoi(aosTokensBand[2]);
     799          84 :         const int nLineOffset = atoi(aosTokensBand[3]);
     800             : 
     801          84 :         if (nPixelOffset <= 0 || nLineOffset <= 0)
     802             :         {
     803             :             // Skip the band with broken offsets.
     804           0 :             continue;
     805             :         }
     806             : 
     807             :         auto poBand = std::make_unique<PAuxRasterBand>(
     808          84 :             poDS.get(), poDS->nBands + 1, poDS->fpImage, nBandOffset,
     809          84 :             nPixelOffset, nLineOffset, eType, bNative);
     810          84 :         if (!poBand->IsValid())
     811           0 :             return nullptr;
     812          84 :         poDS->SetBand(poDS->nBands + 1, std::move(poBand));
     813             :     }
     814             : 
     815             :     /* -------------------------------------------------------------------- */
     816             :     /*      Get the projection.                                             */
     817             :     /* -------------------------------------------------------------------- */
     818             :     const char *pszMapUnits =
     819          42 :         CSLFetchNameValue(poDS->papszAuxLines, "MapUnits");
     820             :     const char *pszProjParams =
     821          42 :         CSLFetchNameValue(poDS->papszAuxLines, "ProjParams");
     822             : 
     823          42 :     if (pszMapUnits != nullptr)
     824             :     {
     825           4 :         poDS->m_oSRS = PCI2SRS(pszMapUnits, pszProjParams);
     826             :     }
     827             : 
     828             :     /* -------------------------------------------------------------------- */
     829             :     /*      Initialize any PAM information.                                 */
     830             :     /* -------------------------------------------------------------------- */
     831          42 :     poDS->SetDescription(osTarget);
     832          42 :     poDS->TryLoadXML();
     833             : 
     834             :     /* -------------------------------------------------------------------- */
     835             :     /*      Check for overviews.                                            */
     836             :     /* -------------------------------------------------------------------- */
     837          42 :     poDS->oOvManager.Initialize(poDS.get(), osTarget);
     838             : 
     839          42 :     poDS->ScanForGCPs();
     840          42 :     poDS->bAuxUpdated = FALSE;
     841             : 
     842          42 :     return poDS.release();
     843             : }
     844             : 
     845             : /************************************************************************/
     846             : /*                               Create()                               */
     847             : /************************************************************************/
     848             : 
     849          62 : GDALDataset *PAuxDataset::Create(const char *pszFilename, int nXSize,
     850             :                                  int nYSize, int nBandsIn, GDALDataType eType,
     851             :                                  char **papszOptions)
     852             : 
     853             : {
     854          62 :     const char *pszInterleave = CSLFetchNameValue(papszOptions, "INTERLEAVE");
     855          62 :     if (pszInterleave == nullptr)
     856          62 :         pszInterleave = "BAND";
     857             : 
     858             :     /* -------------------------------------------------------------------- */
     859             :     /*      Verify input options.                                           */
     860             :     /* -------------------------------------------------------------------- */
     861          62 :     if (eType != GDT_Byte && eType != GDT_Float32 && eType != GDT_UInt16 &&
     862             :         eType != GDT_Int16)
     863             :     {
     864          27 :         CPLError(CE_Failure, CPLE_AppDefined,
     865             :                  "Attempt to create PCI .Aux labelled dataset with an illegal\n"
     866             :                  "data type (%s).\n",
     867             :                  GDALGetDataTypeName(eType));
     868             : 
     869          27 :         return nullptr;
     870             :     }
     871             : 
     872             :     /* -------------------------------------------------------------------- */
     873             :     /*      Sum the sizes of the band pixel types.                          */
     874             :     /* -------------------------------------------------------------------- */
     875          35 :     int nPixelSizeSum = 0;
     876             : 
     877          95 :     for (int iBand = 0; iBand < nBandsIn; iBand++)
     878          60 :         nPixelSizeSum += GDALGetDataTypeSizeBytes(eType);
     879             : 
     880             :     /* -------------------------------------------------------------------- */
     881             :     /*      Try to create the file.                                         */
     882             :     /* -------------------------------------------------------------------- */
     883          35 :     VSILFILE *fp = VSIFOpenL(pszFilename, "w");
     884          35 :     if (fp == nullptr)
     885             :     {
     886           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
     887             :                  "Attempt to create file `%s' failed.\n", pszFilename);
     888           3 :         return nullptr;
     889             :     }
     890             : 
     891             :     /* -------------------------------------------------------------------- */
     892             :     /*      Just write out a couple of bytes to establish the binary        */
     893             :     /*      file, and then close it.                                        */
     894             :     /* -------------------------------------------------------------------- */
     895          32 :     CPL_IGNORE_RET_VAL(VSIFWriteL("\0\0", 2, 1, fp));
     896          32 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     897             : 
     898             :     /* -------------------------------------------------------------------- */
     899             :     /*      Create the aux filename.                                        */
     900             :     /* -------------------------------------------------------------------- */
     901             :     char *pszAuxFilename =
     902          32 :         static_cast<char *>(CPLMalloc(strlen(pszFilename) + 5));
     903          32 :     strcpy(pszAuxFilename, pszFilename);
     904             : 
     905         975 :     for (int i = static_cast<int>(strlen(pszAuxFilename)) - 1; i > 0; i--)
     906             :     {
     907         945 :         if (pszAuxFilename[i] == '.')
     908             :         {
     909           2 :             pszAuxFilename[i] = '\0';
     910           2 :             break;
     911             :         }
     912             :     }
     913             : 
     914          32 :     strcat(pszAuxFilename, ".aux");
     915             : 
     916             :     /* -------------------------------------------------------------------- */
     917             :     /*      Open the file.                                                  */
     918             :     /* -------------------------------------------------------------------- */
     919          32 :     fp = VSIFOpenL(pszAuxFilename, "wt");
     920          32 :     if (fp == nullptr)
     921             :     {
     922           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     923             :                  "Attempt to create file `%s' failed.\n", pszAuxFilename);
     924           0 :         return nullptr;
     925             :     }
     926          32 :     CPLFree(pszAuxFilename);
     927             : 
     928             :     /* -------------------------------------------------------------------- */
     929             :     /*      We need to write out the original filename but without any      */
     930             :     /*      path components in the AuxilaryTarget line.  Do so now.         */
     931             :     /* -------------------------------------------------------------------- */
     932          32 :     int iStart = static_cast<int>(strlen(pszFilename)) - 1;
     933         273 :     while (iStart > 0 && pszFilename[iStart - 1] != '/' &&
     934         241 :            pszFilename[iStart - 1] != '\\')
     935         241 :         iStart--;
     936             : 
     937          32 :     CPL_IGNORE_RET_VAL(
     938          32 :         VSIFPrintfL(fp, "AuxilaryTarget: %s\n", pszFilename + iStart));
     939             : 
     940             :     /* -------------------------------------------------------------------- */
     941             :     /*      Write out the raw definition for the dataset as a whole.        */
     942             :     /* -------------------------------------------------------------------- */
     943          32 :     CPL_IGNORE_RET_VAL(
     944          32 :         VSIFPrintfL(fp, "RawDefinition: %d %d %d\n", nXSize, nYSize, nBandsIn));
     945             : 
     946             :     /* -------------------------------------------------------------------- */
     947             :     /*      Write out a definition for each band.  We always write band     */
     948             :     /*      sequential files for now as these are pretty efficiently        */
     949             :     /*      handled by GDAL.                                                */
     950             :     /* -------------------------------------------------------------------- */
     951          32 :     vsi_l_offset nImgOffset = 0;
     952             : 
     953          89 :     for (int iBand = 0; iBand < nBandsIn; iBand++)
     954             :     {
     955          57 :         int nPixelOffset = 0;
     956          57 :         int nLineOffset = 0;
     957          57 :         vsi_l_offset nNextImgOffset = 0;
     958             : 
     959             :         /* --------------------------------------------------------------------
     960             :          */
     961             :         /*      Establish our file layout based on supplied interleaving. */
     962             :         /* --------------------------------------------------------------------
     963             :          */
     964          57 :         if (EQUAL(pszInterleave, "LINE"))
     965             :         {
     966           0 :             nPixelOffset = GDALGetDataTypeSizeBytes(eType);
     967           0 :             nLineOffset = nXSize * nPixelSizeSum;
     968           0 :             nNextImgOffset =
     969           0 :                 nImgOffset + static_cast<vsi_l_offset>(nPixelOffset) * nXSize;
     970             :         }
     971          57 :         else if (EQUAL(pszInterleave, "PIXEL"))
     972             :         {
     973           0 :             nPixelOffset = nPixelSizeSum;
     974           0 :             nLineOffset = nXSize * nPixelOffset;
     975           0 :             nNextImgOffset = nImgOffset + GDALGetDataTypeSizeBytes(eType);
     976             :         }
     977             :         else /* default to band */
     978             :         {
     979          57 :             nPixelOffset = GDALGetDataTypeSize(eType) / 8;
     980          57 :             nLineOffset = nXSize * nPixelOffset;
     981          57 :             nNextImgOffset =
     982          57 :                 nImgOffset + nYSize * static_cast<vsi_l_offset>(nLineOffset);
     983             :         }
     984             : 
     985             :         /* --------------------------------------------------------------------
     986             :          */
     987             :         /*      Write out line indicating layout. */
     988             :         /* --------------------------------------------------------------------
     989             :          */
     990          57 :         const char *pszTypeName = nullptr;
     991          57 :         if (eType == GDT_Float32)
     992           5 :             pszTypeName = "32R";
     993          52 :         else if (eType == GDT_Int16)
     994           5 :             pszTypeName = "16S";
     995          47 :         else if (eType == GDT_UInt16)
     996           5 :             pszTypeName = "16U";
     997             :         else
     998          42 :             pszTypeName = "8U";
     999             : 
    1000          57 :         CPL_IGNORE_RET_VAL(VSIFPrintfL(
    1001             :             fp, "ChanDefinition-%d: %s " CPL_FRMT_GIB " %d %d %s\n", iBand + 1,
    1002             :             pszTypeName, static_cast<GIntBig>(nImgOffset), nPixelOffset,
    1003             :             nLineOffset,
    1004             : #ifdef CPL_LSB
    1005             :             "Swapped"
    1006             : #else
    1007             :             "Unswapped"
    1008             : #endif
    1009             :             ));
    1010             : 
    1011          57 :         nImgOffset = nNextImgOffset;
    1012             :     }
    1013             : 
    1014             :     /* -------------------------------------------------------------------- */
    1015             :     /*      Cleanup                                                         */
    1016             :     /* -------------------------------------------------------------------- */
    1017          32 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1018             : 
    1019          32 :     return static_cast<GDALDataset *>(GDALOpen(pszFilename, GA_Update));
    1020             : }
    1021             : 
    1022             : /************************************************************************/
    1023             : /*                             PAuxDelete()                             */
    1024             : /************************************************************************/
    1025             : 
    1026           6 : static CPLErr PAuxDelete(const char *pszBasename)
    1027             : 
    1028             : {
    1029           6 :     VSILFILE *fp = VSIFOpenL(CPLResetExtension(pszBasename, "aux"), "r");
    1030           6 :     if (fp == nullptr)
    1031             :     {
    1032           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1033             :                  "%s does not appear to be a PAux dataset: "
    1034             :                  "there is no .aux file.",
    1035             :                  pszBasename);
    1036           0 :         return CE_Failure;
    1037             :     }
    1038             : 
    1039           6 :     const char *pszLine = CPLReadLineL(fp);
    1040           6 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
    1041             : 
    1042           6 :     if (pszLine == nullptr || !STARTS_WITH_CI(pszLine, "AuxilaryTarget"))
    1043             :     {
    1044           0 :         CPLError(CE_Failure, CPLE_AppDefined,
    1045             :                  "%s does not appear to be a PAux dataset:"
    1046             :                  "the .aux file does not start with AuxilaryTarget",
    1047             :                  pszBasename);
    1048           0 :         return CE_Failure;
    1049             :     }
    1050             : 
    1051           6 :     if (VSIUnlink(pszBasename) != 0)
    1052             :     {
    1053           0 :         CPLError(CE_Failure, CPLE_AppDefined, "OS unlinking file %s.",
    1054             :                  pszBasename);
    1055           0 :         return CE_Failure;
    1056             :     }
    1057             : 
    1058           6 :     VSIUnlink(CPLResetExtension(pszBasename, "aux"));
    1059             : 
    1060           6 :     return CE_None;
    1061             : }
    1062             : 
    1063             : /************************************************************************/
    1064             : /*                         GDALRegister_PAux()                          */
    1065             : /************************************************************************/
    1066             : 
    1067        1511 : void GDALRegister_PAux()
    1068             : 
    1069             : {
    1070        1511 :     if (GDALGetDriverByName("PAux") != nullptr)
    1071         295 :         return;
    1072             : 
    1073        1216 :     GDALDriver *poDriver = new GDALDriver();
    1074             : 
    1075        1216 :     poDriver->SetDescription("PAux");
    1076        1216 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
    1077        1216 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "PCI .aux Labelled");
    1078        1216 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/paux.html");
    1079        1216 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
    1080        1216 :                               "Byte Int16 UInt16 Float32");
    1081        1216 :     poDriver->SetMetadataItem(
    1082             :         GDAL_DMD_CREATIONOPTIONLIST,
    1083             :         "<CreationOptionList>"
    1084             :         "   <Option name='INTERLEAVE' type='string-select' default='BAND'>"
    1085             :         "       <Value>BAND</Value>"
    1086             :         "       <Value>LINE</Value>"
    1087             :         "       <Value>PIXEL</Value>"
    1088             :         "   </Option>"
    1089        1216 :         "</CreationOptionList>");
    1090        1216 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
    1091             : 
    1092        1216 :     poDriver->pfnOpen = PAuxDataset::Open;
    1093        1216 :     poDriver->pfnCreate = PAuxDataset::Create;
    1094        1216 :     poDriver->pfnDelete = PAuxDelete;
    1095             : 
    1096        1216 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1097             : }

Generated by: LCOV version 1.14