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

Generated by: LCOV version 1.14