LCOV - code coverage report
Current view: top level - frmts/raw - iscedataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 310 360 86.1 %
Date: 2025-07-09 17:50:03 Functions: 15 15 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ISCE Raster Reader
       4             :  * Purpose:  Implementation of the ISCE raster reader
       5             :  * Author:   Matthieu Volat (ISTerre), matthieu.volat@ujf-grenoble.fr
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2015, Matthieu Volat <matthieu.volat@ujf-grenoble.fr>
       9             :  *
      10             :  * SPDX-License-Identifier: MIT
      11             :  ****************************************************************************/
      12             : 
      13             : #include "gdal_frmts.h"
      14             : #include "ogr_spatialref.h"
      15             : #include "rawdataset.h"
      16             : 
      17             : #include <algorithm>
      18             : 
      19             : static const char *const apszISCE2GDALDatatypes[] = {
      20             :     "BYTE:Byte",       "CHAR:Byte",        "SHORT:Int16",    "INT:Int32",
      21             :     "LONG:Int64",      "FLOAT:Float32",    "DOUBLE:Float64", "CBYTE:Unknown",
      22             :     "CCHAR:Unknown",   "CSHORT:CInt16",    "CINT:CInt32",    "CLONG:CInt64",
      23             :     "CFLOAT:CFloat32", "CDOUBLE:CFloat64", nullptr};
      24             : 
      25             : static const char *const apszGDAL2ISCEDatatypes[] = {
      26             :     "Byte:BYTE",     "Int16:SHORT",     "Int32:INT",        "Int64:LONG",
      27             :     "Float32:FLOAT", "Float64:DOUBLE",  "CInt16:CSHORT",    "CInt32:CINT",
      28             :     "CInt64:CLONG",  "CFloat32:CFLOAT", "CFloat64:CDOUBLE", nullptr};
      29             : 
      30             : enum Scheme
      31             : {
      32             :     BIL = 0,
      33             :     BIP = 1,
      34             :     BSQ = 2
      35             : };
      36             : 
      37             : static const char *const apszSchemeNames[] = {"BIL", "BIP", "BSQ", nullptr};
      38             : 
      39             : /************************************************************************/
      40             : /* ==================================================================== */
      41             : /*                              ISCEDataset                             */
      42             : /* ==================================================================== */
      43             : /************************************************************************/
      44             : 
      45             : class ISCERasterBand;
      46             : 
      47             : class ISCEDataset final : public RawDataset
      48             : {
      49             :     friend class ISCERasterBand;
      50             : 
      51             :     VSILFILE *fpImage;
      52             : 
      53             :     char *pszXMLFilename;
      54             : 
      55             :     enum Scheme eScheme;
      56             : 
      57             :     CPL_DISALLOW_COPY_ASSIGN(ISCEDataset)
      58             : 
      59             :     CPLErr Close() override;
      60             : 
      61             :   public:
      62             :     ISCEDataset();
      63             :     ~ISCEDataset() override;
      64             : 
      65             :     CPLErr FlushCache(bool bAtClosing) override;
      66             :     char **GetFileList() override;
      67             : 
      68             :     static int Identify(GDALOpenInfo *poOpenInfo);
      69             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo);
      70             :     static GDALDataset *Open(GDALOpenInfo *poOpenInfo, bool bFileSizeCheck);
      71             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
      72             :                                int nBandsIn, GDALDataType eType,
      73             :                                char **papszOptions);
      74             : };
      75             : 
      76             : /************************************************************************/
      77             : /* ==================================================================== */
      78             : /*                            ISCERasterBand                            */
      79             : /* ==================================================================== */
      80             : /************************************************************************/
      81             : 
      82         272 : class ISCERasterBand final : public RawRasterBand
      83             : {
      84             :     CPL_DISALLOW_COPY_ASSIGN(ISCERasterBand)
      85             : 
      86             :   public:
      87             :     ISCERasterBand(GDALDataset *poDS, int nBand, VSILFILE *fpRaw,
      88             :                    vsi_l_offset nImgOffset, int nPixelOffset, int nLineOffset,
      89             :                    GDALDataType eDataType, int bNativeOrder);
      90             : 
      91             :     ~ISCERasterBand() override;
      92             : };
      93             : 
      94             : /************************************************************************/
      95             : /*                           getXMLFilename()                           */
      96             : /************************************************************************/
      97             : 
      98       30841 : static CPLString getXMLFilename(GDALOpenInfo *poOpenInfo)
      99             : {
     100       61676 :     CPLString osXMLFilename;
     101             : 
     102       30835 :     if (poOpenInfo->fpL == nullptr)
     103       29382 :         return CPLString();
     104             : 
     105        1453 :     char **papszSiblingFiles = poOpenInfo->GetSiblingFiles();
     106        1451 :     if (papszSiblingFiles == nullptr)
     107             :     {
     108             :         osXMLFilename =
     109          17 :             CPLFormFilenameSafe(nullptr, poOpenInfo->pszFilename, "xml");
     110             :         VSIStatBufL psXMLStatBuf;
     111          34 :         CPLErrorStateBackuper oBackuper(CPLQuietErrorHandler);
     112          17 :         if (VSIStatL(osXMLFilename, &psXMLStatBuf) != 0)
     113             :         {
     114          17 :             osXMLFilename = "";
     115             :         }
     116             :     }
     117             :     else
     118             :     {
     119             :         /* ------------------------------------------------------------ */
     120             :         /*      We need to tear apart the filename to form a .xml       */
     121             :         /*      filename.                                               */
     122             :         /* ------------------------------------------------------------ */
     123        2868 :         const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
     124        2868 :         const CPLString osName = CPLGetFilename(poOpenInfo->pszFilename);
     125             : 
     126             :         const int iFile =
     127        1434 :             CSLFindString(papszSiblingFiles,
     128        2868 :                           CPLFormFilenameSafe(nullptr, osName, "xml").c_str());
     129        1434 :         if (iFile >= 0)
     130             :         {
     131             :             osXMLFilename =
     132         200 :                 CPLFormFilenameSafe(osPath, papszSiblingFiles[iFile], nullptr);
     133             :         }
     134             :     }
     135             : 
     136        1451 :     return osXMLFilename;
     137             : }
     138             : 
     139             : /************************************************************************/
     140             : /*                             ISCEDataset()                            */
     141             : /************************************************************************/
     142             : 
     143          70 : ISCEDataset::ISCEDataset()
     144          70 :     : fpImage(nullptr), pszXMLFilename(nullptr), eScheme(BIL)
     145             : {
     146          70 : }
     147             : 
     148             : /************************************************************************/
     149             : /*                            ~ISCEDataset()                            */
     150             : /************************************************************************/
     151             : 
     152         140 : ISCEDataset::~ISCEDataset()
     153             : 
     154             : {
     155          70 :     ISCEDataset::Close();
     156         140 : }
     157             : 
     158             : /************************************************************************/
     159             : /*                              Close()                                 */
     160             : /************************************************************************/
     161             : 
     162         140 : CPLErr ISCEDataset::Close()
     163             : {
     164         140 :     CPLErr eErr = CE_None;
     165         140 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     166             :     {
     167          70 :         if (ISCEDataset::FlushCache(true) != CE_None)
     168           0 :             eErr = CE_Failure;
     169             : 
     170          70 :         if (fpImage)
     171             :         {
     172          70 :             if (VSIFCloseL(fpImage) != 0)
     173             :             {
     174           0 :                 CPLError(CE_Failure, CPLE_FileIO, "I/O error");
     175           0 :                 eErr = CE_Failure;
     176             :             }
     177             :         }
     178          70 :         CPLFree(pszXMLFilename);
     179             : 
     180          70 :         if (GDALPamDataset::Close() != CE_None)
     181           0 :             eErr = CE_Failure;
     182             :     }
     183         140 :     return eErr;
     184             : }
     185             : 
     186             : /************************************************************************/
     187             : /*                            FlushCache()                              */
     188             : /************************************************************************/
     189             : 
     190          70 : CPLErr ISCEDataset::FlushCache(bool bAtClosing)
     191             : {
     192          70 :     CPLErr eErr = RawDataset::FlushCache(bAtClosing);
     193             : 
     194          70 :     GDALRasterBand *band = (GetRasterCount() > 0) ? GetRasterBand(1) : nullptr;
     195             : 
     196          70 :     if (eAccess == GA_ReadOnly || band == nullptr)
     197          32 :         return eErr;
     198             : 
     199             :     /* -------------------------------------------------------------------- */
     200             :     /*      Recreate a XML doc with the dataset information.                */
     201             :     /* -------------------------------------------------------------------- */
     202          38 :     char sBuf[64] = {'\0'};
     203          38 :     CPLXMLNode *psDocNode = CPLCreateXMLNode(nullptr, CXT_Element, "imageFile");
     204             : 
     205             :     CPLXMLNode *psTmpNode =
     206          38 :         CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     207          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "WIDTH");
     208          38 :     CPLsnprintf(sBuf, sizeof(sBuf), "%d", nRasterXSize);
     209          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     210             : 
     211          38 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     212          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "LENGTH");
     213          38 :     CPLsnprintf(sBuf, sizeof(sBuf), "%d", nRasterYSize);
     214          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     215             : 
     216          38 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     217          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "NUMBER_BANDS");
     218          38 :     CPLsnprintf(sBuf, sizeof(sBuf), "%d", nBands);
     219          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     220             : 
     221          38 :     const char *sType = GDALGetDataTypeName(band->GetRasterDataType());
     222          38 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     223          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "DATA_TYPE");
     224          38 :     CPLCreateXMLElementAndValue(
     225             :         psTmpNode, "value",
     226             :         CSLFetchNameValue(const_cast<char **>(apszGDAL2ISCEDatatypes), sType));
     227             : 
     228          38 :     const char *pszScheme = apszSchemeNames[eScheme];
     229          38 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     230          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "SCHEME");
     231          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", pszScheme);
     232             : 
     233          38 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     234          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "BYTE_ORDER");
     235             : #ifdef CPL_LSB
     236          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", "l");
     237             : #else
     238             :     CPLCreateXMLElementAndValue(psTmpNode, "value", "b");
     239             : #endif
     240             : 
     241          38 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     242          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "ACCESS_MODE");
     243          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", "read");
     244             : 
     245          38 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     246          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "FILE_NAME");
     247          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value",
     248          76 :                                 CPLGetBasenameSafe(pszXMLFilename).c_str());
     249             : 
     250             :     /* -------------------------------------------------------------------- */
     251             :     /*      Then, add the ISCE domain metadata.                             */
     252             :     /* -------------------------------------------------------------------- */
     253          38 :     char **papszISCEMetadata = GetMetadata("ISCE");
     254          38 :     for (int i = 0; i < CSLCount(papszISCEMetadata); i++)
     255             :     {
     256             :         /* Get the tokens from the metadata item */
     257             :         char **papszTokens =
     258           0 :             CSLTokenizeString2(papszISCEMetadata[i], "=",
     259             :                                CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES);
     260           0 :         if (CSLCount(papszTokens) != 2)
     261             :         {
     262           0 :             CPLDebug("ISCE",
     263             :                      "Line of header file could not be split at = into two"
     264             :                      " elements: %s",
     265           0 :                      papszISCEMetadata[i]);
     266           0 :             CSLDestroy(papszTokens);
     267           0 :             continue;
     268             :         }
     269             : 
     270             :         /* Don't write it out if it is one of the bits of metadata that is
     271             :          * written out elsewhere in this routine */
     272           0 :         if (EQUAL(papszTokens[0], "WIDTH") || EQUAL(papszTokens[0], "LENGTH") ||
     273           0 :             EQUAL(papszTokens[0], "NUMBER_BANDS") ||
     274           0 :             EQUAL(papszTokens[0], "DATA_TYPE") ||
     275           0 :             EQUAL(papszTokens[0], "SCHEME") ||
     276           0 :             EQUAL(papszTokens[0], "BYTE_ORDER"))
     277             :         {
     278           0 :             CSLDestroy(papszTokens);
     279           0 :             continue;
     280             :         }
     281             : 
     282           0 :         psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     283           0 :         CPLAddXMLAttributeAndValue(psTmpNode, "name", papszTokens[0]);
     284           0 :         CPLCreateXMLElementAndValue(psTmpNode, "value", papszTokens[1]);
     285             : 
     286           0 :         CSLDestroy(papszTokens);
     287             :     }
     288             : 
     289             :     /* -------------------------------------------------------------------- */
     290             :     /*      Create the "Coordinate" component elements, possibly with       */
     291             :     /*      georeferencing.                                                 */
     292             :     /* -------------------------------------------------------------------- */
     293             :     CPLXMLNode *psCoordinate1Node, *psCoordinate2Node;
     294             : 
     295             :     /* Coordinate 1 */
     296          38 :     psCoordinate1Node = CPLCreateXMLNode(psDocNode, CXT_Element, "component");
     297          38 :     CPLAddXMLAttributeAndValue(psCoordinate1Node, "name", "Coordinate1");
     298          38 :     CPLCreateXMLElementAndValue(psCoordinate1Node, "factorymodule",
     299             :                                 "isceobj.Image");
     300          38 :     CPLCreateXMLElementAndValue(psCoordinate1Node, "factoryname",
     301             :                                 "createCoordinate");
     302          38 :     CPLCreateXMLElementAndValue(psCoordinate1Node, "doc",
     303             :                                 "First coordinate of a 2D image (width).");
     304             :     /* Property name */
     305          38 :     psTmpNode = CPLCreateXMLNode(psCoordinate1Node, CXT_Element, "property");
     306          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "name");
     307          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", "ImageCoordinate_name");
     308             :     /* Property family */
     309          38 :     psTmpNode = CPLCreateXMLNode(psCoordinate1Node, CXT_Element, "property");
     310          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "family");
     311          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", "ImageCoordinate");
     312             :     /* Property size */
     313          38 :     CPLsnprintf(sBuf, sizeof(sBuf), "%d", nRasterXSize);
     314          38 :     psTmpNode = CPLCreateXMLNode(psCoordinate1Node, CXT_Element, "property");
     315          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "size");
     316          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     317             : 
     318             :     /* Coordinate 2 */
     319          38 :     psCoordinate2Node = CPLCreateXMLNode(psDocNode, CXT_Element, "component");
     320          38 :     CPLAddXMLAttributeAndValue(psCoordinate2Node, "name", "Coordinate2");
     321          38 :     CPLCreateXMLElementAndValue(psCoordinate2Node, "factorymodule",
     322             :                                 "isceobj.Image");
     323          38 :     CPLCreateXMLElementAndValue(psCoordinate2Node, "factoryname",
     324             :                                 "createCoordinate");
     325             :     /* Property name */
     326          38 :     psTmpNode = CPLCreateXMLNode(psCoordinate2Node, CXT_Element, "property");
     327          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "name");
     328          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", "ImageCoordinate_name");
     329             :     /* Property family */
     330          38 :     psTmpNode = CPLCreateXMLNode(psCoordinate2Node, CXT_Element, "property");
     331          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "family");
     332          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", "ImageCoordinate");
     333             :     /* Property size */
     334          38 :     CPLsnprintf(sBuf, sizeof(sBuf), "%d", nRasterYSize);
     335          38 :     psTmpNode = CPLCreateXMLNode(psCoordinate2Node, CXT_Element, "property");
     336          38 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "size");
     337          38 :     CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     338             : 
     339          38 :     GDALGeoTransform gt;
     340          38 :     if (GetGeoTransform(gt) == CE_None)
     341             :     {
     342          38 :         if (gt[2] != 0 || gt[4] != 0)
     343             :         {
     344           0 :             CPLError(CE_Warning, CPLE_AppDefined,
     345             :                      "ISCE format do not support geotransform with "
     346             :                      "rotation, discarding info.");
     347             :         }
     348             :         else
     349             :         {
     350          38 :             CPLsnprintf(sBuf, sizeof(sBuf), "%g", gt[0]);
     351             :             psTmpNode =
     352          38 :                 CPLCreateXMLNode(psCoordinate1Node, CXT_Element, "property");
     353          38 :             CPLAddXMLAttributeAndValue(psTmpNode, "name", "startingValue");
     354          38 :             CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     355             : 
     356          38 :             CPLsnprintf(sBuf, sizeof(sBuf), "%g", gt[1]);
     357             :             psTmpNode =
     358          38 :                 CPLCreateXMLNode(psCoordinate1Node, CXT_Element, "property");
     359          38 :             CPLAddXMLAttributeAndValue(psTmpNode, "name", "delta");
     360          38 :             CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     361             : 
     362          38 :             CPLsnprintf(sBuf, sizeof(sBuf), "%g", gt[3]);
     363             :             psTmpNode =
     364          38 :                 CPLCreateXMLNode(psCoordinate2Node, CXT_Element, "property");
     365          38 :             CPLAddXMLAttributeAndValue(psTmpNode, "name", "startingValue");
     366          38 :             CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     367             : 
     368          38 :             CPLsnprintf(sBuf, sizeof(sBuf), "%g", gt[5]);
     369             :             psTmpNode =
     370          38 :                 CPLCreateXMLNode(psCoordinate2Node, CXT_Element, "property");
     371          38 :             CPLAddXMLAttributeAndValue(psTmpNode, "name", "delta");
     372          38 :             CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     373             :         }
     374             :     }
     375             : 
     376             :     /* -------------------------------------------------------------------- */
     377             :     /*      Write the XML file.                                             */
     378             :     /* -------------------------------------------------------------------- */
     379          38 :     if (!CPLSerializeXMLTreeToFile(psDocNode, pszXMLFilename))
     380           0 :         eErr = CE_Failure;
     381             : 
     382             :     /* -------------------------------------------------------------------- */
     383             :     /*      Free the XML Doc.                                               */
     384             :     /* -------------------------------------------------------------------- */
     385          38 :     CPLDestroyXMLNode(psDocNode);
     386             : 
     387          38 :     return eErr;
     388             : }
     389             : 
     390             : /************************************************************************/
     391             : /*                            GetFileList()                             */
     392             : /************************************************************************/
     393             : 
     394           3 : char **ISCEDataset::GetFileList()
     395             : {
     396             :     /* Main data file, etc. */
     397           3 :     char **papszFileList = RawDataset::GetFileList();
     398             : 
     399             :     /* XML file. */
     400           3 :     papszFileList = CSLAddString(papszFileList, pszXMLFilename);
     401             : 
     402           3 :     return papszFileList;
     403             : }
     404             : 
     405             : /************************************************************************/
     406             : /*                             Identify()                               */
     407             : /************************************************************************/
     408             : 
     409       30730 : int ISCEDataset::Identify(GDALOpenInfo *poOpenInfo)
     410             : {
     411             :     /* -------------------------------------------------------------------- */
     412             :     /*      TODO: This function is unusable now:                            */
     413             :     /*          * we can't just check for the presence of a XML file        */
     414             :     /*          * we cannot parse it to check basic tree (Identify() is     */
     415             :     /*            supposed to be faster than this                           */
     416             :     /*          * we could read only a few bytes and strstr() for           */
     417             :     /*            "imageData", but what if a file is padded with comments   */
     418             :     /*            and/or whitespaces? it would still be legit, but the      */
     419             :     /*            driver would fail...                                      */
     420             :     /* -------------------------------------------------------------------- */
     421             :     /* -------------------------------------------------------------------- */
     422             :     /*      Check if there is a .xml file                                   */
     423             :     /* -------------------------------------------------------------------- */
     424       61464 :     CPLString osXMLFilename = getXMLFilename(poOpenInfo);
     425       30718 :     if (osXMLFilename.empty())
     426             :     {
     427       30624 :         return false;
     428             :     }
     429             : 
     430         110 :     return true;
     431             : }
     432             : 
     433             : /************************************************************************/
     434             : /*                                Open()                                */
     435             : /************************************************************************/
     436             : 
     437       30680 : GDALDataset *ISCEDataset::Open(GDALOpenInfo *poOpenInfo)
     438             : {
     439       30680 :     return Open(poOpenInfo, true);
     440             : }
     441             : 
     442       30738 : GDALDataset *ISCEDataset::Open(GDALOpenInfo *poOpenInfo, bool bFileSizeCheck)
     443             : {
     444             :     /* -------------------------------------------------------------------- */
     445             :     /*      Confirm that the header is compatible with a ISCE dataset.    */
     446             :     /* -------------------------------------------------------------------- */
     447       30738 :     if (!Identify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     448             :     {
     449       30625 :         return nullptr;
     450             :     }
     451             : 
     452             :     /* -------------------------------------------------------------------- */
     453             :     /*      Open and parse the .xml file                                    */
     454             :     /* -------------------------------------------------------------------- */
     455         202 :     const CPLString osXMLFilename = getXMLFilename(poOpenInfo);
     456         100 :     CPLXMLNode *psNode = CPLParseXMLFile(osXMLFilename);
     457         100 :     if (psNode == nullptr || CPLGetXMLNode(psNode, "=imageFile") == nullptr)
     458             :     {
     459          10 :         CPLDestroyXMLNode(psNode);
     460          10 :         return nullptr;
     461             :     }
     462          90 :     CPLXMLNode *psCur = CPLGetXMLNode(psNode, "=imageFile")->psChild;
     463         180 :     CPLStringList aosXmlProps;
     464         783 :     while (psCur != nullptr)
     465             :     {
     466         693 :         if (EQUAL(psCur->pszValue, "property"))
     467             :         {
     468             :             /* Top-level property */
     469         629 :             const char *pszName = CPLGetXMLValue(psCur, "name", nullptr);
     470         629 :             const char *pszValue = CPLGetXMLValue(psCur, "value", nullptr);
     471         629 :             if (pszName != nullptr && pszValue != nullptr)
     472             :             {
     473         611 :                 aosXmlProps.SetNameValue(pszName, pszValue);
     474             :             }
     475             :         }
     476          64 :         else if (EQUAL(psCur->pszValue, "component"))
     477             :         {
     478             :             /* "components" elements in ISCE store set of properties.   */
     479             :             /* For now, they are avoided as I am not sure the full      */
     480             :             /* scope of these. An exception is made for the ones named  */
     481             :             /* Coordinate1 and Coordinate2, because they may have the   */
     482             :             /* georeferencing information.                              */
     483          64 :             const char *pszCurName = CPLGetXMLValue(psCur, "name", nullptr);
     484          64 :             if (pszCurName != nullptr && (EQUAL(pszCurName, "Coordinate1") ||
     485          32 :                                           EQUAL(pszCurName, "Coordinate2")))
     486             :             {
     487             :                 /* We need two subproperties: startingValue and delta.  */
     488             :                 /* To simplify parsing code, we will store them in      */
     489             :                 /* aosXmlProps with the coordinate name prefixed to   */
     490             :                 /* the property name.                                   */
     491          64 :                 CPLXMLNode *psCur2 = psCur->psChild;
     492         613 :                 while (psCur2 != nullptr)
     493             :                 {
     494         549 :                     if (!EQUAL(psCur2->pszValue, "property"))
     495             :                     {
     496         229 :                         psCur2 = psCur2->psNext;
     497         229 :                         continue; /* Skip non property elements */
     498             :                     }
     499             : 
     500             :                     const char *pszCur2Name =
     501         320 :                                    CPLGetXMLValue(psCur2, "name", nullptr),
     502             :                                *pszCur2Value =
     503         320 :                                    CPLGetXMLValue(psCur2, "value", nullptr);
     504             : 
     505         320 :                     if (pszCur2Name == nullptr || pszCur2Value == nullptr)
     506             :                     {
     507           0 :                         psCur2 = psCur2->psNext;
     508           0 :                         continue; /* Skip malformatted elements */
     509             :                     }
     510             : 
     511         320 :                     if (EQUAL(pszCur2Name, "startingValue") ||
     512         256 :                         EQUAL(pszCur2Name, "delta"))
     513             :                     {
     514             :                         char szPropName[32];
     515         128 :                         snprintf(szPropName, sizeof(szPropName), "%s%s",
     516             :                                  pszCurName, pszCur2Name);
     517             : 
     518         128 :                         aosXmlProps.SetNameValue(szPropName, pszCur2Value);
     519             :                     }
     520         320 :                     psCur2 = psCur2->psNext;
     521             :                 }
     522             :             }
     523             :         }
     524         693 :         psCur = psCur->psNext;
     525             :     }
     526             : 
     527          90 :     CPLDestroyXMLNode(psNode);
     528             : 
     529             :     /* -------------------------------------------------------------------- */
     530             :     /*      Fetch required fields.                                          */
     531             :     /* -------------------------------------------------------------------- */
     532          90 :     if (aosXmlProps.FetchNameValue("WIDTH") == nullptr ||
     533          90 :         aosXmlProps.FetchNameValue("LENGTH") == nullptr ||
     534          90 :         aosXmlProps.FetchNameValue("NUMBER_BANDS") == nullptr ||
     535         252 :         aosXmlProps.FetchNameValue("DATA_TYPE") == nullptr ||
     536          72 :         aosXmlProps.FetchNameValue("SCHEME") == nullptr)
     537             :     {
     538          18 :         return nullptr;
     539             :     }
     540          72 :     const int nWidth = atoi(aosXmlProps.FetchNameValue("WIDTH"));
     541          72 :     const int nHeight = atoi(aosXmlProps.FetchNameValue("LENGTH"));
     542          72 :     const int nBands = atoi(aosXmlProps.FetchNameValue("NUMBER_BANDS"));
     543             : 
     544         144 :     if (!GDALCheckDatasetDimensions(nWidth, nHeight) ||
     545          72 :         !GDALCheckBandCount(nBands, FALSE))
     546             :     {
     547           2 :         return nullptr;
     548             :     }
     549             : 
     550             :     /* -------------------------------------------------------------------- */
     551             :     /*      Update byte order info if image specify something.              */
     552             :     /* -------------------------------------------------------------------- */
     553          70 :     bool bNativeOrder = true;
     554             : 
     555          70 :     const char *pszByteOrder = aosXmlProps.FetchNameValue("BYTE_ORDER");
     556          70 :     if (pszByteOrder != nullptr)
     557             :     {
     558             : #ifdef CPL_LSB
     559          70 :         if (EQUAL(pszByteOrder, "b"))
     560             : #else
     561             :         if (EQUAL(pszByteOrder, "l"))
     562             : #endif
     563           0 :             bNativeOrder = false;
     564             :     }
     565             : 
     566             :     /* -------------------------------------------------------------------- */
     567             :     /*      Create a corresponding GDALDataset.                             */
     568             :     /* -------------------------------------------------------------------- */
     569         140 :     auto poDS = std::make_unique<ISCEDataset>();
     570          70 :     poDS->nRasterXSize = nWidth;
     571          70 :     poDS->nRasterYSize = nHeight;
     572          70 :     poDS->eAccess = poOpenInfo->eAccess;
     573          70 :     poDS->pszXMLFilename = CPLStrdup(osXMLFilename.c_str());
     574          70 :     std::swap(poDS->fpImage, poOpenInfo->fpL);
     575             : 
     576             :     /* -------------------------------------------------------------------- */
     577             :     /*      Create band information objects.                                */
     578             :     /* -------------------------------------------------------------------- */
     579          70 :     const char *pszDataType = CSLFetchNameValue(
     580             :         apszISCE2GDALDatatypes, aosXmlProps.FetchNameValue("DATA_TYPE"));
     581          70 :     if (pszDataType == nullptr)
     582             :     {
     583           0 :         return nullptr;
     584             :     }
     585          70 :     const GDALDataType eDataType = GDALGetDataTypeByName(pszDataType);
     586          70 :     const int nDTSize = GDALGetDataTypeSizeBytes(eDataType);
     587          70 :     if (nDTSize == 0)
     588             :     {
     589           0 :         return nullptr;
     590             :     }
     591          70 :     const char *pszScheme = aosXmlProps.FetchNameValue("SCHEME");
     592          70 :     int nPixelOffset = 0;
     593          70 :     int nLineOffset = 0;
     594          70 :     vsi_l_offset nBandOffset = 0;
     595          70 :     bool bIntOverflow = false;
     596          70 :     if (EQUAL(pszScheme, "BIL"))
     597             :     {
     598           0 :         poDS->eScheme = BIL;
     599           0 :         nPixelOffset = nDTSize;
     600           0 :         if (nWidth > INT_MAX / (nPixelOffset * nBands))
     601           0 :             bIntOverflow = true;
     602             :         else
     603             :         {
     604           0 :             nLineOffset = nPixelOffset * nWidth * nBands;
     605           0 :             nBandOffset = nDTSize * static_cast<vsi_l_offset>(nWidth);
     606             :         }
     607             :     }
     608          70 :     else if (EQUAL(pszScheme, "BIP"))
     609             :     {
     610          70 :         poDS->eScheme = BIP;
     611          70 :         nPixelOffset = nDTSize * nBands;
     612          70 :         if (nWidth > INT_MAX / nPixelOffset)
     613           0 :             bIntOverflow = true;
     614             :         else
     615             :         {
     616          70 :             nLineOffset = nPixelOffset * nWidth;
     617          70 :             if (nBands > 1 && nLineOffset < INT_MAX / nBands)
     618             :             {
     619             :                 // GDAL 2.1.0 had a value of nLineOffset that was equal to the
     620             :                 // theoretical nLineOffset multiplied by nBands...
     621          30 :                 VSIFSeekL(poDS->fpImage, 0, SEEK_END);
     622          30 :                 const GUIntBig nWrongFileSize =
     623          30 :                     static_cast<GUIntBig>(nDTSize) * nWidth *
     624          30 :                     (static_cast<GUIntBig>(nHeight - 1) * nBands * nBands +
     625             :                      nBands);
     626          30 :                 if (VSIFTellL(poDS->fpImage) == nWrongFileSize)
     627             :                 {
     628           0 :                     CPLError(
     629             :                         CE_Warning, CPLE_AppDefined,
     630             :                         "This file has been incorrectly generated by an older "
     631             :                         "GDAL version whose line offset computation was "
     632             :                         "erroneous. "
     633             :                         "Taking that into account, but the file should be "
     634             :                         "re-encoded ideally");
     635           0 :                     nLineOffset = nLineOffset * nBands;
     636             :                 }
     637             :             }
     638          70 :             nBandOffset = nDTSize;
     639             :         }
     640             :     }
     641           0 :     else if (EQUAL(pszScheme, "BSQ"))
     642             :     {
     643           0 :         poDS->eScheme = BSQ;
     644           0 :         nPixelOffset = nDTSize;
     645           0 :         if (nWidth > INT_MAX / nPixelOffset)
     646           0 :             bIntOverflow = true;
     647             :         else
     648             :         {
     649           0 :             nLineOffset = nPixelOffset * nWidth;
     650           0 :             nBandOffset = nLineOffset * static_cast<vsi_l_offset>(nHeight);
     651             :         }
     652             :     }
     653             :     else
     654             :     {
     655           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     656             :                  "Unknown scheme \"%s\" within ISCE raster.", pszScheme);
     657           0 :         return nullptr;
     658             :     }
     659             : 
     660          70 :     if (bIntOverflow)
     661             :     {
     662           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Int overflow occurred.");
     663           0 :         return nullptr;
     664             :     }
     665             : 
     666         102 :     if (bFileSizeCheck &&
     667          32 :         !RAWDatasetCheckMemoryUsage(poDS->nRasterXSize, poDS->nRasterYSize,
     668             :                                     nBands, nDTSize, nPixelOffset, nLineOffset,
     669          32 :                                     0, nBandOffset, poDS->fpImage))
     670             :     {
     671           0 :         return nullptr;
     672             :     }
     673             : 
     674         206 :     for (int b = 0; b < nBands; b++)
     675             :     {
     676             :         auto poBand = std::make_unique<ISCERasterBand>(
     677         136 :             poDS.get(), b + 1, poDS->fpImage, nBandOffset * b, nPixelOffset,
     678         136 :             nLineOffset, eDataType, bNativeOrder);
     679         136 :         if (!poBand->IsValid())
     680           0 :             return nullptr;
     681         136 :         poDS->SetBand(b + 1, std::move(poBand));
     682             :     }
     683             : 
     684             :     /* -------------------------------------------------------------------- */
     685             :     /*      Interpret georeferencing, if present.                           */
     686             :     /* -------------------------------------------------------------------- */
     687          70 :     if (aosXmlProps.FetchNameValue("Coordinate1startingValue") != nullptr &&
     688          32 :         aosXmlProps.FetchNameValue("Coordinate1delta") != nullptr &&
     689         134 :         aosXmlProps.FetchNameValue("Coordinate2startingValue") != nullptr &&
     690          32 :         aosXmlProps.FetchNameValue("Coordinate2delta") != nullptr)
     691             :     {
     692          32 :         GDALGeoTransform gt;
     693          32 :         gt[0] = CPLAtof(aosXmlProps.FetchNameValue("Coordinate1startingValue"));
     694          32 :         gt[1] = CPLAtof(aosXmlProps.FetchNameValue("Coordinate1delta"));
     695          32 :         gt[2] = 0.0;
     696          32 :         gt[3] = CPLAtof(aosXmlProps.FetchNameValue("Coordinate2startingValue"));
     697          32 :         gt[4] = 0.0;
     698          32 :         gt[5] = CPLAtof(aosXmlProps.FetchNameValue("Coordinate2delta"));
     699          32 :         poDS->SetGeoTransform(gt);
     700             : 
     701             :         /* ISCE format seems not to have a projection field, but uses   */
     702             :         /* WGS84.                                                       */
     703          32 :         poDS->SetProjection(SRS_WKT_WGS84_LAT_LONG);
     704             :     }
     705             : 
     706             :     /* -------------------------------------------------------------------- */
     707             :     /*      Set all the other header metadata into the ISCE domain          */
     708             :     /* -------------------------------------------------------------------- */
     709         707 :     for (int i = 0; i < aosXmlProps.size(); i++)
     710             :     {
     711             :         const CPLStringList aosTokens(CSLTokenizeString2(
     712         637 :             aosXmlProps[i], "=", CSLT_STRIPLEADSPACES | CSLT_STRIPENDSPACES));
     713        1274 :         if (aosTokens.size() < 2 || EQUAL(aosTokens[0], "WIDTH") ||
     714         567 :             EQUAL(aosTokens[0], "LENGTH") ||
     715         497 :             EQUAL(aosTokens[0], "NUMBER_BANDS") ||
     716         427 :             EQUAL(aosTokens[0], "DATA_TYPE") || EQUAL(aosTokens[0], "SCHEME") ||
     717         287 :             EQUAL(aosTokens[0], "BYTE_ORDER") ||
     718         217 :             EQUAL(aosTokens[0], "Coordinate1startingValue") ||
     719         185 :             EQUAL(aosTokens[0], "Coordinate1delta") ||
     720        1395 :             EQUAL(aosTokens[0], "Coordinate2startingValue") ||
     721         121 :             EQUAL(aosTokens[0], "Coordinate2delta"))
     722             :         {
     723         548 :             continue;
     724             :         }
     725          89 :         poDS->SetMetadataItem(aosTokens[0], aosTokens[1], "ISCE");
     726             :     }
     727             : 
     728             :     /* -------------------------------------------------------------------- */
     729             :     /*      Initialize any PAM information.                                 */
     730             :     /* -------------------------------------------------------------------- */
     731          70 :     poDS->SetDescription(poOpenInfo->pszFilename);
     732          70 :     poDS->TryLoadXML();
     733             : 
     734             :     /* -------------------------------------------------------------------- */
     735             :     /*      Check for overviews.                                            */
     736             :     /* -------------------------------------------------------------------- */
     737          70 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     738             : 
     739          70 :     return poDS.release();
     740             : }
     741             : 
     742             : /************************************************************************/
     743             : /*                              Create()                                */
     744             : /************************************************************************/
     745             : 
     746          62 : GDALDataset *ISCEDataset::Create(const char *pszFilename, int nXSize,
     747             :                                  int nYSize, int nBandsIn, GDALDataType eType,
     748             :                                  char **papszOptions)
     749             : {
     750          62 :     const char *sType = GDALGetDataTypeName(eType);
     751          62 :     const char *pszScheme = CSLFetchNameValueDef(papszOptions, "SCHEME", "BIP");
     752             : 
     753             :     /* -------------------------------------------------------------------- */
     754             :     /*      Try to create the file.                                         */
     755             :     /* -------------------------------------------------------------------- */
     756          62 :     VSILFILE *fp = VSIFOpenL(pszFilename, "wb");
     757          62 :     if (fp == nullptr)
     758             :     {
     759           3 :         CPLError(CE_Failure, CPLE_OpenFailed,
     760             :                  "Attempt to create file `%s' failed.", pszFilename);
     761           3 :         return nullptr;
     762             :     }
     763             : 
     764             :     /* -------------------------------------------------------------------- */
     765             :     /*      Just write out a couple of bytes to establish the binary        */
     766             :     /*      file, and then close it.                                        */
     767             :     /* -------------------------------------------------------------------- */
     768          59 :     CPL_IGNORE_RET_VAL(VSIFWriteL("\0\0", 2, 1, fp));
     769          59 :     CPL_IGNORE_RET_VAL(VSIFCloseL(fp));
     770             : 
     771             :     /* -------------------------------------------------------------------- */
     772             :     /*      Create a minimal XML document.                                  */
     773             :     /* -------------------------------------------------------------------- */
     774          59 :     CPLXMLNode *psDocNode = CPLCreateXMLNode(nullptr, CXT_Element, "imageFile");
     775             : 
     776             :     CPLXMLNode *psTmpNode =
     777          59 :         CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     778          59 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "WIDTH");
     779          59 :     char sBuf[64] = {'\0'};
     780          59 :     CPLsnprintf(sBuf, sizeof(sBuf), "%d", nXSize);
     781          59 :     CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     782             : 
     783          59 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     784          59 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "LENGTH");
     785          59 :     CPLsnprintf(sBuf, sizeof(sBuf), "%d", nYSize);
     786          59 :     CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     787             : 
     788          59 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     789          59 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "NUMBER_BANDS");
     790          59 :     CPLsnprintf(sBuf, sizeof(sBuf), "%d", nBandsIn);
     791          59 :     CPLCreateXMLElementAndValue(psTmpNode, "value", sBuf);
     792             : 
     793          59 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     794          59 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "DATA_TYPE");
     795          59 :     CPLCreateXMLElementAndValue(
     796             :         psTmpNode, "value",
     797             :         CSLFetchNameValue(const_cast<char **>(apszGDAL2ISCEDatatypes), sType));
     798             : 
     799          59 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     800          59 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "SCHEME");
     801          59 :     CPLCreateXMLElementAndValue(psTmpNode, "value", pszScheme);
     802             : 
     803          59 :     psTmpNode = CPLCreateXMLNode(psDocNode, CXT_Element, "property");
     804          59 :     CPLAddXMLAttributeAndValue(psTmpNode, "name", "BYTE_ORDER");
     805             : #ifdef CPL_LSB
     806          59 :     CPLCreateXMLElementAndValue(psTmpNode, "value", "l");
     807             : #else
     808             :     CPLCreateXMLElementAndValue(psTmpNode, "value", "b");
     809             : #endif
     810             : 
     811             :     /* -------------------------------------------------------------------- */
     812             :     /*      Write the XML file.                                             */
     813             :     /* -------------------------------------------------------------------- */
     814             :     const std::string osXMLFilename =
     815         118 :         CPLFormFilenameSafe(nullptr, pszFilename, "xml");
     816          59 :     CPLSerializeXMLTreeToFile(psDocNode, osXMLFilename.c_str());
     817             : 
     818             :     /* -------------------------------------------------------------------- */
     819             :     /*      Free the XML Doc.                                               */
     820             :     /* -------------------------------------------------------------------- */
     821          59 :     CPLDestroyXMLNode(psDocNode);
     822             : 
     823         118 :     GDALOpenInfo oOpenInfo(pszFilename, GA_Update);
     824          59 :     return Open(&oOpenInfo, false);
     825             : }
     826             : 
     827             : /************************************************************************/
     828             : /*                          ISCERasterBand()                            */
     829             : /************************************************************************/
     830             : 
     831         136 : ISCERasterBand::ISCERasterBand(GDALDataset *poDSIn, int nBandIn,
     832             :                                VSILFILE *fpRawIn, vsi_l_offset nImgOffsetIn,
     833             :                                int nPixelOffsetIn, int nLineOffsetIn,
     834         136 :                                GDALDataType eDataTypeIn, int bNativeOrderIn)
     835             :     : RawRasterBand(poDSIn, nBandIn, fpRawIn, nImgOffsetIn, nPixelOffsetIn,
     836             :                     nLineOffsetIn, eDataTypeIn, bNativeOrderIn,
     837         136 :                     RawRasterBand::OwnFP::NO)
     838             : {
     839         136 : }
     840             : 
     841             : ISCERasterBand::~ISCERasterBand() = default;
     842             : 
     843             : /************************************************************************/
     844             : /*                         GDALRegister_ISCE()                          */
     845             : /************************************************************************/
     846             : 
     847        1935 : void GDALRegister_ISCE()
     848             : {
     849        1935 :     if (GDALGetDriverByName("ISCE") != nullptr)
     850         282 :         return;
     851             : 
     852        1653 :     GDALDriver *poDriver = new GDALDriver();
     853             : 
     854        1653 :     poDriver->SetDescription("ISCE");
     855        1653 :     poDriver->SetMetadataItem(GDAL_DMD_LONGNAME, "ISCE raster");
     856        1653 :     poDriver->SetMetadataItem(GDAL_DMD_HELPTOPIC, "drivers/raster/isce.html");
     857        1653 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONDATATYPES,
     858             :                               "Byte Int16 Int32 Int64 Float32"
     859             :                               " Float64 CInt16 CInt64 CFloat32 "
     860        1653 :                               " CFloat64");
     861        1653 :     poDriver->SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST,
     862             :                               "<CreationOptionList>"
     863             :                               "   <Option name='SCHEME' type='string-select'>"
     864             :                               "       <Value>BIP</Value>"
     865             :                               "       <Value>BIL</Value>"
     866             :                               "       <Value>BSQ</Value>"
     867             :                               "   </Option>"
     868        1653 :                               "</CreationOptionList>");
     869        1653 :     poDriver->SetMetadataItem(GDAL_DCAP_RASTER, "YES");
     870        1653 :     poDriver->SetMetadataItem(GDAL_DCAP_VIRTUALIO, "YES");
     871             : 
     872        1653 :     poDriver->pfnOpen = ISCEDataset::Open;
     873        1653 :     poDriver->pfnCreate = ISCEDataset::Create;
     874             : 
     875        1653 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     876             : }

Generated by: LCOV version 1.14