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

Generated by: LCOV version 1.14