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

Generated by: LCOV version 1.14