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

Generated by: LCOV version 1.14