LCOV - code coverage report
Current view: top level - frmts/pds - isis2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 413 517 79.9 %
Date: 2024-05-03 15:49:35 Functions: 19 19 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ISIS Version 2 Driver
       4             :  * Purpose:  Implementation of ISIS2Dataset
       5             :  * Author:   Trent Hare (thare@usgs.gov),
       6             :  *           Robert Soricone (rsoricone@usgs.gov)
       7             :  *           Ludovic Mercier (ludovic.mercier@gmail.com)
       8             :  *           Frank Warmerdam (warmerdam@pobox.com)
       9             :  *
      10             :  * NOTE: Original code authored by Trent and Robert and placed in the public
      11             :  * domain as per US government policy.  I have (within my rights) appropriated
      12             :  * it and placed it under the following license.  This is not intended to
      13             :  * diminish Trent and Roberts contribution.
      14             :  ******************************************************************************
      15             :  * Copyright (c) 2006, Frank Warmerdam <warmerdam@pobox.com>
      16             :  * Copyright (c) 2008-2011, Even Rouault <even dot rouault at spatialys.com>
      17             :  *
      18             :  * Permission is hereby granted, free of charge, to any person obtaining a
      19             :  * copy of this software and associated documentation files (the "Software"),
      20             :  * to deal in the Software without restriction, including without limitation
      21             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      22             :  * and/or sell copies of the Software, and to permit persons to whom the
      23             :  * Software is furnished to do so, subject to the following conditions:
      24             :  *
      25             :  * The above copyright notice and this permission notice shall be included
      26             :  * in all copies or substantial portions of the Software.
      27             :  *
      28             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      29             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      30             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      31             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      32             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      33             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      34             :  * DEALINGS IN THE SOFTWARE.
      35             :  ****************************************************************************/
      36             : 
      37             : constexpr int NULL1 = 0;
      38             : constexpr int NULL2 = -32768;
      39             : constexpr double NULL3 = -3.4028226550889044521e+38;
      40             : 
      41             : constexpr int RECORD_SIZE = 512;
      42             : 
      43             : #include "cpl_string.h"
      44             : #include "gdal_frmts.h"
      45             : #include "nasakeywordhandler.h"
      46             : #include "ogr_spatialref.h"
      47             : #include "rawdataset.h"
      48             : #include "pdsdrivercore.h"
      49             : 
      50             : /************************************************************************/
      51             : /* ==================================================================== */
      52             : /*                      ISISDataset     version2                        */
      53             : /* ==================================================================== */
      54             : /************************************************************************/
      55             : 
      56             : class ISIS2Dataset final : public RawDataset
      57             : {
      58             :     VSILFILE *fpImage;  // image data file.
      59             :     CPLString osExternalCube;
      60             : 
      61             :     NASAKeywordHandler oKeywords;
      62             : 
      63             :     int bGotTransform;
      64             :     double adfGeoTransform[6];
      65             : 
      66             :     OGRSpatialReference m_oSRS{};
      67             : 
      68             :     int parse_label(const char *file, char *keyword, char *value);
      69             :     int strstrip(char instr[], char outstr[], int position);
      70             : 
      71             :     CPLString oTempResult;
      72             : 
      73             :     static void CleanString(CPLString &osInput);
      74             : 
      75             :     const char *GetKeyword(const char *pszPath, const char *pszDefault = "");
      76             :     const char *GetKeywordSub(const char *pszPath, int iSubscript,
      77             :                               const char *pszDefault = "");
      78             : 
      79             :     CPLErr Close() override;
      80             : 
      81             :   public:
      82             :     ISIS2Dataset();
      83             :     virtual ~ISIS2Dataset();
      84             : 
      85             :     virtual CPLErr GetGeoTransform(double *padfTransform) override;
      86             :     const OGRSpatialReference *GetSpatialRef() const override;
      87             : 
      88             :     virtual char **GetFileList() override;
      89             : 
      90             :     static GDALDataset *Open(GDALOpenInfo *);
      91             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
      92             :                                int nBandsIn, GDALDataType eType,
      93             :                                char **papszParamList);
      94             : 
      95             :     // Write related.
      96             :     static int WriteRaster(const std::string &osFilename, bool includeLabel,
      97             :                            GUIntBig iRecord, GUIntBig iLabelRecords,
      98             :                            GDALDataType eType, const char *pszInterleaving);
      99             : 
     100             :     static int WriteLabel(const std::string &osFilename,
     101             :                           const std::string &osRasterFile,
     102             :                           const std::string &sObjectTag, unsigned int nXSize,
     103             :                           unsigned int nYSize, unsigned int nBandsIn,
     104             :                           GDALDataType eType, GUIntBig iRecords,
     105             :                           const char *pszInterleaving, GUIntBig &iLabelRecords,
     106             :                           bool bRelaunch = false);
     107             :     static int WriteQUBE_Information(VSILFILE *fpLabel, unsigned int iLevel,
     108             :                                      unsigned int &nWritingBytes,
     109             :                                      unsigned int nXSize, unsigned int nYSize,
     110             :                                      unsigned int nBandsIn, GDALDataType eType,
     111             :                                      const char *pszInterleaving);
     112             : 
     113             :     static unsigned int WriteKeyword(VSILFILE *fpLabel, unsigned int iLevel,
     114             :                                      CPLString key, CPLString value);
     115             :     static unsigned int WriteFormatting(VSILFILE *fpLabel, CPLString data);
     116             :     static GUIntBig RecordSizeCalculation(unsigned int nXSize,
     117             :                                           unsigned int nYSize,
     118             :                                           unsigned int nBands,
     119             :                                           GDALDataType eType);
     120             : };
     121             : 
     122             : /************************************************************************/
     123             : /*                            ISIS2Dataset()                            */
     124             : /************************************************************************/
     125             : 
     126          45 : ISIS2Dataset::ISIS2Dataset() : fpImage(nullptr), bGotTransform(FALSE)
     127             : {
     128          45 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     129          45 :     adfGeoTransform[0] = 0.0;
     130          45 :     adfGeoTransform[1] = 1.0;
     131          45 :     adfGeoTransform[2] = 0.0;
     132          45 :     adfGeoTransform[3] = 0.0;
     133          45 :     adfGeoTransform[4] = 0.0;
     134          45 :     adfGeoTransform[5] = 1.0;
     135          45 : }
     136             : 
     137             : /************************************************************************/
     138             : /*                            ~ISIS2Dataset()                            */
     139             : /************************************************************************/
     140             : 
     141          90 : ISIS2Dataset::~ISIS2Dataset()
     142             : 
     143             : {
     144          45 :     ISIS2Dataset::Close();
     145          90 : }
     146             : 
     147             : /************************************************************************/
     148             : /*                              Close()                                 */
     149             : /************************************************************************/
     150             : 
     151          88 : CPLErr ISIS2Dataset::Close()
     152             : {
     153          88 :     CPLErr eErr = CE_None;
     154          88 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     155             :     {
     156          45 :         if (ISIS2Dataset::FlushCache(true) != CE_None)
     157           0 :             eErr = CE_Failure;
     158          45 :         if (fpImage != nullptr)
     159             :         {
     160          43 :             if (VSIFCloseL(fpImage) != 0)
     161           0 :                 eErr = CE_Failure;
     162             :         }
     163          45 :         if (GDALPamDataset::Close() != CE_None)
     164           0 :             eErr = CE_Failure;
     165             :     }
     166          88 :     return eErr;
     167             : }
     168             : 
     169             : /************************************************************************/
     170             : /*                            GetFileList()                             */
     171             : /************************************************************************/
     172             : 
     173           3 : char **ISIS2Dataset::GetFileList()
     174             : 
     175             : {
     176           3 :     char **papszFileList = GDALPamDataset::GetFileList();
     177             : 
     178           3 :     if (!osExternalCube.empty())
     179           1 :         papszFileList = CSLAddString(papszFileList, osExternalCube);
     180             : 
     181           3 :     return papszFileList;
     182             : }
     183             : 
     184             : /************************************************************************/
     185             : /*                         GetSpatialRef()                              */
     186             : /************************************************************************/
     187             : 
     188           1 : const OGRSpatialReference *ISIS2Dataset::GetSpatialRef() const
     189             : {
     190           1 :     if (!m_oSRS.IsEmpty())
     191           1 :         return &m_oSRS;
     192           0 :     return GDALPamDataset::GetSpatialRef();
     193             : }
     194             : 
     195             : /************************************************************************/
     196             : /*                          GetGeoTransform()                           */
     197             : /************************************************************************/
     198             : 
     199          14 : CPLErr ISIS2Dataset::GetGeoTransform(double *padfTransform)
     200             : 
     201             : {
     202          14 :     if (bGotTransform)
     203             :     {
     204           1 :         memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     205           1 :         return CE_None;
     206             :     }
     207             : 
     208          13 :     return GDALPamDataset::GetGeoTransform(padfTransform);
     209             : }
     210             : 
     211             : /************************************************************************/
     212             : /*                                Open()                                */
     213             : /************************************************************************/
     214             : 
     215          45 : GDALDataset *ISIS2Dataset::Open(GDALOpenInfo *poOpenInfo)
     216             : {
     217             :     /* -------------------------------------------------------------------- */
     218             :     /*      Does this look like a CUBE or an IMAGE Primary Data Object?     */
     219             :     /* -------------------------------------------------------------------- */
     220          45 :     if (!ISIS2DriverIdentify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     221           0 :         return nullptr;
     222             : 
     223          45 :     VSILFILE *fpQube = poOpenInfo->fpL;
     224          45 :     poOpenInfo->fpL = nullptr;
     225             : 
     226          90 :     auto poDS = std::make_unique<ISIS2Dataset>();
     227             : 
     228          45 :     if (!poDS->oKeywords.Ingest(fpQube, 0))
     229             :     {
     230           0 :         VSIFCloseL(fpQube);
     231           0 :         return nullptr;
     232             :     }
     233             : 
     234          45 :     VSIFCloseL(fpQube);
     235             : 
     236             :     /* -------------------------------------------------------------------- */
     237             :     /*      We assume the user is pointing to the label (i.e. .lab) file.   */
     238             :     /* -------------------------------------------------------------------- */
     239             :     // QUBE can be inline or detached and point to an image name
     240             :     // ^QUBE = 76
     241             :     // ^QUBE = ("ui31s015.img",6441<BYTES>) - has another label on the image
     242             :     // ^QUBE = "ui31s015.img" - which implies no label or skip value
     243             : 
     244          45 :     const char *pszQube = poDS->GetKeyword("^QUBE");
     245          45 :     int nQube = 0;
     246          45 :     int bByteLocation = FALSE;
     247          90 :     CPLString osTargetFile = poOpenInfo->pszFilename;
     248             : 
     249          45 :     if (pszQube[0] == '"')
     250             :     {
     251           0 :         const CPLString osTPath = CPLGetPath(poOpenInfo->pszFilename);
     252           0 :         CPLString osFilename = pszQube;
     253           0 :         poDS->CleanString(osFilename);
     254           0 :         osTargetFile = CPLFormCIFilename(osTPath, osFilename, nullptr);
     255           0 :         poDS->osExternalCube = osTargetFile;
     256             :     }
     257          45 :     else if (pszQube[0] == '(')
     258             :     {
     259           6 :         const CPLString osTPath = CPLGetPath(poOpenInfo->pszFilename);
     260           6 :         CPLString osFilename = poDS->GetKeywordSub("^QUBE", 1, "");
     261           3 :         poDS->CleanString(osFilename);
     262           3 :         osTargetFile = CPLFormCIFilename(osTPath, osFilename, nullptr);
     263           3 :         poDS->osExternalCube = osTargetFile;
     264             : 
     265           3 :         nQube = atoi(poDS->GetKeywordSub("^QUBE", 2, "1"));
     266           3 :         if (strstr(poDS->GetKeywordSub("^QUBE", 2, "1"), "<BYTES>") != nullptr)
     267           0 :             bByteLocation = true;
     268             :     }
     269             :     else
     270             :     {
     271          42 :         nQube = atoi(pszQube);
     272          42 :         if (strstr(pszQube, "<BYTES>") != nullptr)
     273           0 :             bByteLocation = true;
     274             :     }
     275             : 
     276             :     /* -------------------------------------------------------------------- */
     277             :     /*      Check if file an ISIS2 header file?  Read a few lines of text   */
     278             :     /*      searching for something starting with nrows or ncols.           */
     279             :     /* -------------------------------------------------------------------- */
     280             : 
     281             :     /* -------------------------------------------------------------------- */
     282             :     /*      Checks to see if this is valid ISIS2 cube                       */
     283             :     /*      SUFFIX_ITEM tag in .cub file should be (0,0,0); no side-planes  */
     284             :     /* -------------------------------------------------------------------- */
     285          45 :     const int s_ix = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 1));
     286          45 :     const int s_iy = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 2));
     287          45 :     const int s_iz = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 3));
     288             : 
     289          45 :     if (s_ix != 0 || s_iy != 0 || s_iz != 0)
     290             :     {
     291           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     292             :                  "*** ISIS 2 cube file has invalid SUFFIX_ITEMS parameters:\n"
     293             :                  "*** gdal isis2 driver requires (0, 0, 0), thus no sideplanes "
     294             :                  "or backplanes\n"
     295             :                  "found: (%i, %i, %i)\n\n",
     296             :                  s_ix, s_iy, s_iz);
     297           0 :         return nullptr;
     298             :     }
     299             : 
     300             :     /**************** end SUFFIX_ITEM check ***********************/
     301             : 
     302             :     /***********   Grab layout type (BSQ, BIP, BIL) ************/
     303             :     //  AXIS_NAME = (SAMPLE,LINE,BAND)
     304             :     /***********************************************************/
     305             : 
     306          45 :     char szLayout[10] = "BSQ";  // default to band seq.
     307          45 :     const char *value = poDS->GetKeyword("QUBE.AXIS_NAME", "");
     308          45 :     if (EQUAL(value, "(SAMPLE,LINE,BAND)"))
     309          45 :         strcpy(szLayout, "BSQ");
     310           0 :     else if (EQUAL(value, "(BAND,LINE,SAMPLE)"))
     311           0 :         strcpy(szLayout, "BIP");
     312           0 :     else if (EQUAL(value, "(SAMPLE,BAND,LINE)") || EQUAL(value, ""))
     313           0 :         strcpy(szLayout, "BSQ");
     314             :     else
     315             :     {
     316           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     317             :                  "%s layout not supported. Abort\n\n", value);
     318           0 :         return nullptr;
     319             :     }
     320             : 
     321             :     /***********   Grab samples lines band ************/
     322          45 :     const int nCols = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 1));
     323          45 :     const int nRows = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 2));
     324          45 :     const int nBands = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 3));
     325             : 
     326             :     /***********   Grab Qube record bytes  **********/
     327          45 :     const int record_bytes = atoi(poDS->GetKeyword("RECORD_BYTES"));
     328          45 :     if (record_bytes < 0)
     329             :     {
     330           0 :         return nullptr;
     331             :     }
     332             : 
     333          45 :     GUIntBig nSkipBytes = 0;
     334          45 :     if (nQube > 0 && bByteLocation)
     335           0 :         nSkipBytes = (nQube - 1);
     336          45 :     else if (nQube > 0)
     337          45 :         nSkipBytes = static_cast<GUIntBig>(nQube - 1) * record_bytes;
     338             :     else
     339           0 :         nSkipBytes = 0;
     340             : 
     341             :     /***********   Grab samples lines band ************/
     342          45 :     char chByteOrder = 'M';  // default to MSB
     343          90 :     CPLString osCoreItemType = poDS->GetKeyword("QUBE.CORE_ITEM_TYPE");
     344          45 :     if ((EQUAL(osCoreItemType, "PC_INTEGER")) ||
     345          60 :         (EQUAL(osCoreItemType, "PC_UNSIGNED_INTEGER")) ||
     346          15 :         (EQUAL(osCoreItemType, "PC_REAL")))
     347             :     {
     348          43 :         chByteOrder = 'I';
     349             :     }
     350             : 
     351             :     /********   Grab format type - isis2 only supports 8,16,32 *******/
     352          45 :     GDALDataType eDataType = GDT_Byte;
     353          45 :     bool bNoDataSet = false;
     354          45 :     double dfNoData = 0.0;
     355             : 
     356          45 :     int itype = atoi(poDS->GetKeyword("QUBE.CORE_ITEM_BYTES", ""));
     357          45 :     switch (itype)
     358             :     {
     359          20 :         case 1:
     360          20 :             eDataType = GDT_Byte;
     361          20 :             dfNoData = NULL1;
     362          20 :             bNoDataSet = true;
     363          20 :             break;
     364          10 :         case 2:
     365          10 :             if (strstr(osCoreItemType, "UNSIGNED") != nullptr)
     366             :             {
     367           5 :                 dfNoData = 0;
     368           5 :                 eDataType = GDT_UInt16;
     369             :             }
     370             :             else
     371             :             {
     372           5 :                 dfNoData = NULL2;
     373           5 :                 eDataType = GDT_Int16;
     374             :             }
     375          10 :             bNoDataSet = true;
     376          10 :             break;
     377          10 :         case 4:
     378          10 :             eDataType = GDT_Float32;
     379          10 :             dfNoData = NULL3;
     380          10 :             bNoDataSet = true;
     381          10 :             break;
     382           5 :         case 8:
     383           5 :             eDataType = GDT_Float64;
     384           5 :             dfNoData = NULL3;
     385           5 :             bNoDataSet = true;
     386           5 :             break;
     387           0 :         default:
     388           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     389             :                      "Itype of %d is not supported in ISIS 2.", itype);
     390           0 :             return nullptr;
     391             :     }
     392             : 
     393             :     /***********   Grab Cellsize ************/
     394          45 :     double dfXDim = 1.0;
     395          45 :     double dfYDim = 1.0;
     396             : 
     397          45 :     value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.MAP_SCALE");
     398          45 :     if (strlen(value) > 0)
     399             :     {
     400             :         // Convert km to m
     401           2 :         dfXDim = static_cast<float>(CPLAtof(value) * 1000.0);
     402           2 :         dfYDim = static_cast<float>(CPLAtof(value) * 1000.0 * -1);
     403             :     }
     404             : 
     405             :     /***********   Grab LINE_PROJECTION_OFFSET ************/
     406          45 :     double dfULYMap = 0.5;
     407             : 
     408             :     value =
     409          45 :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.LINE_PROJECTION_OFFSET");
     410          45 :     if (strlen(value) > 0)
     411             :     {
     412           2 :         const double yulcenter = static_cast<float>(CPLAtof(value)) * dfYDim;
     413           2 :         dfULYMap = yulcenter - (dfYDim / 2);
     414             :     }
     415             : 
     416             :     /***********   Grab SAMPLE_PROJECTION_OFFSET ************/
     417          45 :     double dfULXMap = 0.5;
     418             : 
     419             :     value =
     420          45 :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.SAMPLE_PROJECTION_OFFSET");
     421          45 :     if (strlen(value) > 0)
     422             :     {
     423           2 :         const double xulcenter = static_cast<float>(CPLAtof(value)) * dfXDim;
     424           2 :         dfULXMap = xulcenter - (dfXDim / 2);
     425             :     }
     426             : 
     427             :     /***********  Grab TARGET_NAME  ************/
     428             :     /**** This is the planets name i.e. MARS ***/
     429          90 :     const CPLString target_name = poDS->GetKeyword("QUBE.TARGET_NAME");
     430             : 
     431             :     /***********   Grab MAP_PROJECTION_TYPE ************/
     432             :     CPLString map_proj_name =
     433          90 :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE");
     434          45 :     poDS->CleanString(map_proj_name);
     435             : 
     436             :     /***********   Grab SEMI-MAJOR ************/
     437             :     const double semi_major =
     438          45 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.A_AXIS_RADIUS")) *
     439          45 :         1000.0;
     440             : 
     441             :     /***********   Grab semi-minor ************/
     442             :     const double semi_minor =
     443          45 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.C_AXIS_RADIUS")) *
     444          45 :         1000.0;
     445             : 
     446             :     /***********   Grab CENTER_LAT ************/
     447             :     const double center_lat =
     448          45 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.CENTER_LATITUDE"));
     449             : 
     450             :     /***********   Grab CENTER_LON ************/
     451             :     const double center_lon =
     452          45 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.CENTER_LONGITUDE"));
     453             : 
     454             :     /***********   Grab 1st std parallel ************/
     455          45 :     const double first_std_parallel = CPLAtof(
     456             :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.FIRST_STANDARD_PARALLEL"));
     457             : 
     458             :     /***********   Grab 2nd std parallel ************/
     459          45 :     const double second_std_parallel = CPLAtof(
     460             :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.SECOND_STANDARD_PARALLEL"));
     461             : 
     462             :     /*** grab  PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/
     463             :     // Need to further study how ocentric/ographic will effect the gdal library.
     464             :     // So far we will use this fact to define a sphere or ellipse for some
     465             :     // projections Frank - may need to talk this over
     466          45 :     bool bIsGeographic = true;
     467             :     value =
     468          45 :         poDS->GetKeyword("CUBE.IMAGE_MAP_PROJECTION.PROJECTION_LATITUDE_TYPE");
     469          45 :     if (EQUAL(value, "\"PLANETOCENTRIC\""))
     470           0 :         bIsGeographic = false;
     471             : 
     472          45 :     CPLDebug("ISIS2", "using projection %s", map_proj_name.c_str());
     473             : 
     474          90 :     OGRSpatialReference oSRS;
     475          45 :     oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     476          45 :     bool bProjectionSet = true;
     477             : 
     478             :     // Set oSRS projection and parameters
     479          45 :     if ((EQUAL(map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL")) ||
     480          90 :         (EQUAL(map_proj_name, "EQUIRECTANGULAR")) ||
     481          45 :         (EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")))
     482             :     {
     483           2 :         oSRS.OGRSpatialReference::SetEquirectangular2(0.0, center_lon,
     484             :                                                       center_lat, 0, 0);
     485             :     }
     486          43 :     else if (EQUAL(map_proj_name, "ORTHOGRAPHIC"))
     487             :     {
     488           0 :         oSRS.OGRSpatialReference::SetOrthographic(center_lat, center_lon, 0, 0);
     489             :     }
     490          86 :     else if ((EQUAL(map_proj_name, "SINUSOIDAL")) ||
     491          43 :              (EQUAL(map_proj_name, "SINUSOIDAL_EQUAL-AREA")))
     492             :     {
     493           0 :         oSRS.OGRSpatialReference::SetSinusoidal(center_lon, 0, 0);
     494             :     }
     495          43 :     else if (EQUAL(map_proj_name, "MERCATOR"))
     496             :     {
     497           0 :         oSRS.OGRSpatialReference::SetMercator(center_lat, center_lon, 1, 0, 0);
     498             :     }
     499          43 :     else if (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC"))
     500             :     {
     501           0 :         oSRS.OGRSpatialReference::SetPS(center_lat, center_lon, 1, 0, 0);
     502             :     }
     503          43 :     else if (EQUAL(map_proj_name, "TRANSVERSE_MERCATOR"))
     504             :     {
     505           0 :         oSRS.OGRSpatialReference::SetTM(center_lat, center_lon, 1, 0, 0);
     506             :     }
     507          43 :     else if (EQUAL(map_proj_name, "LAMBERT_CONFORMAL_CONIC"))
     508             :     {
     509           0 :         oSRS.OGRSpatialReference::SetLCC(first_std_parallel,
     510             :                                          second_std_parallel, center_lat,
     511             :                                          center_lon, 0, 0);
     512             :     }
     513          43 :     else if (EQUAL(map_proj_name, ""))
     514             :     {
     515             :         /* no projection */
     516          43 :         bProjectionSet = false;
     517             :     }
     518             :     else
     519             :     {
     520           0 :         CPLDebug("ISIS2",
     521             :                  "Dataset projection %s is not supported. Continuing...",
     522             :                  map_proj_name.c_str());
     523           0 :         bProjectionSet = false;
     524             :     }
     525             : 
     526          45 :     if (bProjectionSet)
     527             :     {
     528             :         // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
     529           6 :         const CPLString proj_target_name = map_proj_name + " " + target_name;
     530           2 :         oSRS.SetProjCS(proj_target_name);  // set ProjCS keyword
     531             : 
     532             :         // The geographic/geocentric name will be the same basic name as the
     533             :         // body name 'GCS' = Geographic/Geocentric Coordinate System
     534           4 :         const CPLString geog_name = "GCS_" + target_name;
     535             : 
     536             :         // The datum and sphere names will be the same basic name aas the planet
     537           4 :         const CPLString datum_name = "D_" + target_name;
     538             :         // Might not be IAU defined so don't add.
     539           4 :         CPLString sphere_name = target_name;  // + "_IAU_IAG");
     540             : 
     541             :         // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
     542           2 :         double iflattening = 0.0;
     543           2 :         if ((semi_major - semi_minor) < 0.0000001)
     544           2 :             iflattening = 0;
     545             :         else
     546           0 :             iflattening = semi_major / (semi_major - semi_minor);
     547             : 
     548             :         // Set the body size but take into consideration which proj is being
     549             :         // used to help w/ proj4 compatibility The use of a Sphere, polar radius
     550             :         // or ellipse here is based on how ISIS does it internally
     551           2 :         if (((EQUAL(map_proj_name, "STEREOGRAPHIC") &&
     552           4 :               (fabs(center_lat) == 90))) ||
     553           2 :             (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC")))
     554             :         {
     555           0 :             if (bIsGeographic)
     556             :             {
     557             :                 // Geograpraphic, so set an ellipse
     558           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     559             :                                iflattening, "Reference_Meridian", 0.0);
     560             :             }
     561             :             else
     562             :             {
     563             :                 // Geocentric, so force a sphere using the semi-minor axis. I
     564             :                 // hope...
     565           0 :                 sphere_name += "_polarRadius";
     566           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_minor,
     567             :                                0.0, "Reference_Meridian", 0.0);
     568             :             }
     569             :         }
     570           2 :         else if ((EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
     571           0 :                  (EQUAL(map_proj_name, "ORTHOGRAPHIC")) ||
     572           0 :                  (EQUAL(map_proj_name, "STEREOGRAPHIC")) ||
     573           2 :                  (EQUAL(map_proj_name, "SINUSOIDAL_EQUAL-AREA")) ||
     574           0 :                  (EQUAL(map_proj_name, "SINUSOIDAL")))
     575             :         {
     576             :             // ISIS uses the spherical equation for these projections so force
     577             :             // a sphere.
     578           2 :             oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
     579             :                            "Reference_Meridian", 0.0);
     580             :         }
     581           0 :         else if ((EQUAL(map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL")) ||
     582           0 :                  (EQUAL(map_proj_name, "EQUIRECTANGULAR")))
     583             :         {
     584             :             // Calculate localRadius using ISIS3 simple elliptical method
     585             :             //   not the more standard Radius of Curvature method
     586             :             // PI = 4 * atan(1);
     587           0 :             if (center_lon == 0)
     588             :             {  // No need to calculate local radius
     589           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     590             :                                0.0, "Reference_Meridian", 0.0);
     591             :             }
     592             :             else
     593             :             {
     594           0 :                 const double radLat = center_lat * M_PI / 180;  // in radians
     595             :                 const double localRadius =
     596           0 :                     semi_major * semi_minor /
     597           0 :                     sqrt(pow(semi_minor * cos(radLat), 2) +
     598           0 :                          pow(semi_major * sin(radLat), 2));
     599           0 :                 sphere_name += "_localRadius";
     600           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, localRadius,
     601             :                                0.0, "Reference_Meridian", 0.0);
     602           0 :                 CPLDebug("ISIS2", "local radius: %f", localRadius);
     603             :             }
     604             :         }
     605             :         else
     606             :         {
     607             :             // All other projections: Mercator, Transverse Mercator, Lambert
     608             :             // Conformal, etc. Geographic, so set an ellipse
     609           0 :             if (bIsGeographic)
     610             :             {
     611           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     612             :                                iflattening, "Reference_Meridian", 0.0);
     613             :             }
     614             :             else
     615             :             {
     616             :                 // Geocentric, so force a sphere. I hope...
     617           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     618             :                                0.0, "Reference_Meridian", 0.0);
     619             :             }
     620             :         }
     621             : 
     622             :         // translate back into a projection string.
     623           2 :         poDS->m_oSRS = std::move(oSRS);
     624             :     }
     625             : 
     626             :     /* END ISIS2 Label Read */
     627             :     /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
     628             : 
     629             :     /* -------------------------------------------------------------------- */
     630             :     /*      Did we get the required keywords?  If not we return with        */
     631             :     /*      this never having been considered to be a match. This isn't     */
     632             :     /*      an error!                                                       */
     633             :     /* -------------------------------------------------------------------- */
     634          90 :     if (!GDALCheckDatasetDimensions(nCols, nRows) ||
     635          45 :         !GDALCheckBandCount(nBands, false))
     636             :     {
     637           2 :         return nullptr;
     638             :     }
     639             : 
     640             :     /* -------------------------------------------------------------------- */
     641             :     /*      Capture some information from the file that is of interest.     */
     642             :     /* -------------------------------------------------------------------- */
     643          43 :     poDS->nRasterXSize = nCols;
     644          43 :     poDS->nRasterYSize = nRows;
     645             : 
     646             :     /* -------------------------------------------------------------------- */
     647             :     /*      Open target binary file.                                        */
     648             :     /* -------------------------------------------------------------------- */
     649             : 
     650          43 :     if (poOpenInfo->eAccess == GA_ReadOnly)
     651          19 :         poDS->fpImage = VSIFOpenL(osTargetFile, "rb");
     652             :     else
     653          24 :         poDS->fpImage = VSIFOpenL(osTargetFile, "r+b");
     654             : 
     655          43 :     if (poDS->fpImage == nullptr)
     656             :     {
     657           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     658             :                  "Failed to open %s with write permission.\n%s",
     659           0 :                  osTargetFile.c_str(), VSIStrerror(errno));
     660           0 :         return nullptr;
     661             :     }
     662             : 
     663          43 :     poDS->eAccess = poOpenInfo->eAccess;
     664             : 
     665             :     /* -------------------------------------------------------------------- */
     666             :     /*      Compute the line offset.                                        */
     667             :     /* -------------------------------------------------------------------- */
     668          43 :     int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
     669             :     int nLineOffset, nPixelOffset;
     670             :     vsi_l_offset nBandOffset;
     671             : 
     672          43 :     if (EQUAL(szLayout, "BIP"))
     673             :     {
     674           0 :         nPixelOffset = nItemSize * nBands;
     675           0 :         if (nPixelOffset > INT_MAX / nBands)
     676             :         {
     677           0 :             return nullptr;
     678             :         }
     679           0 :         nLineOffset = nPixelOffset * nCols;
     680           0 :         nBandOffset = nItemSize;
     681             :     }
     682          43 :     else if (EQUAL(szLayout, "BSQ"))
     683             :     {
     684          43 :         nPixelOffset = nItemSize;
     685          43 :         if (nPixelOffset > INT_MAX / nCols)
     686             :         {
     687           0 :             return nullptr;
     688             :         }
     689          43 :         nLineOffset = nPixelOffset * nCols;
     690          43 :         nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
     691             :     }
     692             :     else /* assume BIL */
     693             :     {
     694           0 :         nPixelOffset = nItemSize;
     695           0 :         if (nPixelOffset > INT_MAX / nBands ||
     696           0 :             nPixelOffset * nBands > INT_MAX / nCols)
     697             :         {
     698           0 :             return nullptr;
     699             :         }
     700           0 :         nLineOffset = nItemSize * nBands * nCols;
     701           0 :         nBandOffset = static_cast<vsi_l_offset>(nItemSize) * nCols;
     702             :     }
     703             : 
     704             :     /* -------------------------------------------------------------------- */
     705             :     /*      Create band information objects.                                */
     706             :     /* -------------------------------------------------------------------- */
     707         132 :     for (int i = 0; i < nBands; i++)
     708             :     {
     709             :         auto poBand = RawRasterBand::Create(
     710         178 :             poDS.get(), i + 1, poDS->fpImage, nSkipBytes + nBandOffset * i,
     711             :             nPixelOffset, nLineOffset, eDataType,
     712             :             chByteOrder == 'I' || chByteOrder == 'L'
     713          89 :                 ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
     714             :                 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
     715          89 :             RawRasterBand::OwnFP::NO);
     716          89 :         if (!poBand)
     717           0 :             return nullptr;
     718             : 
     719          89 :         if (bNoDataSet)
     720          89 :             poBand->SetNoDataValue(dfNoData);
     721             : 
     722             :         // Set offset/scale values at the PAM level.
     723          89 :         poBand->SetOffset(CPLAtofM(poDS->GetKeyword("QUBE.CORE_BASE", "0.0")));
     724         178 :         poBand->SetScale(
     725          89 :             CPLAtofM(poDS->GetKeyword("QUBE.CORE_MULTIPLIER", "1.0")));
     726             : 
     727          89 :         poDS->SetBand(i + 1, std::move(poBand));
     728             :     }
     729             : 
     730             :     /* -------------------------------------------------------------------- */
     731             :     /*      Check for a .prj file. For isis2 I would like to keep this in   */
     732             :     /* -------------------------------------------------------------------- */
     733          86 :     const CPLString osPath = CPLGetPath(poOpenInfo->pszFilename);
     734          86 :     const CPLString osName = CPLGetBasename(poOpenInfo->pszFilename);
     735          43 :     const char *pszPrjFile = CPLFormCIFilename(osPath, osName, "prj");
     736             : 
     737          43 :     VSILFILE *fp = VSIFOpenL(pszPrjFile, "r");
     738          43 :     if (fp != nullptr)
     739             :     {
     740           0 :         VSIFCloseL(fp);
     741             : 
     742           0 :         char **papszLines = CSLLoad(pszPrjFile);
     743             : 
     744           0 :         poDS->m_oSRS.importFromESRI(papszLines);
     745             : 
     746           0 :         CSLDestroy(papszLines);
     747             :     }
     748             : 
     749          43 :     if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0)
     750             :     {
     751           2 :         poDS->bGotTransform = TRUE;
     752           2 :         poDS->adfGeoTransform[0] = dfULXMap;
     753           2 :         poDS->adfGeoTransform[1] = dfXDim;
     754           2 :         poDS->adfGeoTransform[2] = 0.0;
     755           2 :         poDS->adfGeoTransform[3] = dfULYMap;
     756           2 :         poDS->adfGeoTransform[4] = 0.0;
     757           2 :         poDS->adfGeoTransform[5] = dfYDim;
     758             :     }
     759             : 
     760          43 :     if (!poDS->bGotTransform)
     761          41 :         poDS->bGotTransform = GDALReadWorldFile(poOpenInfo->pszFilename, "cbw",
     762          41 :                                                 poDS->adfGeoTransform);
     763             : 
     764          43 :     if (!poDS->bGotTransform)
     765          41 :         poDS->bGotTransform = GDALReadWorldFile(poOpenInfo->pszFilename, "wld",
     766          41 :                                                 poDS->adfGeoTransform);
     767             : 
     768             :     /* -------------------------------------------------------------------- */
     769             :     /*      Initialize any PAM information.                                 */
     770             :     /* -------------------------------------------------------------------- */
     771          43 :     poDS->SetDescription(poOpenInfo->pszFilename);
     772          43 :     poDS->TryLoadXML();
     773             : 
     774             :     /* -------------------------------------------------------------------- */
     775             :     /*      Check for overviews.                                            */
     776             :     /* -------------------------------------------------------------------- */
     777          43 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     778             : 
     779          43 :     return poDS.release();
     780             : }
     781             : 
     782             : /************************************************************************/
     783             : /*                             GetKeyword()                             */
     784             : /************************************************************************/
     785             : 
     786         943 : const char *ISIS2Dataset::GetKeyword(const char *pszPath,
     787             :                                      const char *pszDefault)
     788             : 
     789             : {
     790         943 :     return oKeywords.GetKeyword(pszPath, pszDefault);
     791             : }
     792             : 
     793             : /************************************************************************/
     794             : /*                            GetKeywordSub()                           */
     795             : /************************************************************************/
     796             : 
     797         279 : const char *ISIS2Dataset::GetKeywordSub(const char *pszPath, int iSubscript,
     798             :                                         const char *pszDefault)
     799             : 
     800             : {
     801         279 :     const char *pszResult = oKeywords.GetKeyword(pszPath, nullptr);
     802             : 
     803         279 :     if (pszResult == nullptr)
     804           0 :         return pszDefault;
     805             : 
     806         279 :     if (pszResult[0] != '(')
     807           0 :         return pszDefault;
     808             : 
     809             :     char **papszTokens =
     810         279 :         CSLTokenizeString2(pszResult, "(,)", CSLT_HONOURSTRINGS);
     811             : 
     812         279 :     if (iSubscript <= CSLCount(papszTokens))
     813             :     {
     814         279 :         oTempResult = papszTokens[iSubscript - 1];
     815         279 :         CSLDestroy(papszTokens);
     816         279 :         return oTempResult.c_str();
     817             :     }
     818             : 
     819           0 :     CSLDestroy(papszTokens);
     820           0 :     return pszDefault;
     821             : }
     822             : 
     823             : /************************************************************************/
     824             : /*                            CleanString()                             */
     825             : /*                                                                      */
     826             : /* Removes single or double quotes, and converts spaces to underscores. */
     827             : /* The change is made in-place to CPLString.                            */
     828             : /************************************************************************/
     829             : 
     830          48 : void ISIS2Dataset::CleanString(CPLString &osInput)
     831             : 
     832             : {
     833          53 :     if ((osInput.size() < 2) ||
     834           5 :         ((osInput.at(0) != '"' || osInput.back() != '"') &&
     835           5 :          (osInput.at(0) != '\'' || osInput.back() != '\'')))
     836          48 :         return;
     837             : 
     838           0 :     char *pszWrk = CPLStrdup(osInput.c_str() + 1);
     839             : 
     840           0 :     pszWrk[strlen(pszWrk) - 1] = '\0';
     841             : 
     842           0 :     for (int i = 0; pszWrk[i] != '\0'; i++)
     843             :     {
     844           0 :         if (pszWrk[i] == ' ')
     845           0 :             pszWrk[i] = '_';
     846             :     }
     847             : 
     848           0 :     osInput = pszWrk;
     849           0 :     CPLFree(pszWrk);
     850             : }
     851             : 
     852             : /************************************************************************/
     853             : /*                           Create()                                   */
     854             : /************************************************************************/
     855             : /**
     856             :  * Hidden Creation Options:
     857             :  * INTERLEAVE=BSQ/BIP/BIL: Force the generation specified type of interleaving.
     858             :  *  BSQ --- band sequental (default),
     859             :  *  BIP --- band interleaved by pixel,
     860             :  *  BIL --- band interleaved by line.
     861             :  * OBJECT=QUBE/IMAGE/SPECTRAL_QUBE, if null default is QUBE
     862             :  */
     863             : 
     864          62 : GDALDataset *ISIS2Dataset::Create(const char *pszFilename, int nXSize,
     865             :                                   int nYSize, int nBandsIn, GDALDataType eType,
     866             :                                   char **papszParamList)
     867             : {
     868             : 
     869             :     /* Verify settings. In Isis 2 core pixel values can be represented in
     870             :      * three different ways : 1, 2 4, or 8 Bytes */
     871          62 :     if (eType != GDT_Byte && eType != GDT_Int16 && eType != GDT_Float32 &&
     872          27 :         eType != GDT_UInt16 && eType != GDT_Float64)
     873             :     {
     874          24 :         CPLError(
     875             :             CE_Failure, CPLE_AppDefined,
     876             :             "The ISIS2 driver does not supporting creating files of type %s.",
     877             :             GDALGetDataTypeName(eType));
     878          24 :         return nullptr;
     879             :     }
     880             : 
     881             :     /*  (SAMPLE, LINE, BAND) - Band Sequential (BSQ) - default choice
     882             :         (SAMPLE, BAND, LINE) - Band Interleaved by Line (BIL)
     883             :         (BAND, SAMPLE, LINE) - Band Interleaved by Pixel (BIP) */
     884          38 :     const char *pszInterleaving = "(SAMPLE,LINE,BAND)";
     885             :     const char *pszInterleavingParam =
     886          38 :         CSLFetchNameValue(papszParamList, "INTERLEAVE");
     887          38 :     if (pszInterleavingParam)
     888             :     {
     889           0 :         if (STARTS_WITH_CI(pszInterleavingParam, "bip"))
     890           0 :             pszInterleaving = "(BAND,SAMPLE,LINE)";
     891           0 :         else if (STARTS_WITH_CI(pszInterleavingParam, "bil"))
     892           0 :             pszInterleaving = "(SAMPLE,BAND,LINE)";
     893             :         else
     894           0 :             pszInterleaving = "(SAMPLE,LINE,BAND)";
     895             :     }
     896             : 
     897             :     /* default labeling method is attached */
     898          38 :     bool bAttachedLabelingMethod = true;
     899             :     /* check if labeling method is set : check the all three first chars */
     900             :     const char *pszLabelingMethod =
     901          38 :         CSLFetchNameValue(papszParamList, "LABELING_METHOD");
     902          38 :     if (pszLabelingMethod)
     903             :     {
     904           1 :         if (STARTS_WITH_CI(pszLabelingMethod, "det" /* "detached" */))
     905             :         {
     906           1 :             bAttachedLabelingMethod = false;
     907             :         }
     908           1 :         if (STARTS_WITH_CI(pszLabelingMethod, "att" /* attached" */))
     909             :         {
     910           0 :             bAttachedLabelingMethod = true;
     911             :         }
     912             :     }
     913             : 
     914             :     /*  set the label and data files */
     915          76 :     CPLString osLabelFile, osRasterFile, osOutFile;
     916          38 :     if (bAttachedLabelingMethod)
     917             :     {
     918          37 :         osLabelFile = "";
     919          37 :         osRasterFile = pszFilename;
     920          37 :         osOutFile = osRasterFile;
     921             :     }
     922             :     else
     923             :     {
     924           1 :         CPLString sExtension = "cub";
     925             :         const char *pszExtension =
     926           1 :             CSLFetchNameValue(papszParamList, "IMAGE_EXTENSION");
     927           1 :         if (pszExtension)
     928             :         {
     929           1 :             sExtension = pszExtension;
     930             :         }
     931             : 
     932           1 :         if (EQUAL(CPLGetExtension(pszFilename), sExtension))
     933             :         {
     934           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     935             :                      "IMAGE_EXTENSION (%s) cannot match LABEL file extension.",
     936             :                      sExtension.c_str());
     937           0 :             return nullptr;
     938             :         }
     939             : 
     940           1 :         osLabelFile = pszFilename;
     941           1 :         osRasterFile = CPLResetExtension(osLabelFile, sExtension);
     942           1 :         osOutFile = osLabelFile;
     943             :     }
     944             : 
     945          38 :     const char *pszObject = CSLFetchNameValue(papszParamList, "OBJECT");
     946          76 :     CPLString sObject = "QUBE";  // default choice
     947          38 :     if (pszObject)
     948             :     {
     949           0 :         if (EQUAL(pszObject, "IMAGE"))
     950             :         {
     951           0 :             sObject = "IMAGE";
     952             :         }
     953           0 :         if (EQUAL(pszObject, "SPECTRAL_QUBE"))
     954             :         {
     955           0 :             sObject = "SPECTRAL_QUBE";
     956             :         }
     957             :     }
     958             : 
     959             :     GUIntBig iRecords =
     960          38 :         ISIS2Dataset::RecordSizeCalculation(nXSize, nYSize, nBandsIn, eType);
     961          38 :     GUIntBig iLabelRecords(2);
     962             : 
     963          38 :     CPLDebug("ISIS2", "irecord = %i", static_cast<int>(iRecords));
     964             : 
     965          38 :     if (bAttachedLabelingMethod)
     966             :     {
     967          37 :         ISIS2Dataset::WriteLabel(osRasterFile, "", sObject, nXSize, nYSize,
     968             :                                  nBandsIn, eType, iRecords, pszInterleaving,
     969             :                                  iLabelRecords, true);
     970             :     }
     971             :     else
     972             :     {
     973           1 :         ISIS2Dataset::WriteLabel(osLabelFile, osRasterFile, sObject, nXSize,
     974             :                                  nYSize, nBandsIn, eType, iRecords,
     975             :                                  pszInterleaving, iLabelRecords);
     976             :     }
     977             : 
     978          38 :     if (!ISIS2Dataset::WriteRaster(osRasterFile, bAttachedLabelingMethod,
     979             :                                    iRecords, iLabelRecords, eType,
     980             :                                    pszInterleaving))
     981          13 :         return nullptr;
     982             : 
     983          25 :     return GDALDataset::FromHandle(GDALOpen(osOutFile, GA_Update));
     984             : }
     985             : 
     986             : /************************************************************************/
     987             : /*                            WriteRaster()                             */
     988             : /************************************************************************/
     989             : 
     990          38 : int ISIS2Dataset::WriteRaster(const std::string &osFilename, bool includeLabel,
     991             :                               GUIntBig iRecords, GUIntBig iLabelRecords,
     992             :                               CPL_UNUSED GDALDataType eType,
     993             :                               CPL_UNUSED const char *pszInterleaving)
     994             : {
     995          38 :     VSILFILE *fpBin = VSIFOpenL(osFilename.c_str(), includeLabel ? "ab" : "wb");
     996          38 :     if (fpBin == nullptr)
     997             :     {
     998           3 :         CPLError(CE_Failure, CPLE_FileIO, "Failed to create %s:\n%s",
     999           3 :                  osFilename.c_str(), VSIStrerror(errno));
    1000           3 :         return FALSE;
    1001             :     }
    1002             : 
    1003          35 :     GUIntBig nSize = iRecords * RECORD_SIZE;
    1004          35 :     CPLDebug("ISIS2", "nSize = %i", static_cast<int>(nSize));
    1005             : 
    1006          35 :     if (includeLabel)
    1007          34 :         nSize = iLabelRecords * RECORD_SIZE + nSize;
    1008             : 
    1009             :     // write last byte
    1010          35 :     const GByte byZero(0);
    1011          70 :     if (VSIFSeekL(fpBin, nSize - 1, SEEK_SET) != 0 ||
    1012          35 :         VSIFWriteL(&byZero, 1, 1, fpBin) != 1)
    1013             :     {
    1014          10 :         CPLError(CE_Failure, CPLE_FileIO, "Failed to write %s:\n%s",
    1015          10 :                  osFilename.c_str(), VSIStrerror(errno));
    1016          10 :         VSIFCloseL(fpBin);
    1017          10 :         return FALSE;
    1018             :     }
    1019          25 :     VSIFCloseL(fpBin);
    1020             : 
    1021          25 :     return TRUE;
    1022             : }
    1023             : 
    1024             : /************************************************************************/
    1025             : /*                       RecordSizeCalculation()                        */
    1026             : /************************************************************************/
    1027          38 : GUIntBig ISIS2Dataset::RecordSizeCalculation(unsigned int nXSize,
    1028             :                                              unsigned int nYSize,
    1029             :                                              unsigned int nBandsIn,
    1030             :                                              GDALDataType eType)
    1031             : 
    1032             : {
    1033          38 :     const GUIntBig n = static_cast<GUIntBig>(nXSize) * nYSize * nBandsIn *
    1034          38 :                        (GDALGetDataTypeSize(eType) / 8);
    1035             :     // size of pds file is a multiple of RECORD_SIZE Bytes.
    1036          38 :     CPLDebug("ISIS2", "n = %i", static_cast<int>(n));
    1037          38 :     CPLDebug("ISIS2", "RECORD SIZE = %i", RECORD_SIZE);
    1038          38 :     CPLDebug("ISIS2", "nXSize = %i", nXSize);
    1039          38 :     CPLDebug("ISIS2", "nYSize = %i", nYSize);
    1040          38 :     CPLDebug("ISIS2", "nBands = %i", nBandsIn);
    1041          38 :     CPLDebug("ISIS2", "DataTypeSize = %i", GDALGetDataTypeSize(eType));
    1042          38 :     return static_cast<GUIntBig>(ceil(static_cast<float>(n) / RECORD_SIZE));
    1043             : }
    1044             : 
    1045             : /************************************************************************/
    1046             : /*                       WriteQUBE_Information()                        */
    1047             : /************************************************************************/
    1048             : 
    1049          35 : int ISIS2Dataset::WriteQUBE_Information(
    1050             :     VSILFILE *fpLabel, unsigned int iLevel, unsigned int &nWritingBytes,
    1051             :     unsigned int nXSize, unsigned int nYSize, unsigned int nBandsIn,
    1052             :     GDALDataType eType, const char *pszInterleaving)
    1053             : 
    1054             : {
    1055          35 :     nWritingBytes += ISIS2Dataset::WriteFormatting(fpLabel, "");
    1056          35 :     nWritingBytes +=
    1057          35 :         ISIS2Dataset::WriteFormatting(fpLabel, "/* Qube structure */");
    1058          35 :     nWritingBytes +=
    1059          35 :         ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "OBJECT", "QUBE");
    1060          35 :     iLevel++;
    1061          35 :     nWritingBytes += ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "AXES", "3");
    1062          35 :     nWritingBytes += ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "AXIS_NAME",
    1063             :                                                 pszInterleaving);
    1064          35 :     nWritingBytes +=
    1065          35 :         ISIS2Dataset::WriteFormatting(fpLabel, "/* Core description */");
    1066             : 
    1067          35 :     CPLDebug("ISIS2", "%d,%d,%d", nXSize, nYSize, nBandsIn);
    1068             : 
    1069          35 :     nWritingBytes += ISIS2Dataset::WriteKeyword(
    1070             :         fpLabel, iLevel, "CORE_ITEMS",
    1071          70 :         CPLString().Printf("(%d,%d,%d)", nXSize, nYSize, nBandsIn));
    1072          35 :     nWritingBytes += ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_NAME",
    1073             :                                                 "\"RAW DATA NUMBER\"");
    1074          35 :     nWritingBytes +=
    1075          35 :         ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_UNIT", "\"N/A\"");
    1076             :     // TODO change for eType
    1077             : 
    1078          35 :     if (eType == GDT_Byte)
    1079             :     {
    1080          22 :         nWritingBytes += ISIS2Dataset::WriteKeyword(
    1081             :             fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_UNSIGNED_INTEGER");
    1082          22 :         nWritingBytes +=
    1083          22 :             ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_ITEM_BYTES", "1");
    1084             :     }
    1085          13 :     else if (eType == GDT_UInt16)
    1086             :     {
    1087           3 :         nWritingBytes += ISIS2Dataset::WriteKeyword(
    1088             :             fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_UNSIGNED_INTEGER");
    1089           3 :         nWritingBytes +=
    1090           3 :             ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_ITEM_BYTES", "2");
    1091             :     }
    1092          10 :     else if (eType == GDT_Int16)
    1093             :     {
    1094           3 :         nWritingBytes += ISIS2Dataset::WriteKeyword(
    1095             :             fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_INTEGER");
    1096           3 :         nWritingBytes +=
    1097           3 :             ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_ITEM_BYTES", "2");
    1098             :     }
    1099           7 :     else if (eType == GDT_Float32)
    1100             :     {
    1101           4 :         nWritingBytes += ISIS2Dataset::WriteKeyword(
    1102             :             fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_REAL");
    1103           4 :         nWritingBytes +=
    1104           4 :             ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_ITEM_BYTES", "4");
    1105             :     }
    1106           3 :     else if (eType == GDT_Float64)
    1107             :     {
    1108           3 :         nWritingBytes += ISIS2Dataset::WriteKeyword(
    1109             :             fpLabel, iLevel, "CORE_ITEM_TYPE", "PC_REAL");
    1110           3 :         nWritingBytes +=
    1111           3 :             ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_ITEM_BYTES", "8");
    1112             :     }
    1113             : 
    1114             :     // TODO add core null value
    1115             : 
    1116          35 :     nWritingBytes +=
    1117          35 :         ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_BASE", "0.0");
    1118          35 :     nWritingBytes +=
    1119          35 :         ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "CORE_MULTIPLIER", "1.0");
    1120          35 :     nWritingBytes +=
    1121          35 :         ISIS2Dataset::WriteFormatting(fpLabel, "/* Suffix description */");
    1122          35 :     nWritingBytes +=
    1123          35 :         ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "SUFFIX_BYTES", "4");
    1124          35 :     nWritingBytes += ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "SUFFIX_ITEMS",
    1125             :                                                 "( 0, 0, 0)");
    1126          35 :     iLevel--;
    1127          35 :     nWritingBytes +=
    1128          35 :         ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "END_OBJECT", "QUBE");
    1129             : 
    1130          35 :     return TRUE;
    1131             : }
    1132             : 
    1133             : /************************************************************************/
    1134             : /*                             WriteLabel()                             */
    1135             : /*                                                                      */
    1136             : /*      osRasterFile : name of raster file but if it is empty we        */
    1137             : /*                     have only one file with an attached label        */
    1138             : /*      sObjectTag : QUBE, IMAGE or SPECTRAL_QUBE                       */
    1139             : /*      bRelaunch : flag to allow recursive call                        */
    1140             : /************************************************************************/
    1141             : 
    1142          38 : int ISIS2Dataset::WriteLabel(const std::string &osFilename,
    1143             :                              const std::string &osRasterFile,
    1144             :                              const std::string &sObjectTag, unsigned int nXSize,
    1145             :                              unsigned int nYSize, unsigned int nBandsIn,
    1146             :                              GDALDataType eType, GUIntBig iRecords,
    1147             :                              const char *pszInterleaving,
    1148             :                              GUIntBig &iLabelRecords, CPL_UNUSED bool bRelaunch)
    1149             : {
    1150          38 :     CPLDebug("ISIS2", "Write Label filename = %s, rasterfile = %s",
    1151             :              osFilename.c_str(), osRasterFile.c_str());
    1152          38 :     bool bAttachedLabel = EQUAL(osRasterFile.c_str(), "");
    1153             : 
    1154          38 :     VSILFILE *fpLabel = VSIFOpenL(osFilename.c_str(), "w");
    1155             : 
    1156          38 :     if (fpLabel == nullptr)
    1157             :     {
    1158           3 :         CPLError(CE_Failure, CPLE_FileIO, "Failed to create %s:\n%s",
    1159           3 :                  osFilename.c_str(), VSIStrerror(errno));
    1160           3 :         return FALSE;
    1161             :     }
    1162             : 
    1163          35 :     const unsigned int iLevel(0);
    1164          35 :     unsigned int nWritingBytes(0);
    1165             : 
    1166             :     /* write common header */
    1167          35 :     nWritingBytes +=
    1168          35 :         ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "PDS_VERSION_ID", "PDS3");
    1169          35 :     nWritingBytes += ISIS2Dataset::WriteFormatting(fpLabel, "");
    1170          35 :     nWritingBytes += ISIS2Dataset::WriteFormatting(
    1171             :         fpLabel, "/* File identification and structure */");
    1172          35 :     nWritingBytes += ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "RECORD_TYPE",
    1173             :                                                 "FIXED_LENGTH");
    1174          35 :     nWritingBytes += ISIS2Dataset::WriteKeyword(
    1175          70 :         fpLabel, iLevel, "RECORD_BYTES", CPLString().Printf("%d", RECORD_SIZE));
    1176          35 :     nWritingBytes +=
    1177          35 :         ISIS2Dataset::WriteKeyword(fpLabel, iLevel, "FILE_RECORDS",
    1178          70 :                                    CPLString().Printf(CPL_FRMT_GUIB, iRecords));
    1179          35 :     nWritingBytes += ISIS2Dataset::WriteKeyword(
    1180             :         fpLabel, iLevel, "LABEL_RECORDS",
    1181          70 :         CPLString().Printf(CPL_FRMT_GUIB, iLabelRecords));
    1182          35 :     if (!bAttachedLabel)
    1183             :     {
    1184           1 :         nWritingBytes += ISIS2Dataset::WriteKeyword(
    1185             :             fpLabel, iLevel, "FILE_NAME", CPLGetFilename(osRasterFile.c_str()));
    1186             :     }
    1187          35 :     nWritingBytes += ISIS2Dataset::WriteFormatting(fpLabel, "");
    1188             : 
    1189          35 :     nWritingBytes += ISIS2Dataset::WriteFormatting(
    1190             :         fpLabel, "/* Pointers to Data Objects */");
    1191             : 
    1192          35 :     if (bAttachedLabel)
    1193             :     {
    1194          34 :         nWritingBytes += ISIS2Dataset::WriteKeyword(
    1195          68 :             fpLabel, iLevel, CPLString().Printf("^%s", sObjectTag.c_str()),
    1196          68 :             CPLString().Printf(CPL_FRMT_GUIB, iLabelRecords + 1));
    1197             :     }
    1198             :     else
    1199             :     {
    1200           1 :         nWritingBytes += ISIS2Dataset::WriteKeyword(
    1201           2 :             fpLabel, iLevel, CPLString().Printf("^%s", sObjectTag.c_str()),
    1202           2 :             CPLString().Printf("(\"%s\",1)",
    1203           1 :                                CPLGetFilename(osRasterFile.c_str())));
    1204             :     }
    1205             : 
    1206          35 :     if (EQUAL(sObjectTag.c_str(), "QUBE"))
    1207             :     {
    1208          35 :         ISIS2Dataset::WriteQUBE_Information(fpLabel, iLevel, nWritingBytes,
    1209             :                                             nXSize, nYSize, nBandsIn, eType,
    1210             :                                             pszInterleaving);
    1211             :     }
    1212             : 
    1213          35 :     nWritingBytes += ISIS2Dataset::WriteFormatting(fpLabel, "END");
    1214             : 
    1215             :     // check if file record is correct
    1216          35 :     const unsigned int q = nWritingBytes / RECORD_SIZE;
    1217          35 :     if (q <= iLabelRecords)
    1218             :     {
    1219             :         // correct we add space after the label end for complete from
    1220             :         // iLabelRecords
    1221          35 :         unsigned int nSpaceBytesToWrite = static_cast<unsigned int>(
    1222          35 :             iLabelRecords * RECORD_SIZE - nWritingBytes);
    1223          35 :         VSIFPrintfL(fpLabel, "%*c", nSpaceBytesToWrite, ' ');
    1224             :     }
    1225             :     else
    1226             :     {
    1227           0 :         iLabelRecords = q + 1;
    1228           0 :         ISIS2Dataset::WriteLabel(osFilename, osRasterFile, sObjectTag, nXSize,
    1229             :                                  nYSize, nBandsIn, eType, iRecords,
    1230             :                                  pszInterleaving, iLabelRecords);
    1231             :     }
    1232          35 :     VSIFCloseL(fpLabel);
    1233             : 
    1234          35 :     return TRUE;
    1235             : }
    1236             : 
    1237             : /************************************************************************/
    1238             : /*                            WriteKeyword()                            */
    1239             : /************************************************************************/
    1240             : 
    1241         666 : unsigned int ISIS2Dataset::WriteKeyword(VSILFILE *fpLabel, unsigned int iLevel,
    1242             :                                         CPLString key, CPLString value)
    1243             : 
    1244             : {
    1245         666 :     CPLString tab = "";
    1246         666 :     iLevel *= 4;  // each struct is indented by 4 spaces.
    1247             : 
    1248         666 :     return VSIFPrintfL(fpLabel, "%*s%s=%s\n", iLevel, tab.c_str(), key.c_str(),
    1249        1332 :                        value.c_str());
    1250             : }
    1251             : 
    1252             : /************************************************************************/
    1253             : /*                          WriteFormatting()                           */
    1254             : /************************************************************************/
    1255             : 
    1256         315 : unsigned int ISIS2Dataset::WriteFormatting(VSILFILE *fpLabel, CPLString data)
    1257             : 
    1258             : {
    1259         315 :     return VSIFPrintfL(fpLabel, "%s\n", data.c_str());
    1260             : }
    1261             : 
    1262             : /************************************************************************/
    1263             : /*                         GDALRegister_ISIS2()                         */
    1264             : /************************************************************************/
    1265             : 
    1266        1511 : void GDALRegister_ISIS2()
    1267             : 
    1268             : {
    1269        1511 :     if (GDALGetDriverByName(ISIS2_DRIVER_NAME) != nullptr)
    1270         295 :         return;
    1271             : 
    1272        1216 :     GDALDriver *poDriver = new GDALDriver();
    1273        1216 :     ISIS2DriverSetCommonMetadata(poDriver);
    1274             : 
    1275        1216 :     poDriver->pfnOpen = ISIS2Dataset::Open;
    1276        1216 :     poDriver->pfnCreate = ISIS2Dataset::Create;
    1277             : 
    1278        1216 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1279             : }

Generated by: LCOV version 1.14