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

Generated by: LCOV version 1.14