LCOV - code coverage report
Current view: top level - frmts/pds - isis2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 194 327 59.3 %
Date: 2025-06-28 21:28:23 Functions: 12 12 100.0 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  ISIS Version 2 Driver
       4             :  * Purpose:  Implementation of ISIS2Dataset
       5             :  * Author:   Trent Hare (thare at usgs.gov),
       6             :  *           Robert Soricone (rsoricone at usgs.gov)
       7             :  *           Ludovic Mercier (ludovic.mercier at gmail.com)
       8             :  *           Frank Warmerdam (warmerdam at 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 at 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             : #include "cpl_string.h"
      26             : #include "gdal_frmts.h"
      27             : #include "nasakeywordhandler.h"
      28             : #include "ogr_spatialref.h"
      29             : #include "rawdataset.h"
      30             : #include "pdsdrivercore.h"
      31             : 
      32             : /************************************************************************/
      33             : /* ==================================================================== */
      34             : /*                      ISISDataset     version2                        */
      35             : /* ==================================================================== */
      36             : /************************************************************************/
      37             : 
      38             : class ISIS2Dataset final : public RawDataset
      39             : {
      40             :     VSILFILE *fpImage{};  // image data file.
      41             :     CPLString osExternalCube{};
      42             : 
      43             :     NASAKeywordHandler oKeywords{};
      44             : 
      45             :     bool bGotTransform{};
      46             :     GDALGeoTransform m_gt{};
      47             : 
      48             :     OGRSpatialReference m_oSRS{};
      49             : 
      50             :     int parse_label(const char *file, char *keyword, char *value);
      51             :     int strstrip(char instr[], char outstr[], int position);
      52             : 
      53             :     CPLString oTempResult{};
      54             : 
      55             :     static void CleanString(CPLString &osInput);
      56             : 
      57             :     const char *GetKeyword(const char *pszPath, const char *pszDefault = "");
      58             :     const char *GetKeywordSub(const char *pszPath, int iSubscript,
      59             :                               const char *pszDefault = "");
      60             : 
      61             :     CPLErr Close() override;
      62             : 
      63             :     CPL_DISALLOW_COPY_ASSIGN(ISIS2Dataset)
      64             : 
      65             :   public:
      66             :     ISIS2Dataset();
      67             :     virtual ~ISIS2Dataset();
      68             : 
      69             :     virtual CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
      70             :     const OGRSpatialReference *GetSpatialRef() const override;
      71             : 
      72             :     virtual char **GetFileList() override;
      73             : 
      74             :     static GDALDataset *Open(GDALOpenInfo *);
      75             : };
      76             : 
      77             : /************************************************************************/
      78             : /*                            ISIS2Dataset()                            */
      79             : /************************************************************************/
      80             : 
      81           2 : ISIS2Dataset::ISIS2Dataset()
      82             : {
      83           2 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
      84           2 : }
      85             : 
      86             : /************************************************************************/
      87             : /*                            ~ISIS2Dataset()                            */
      88             : /************************************************************************/
      89             : 
      90           4 : ISIS2Dataset::~ISIS2Dataset()
      91             : 
      92             : {
      93           2 :     ISIS2Dataset::Close();
      94           4 : }
      95             : 
      96             : /************************************************************************/
      97             : /*                              Close()                                 */
      98             : /************************************************************************/
      99             : 
     100           4 : CPLErr ISIS2Dataset::Close()
     101             : {
     102           4 :     CPLErr eErr = CE_None;
     103           4 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     104             :     {
     105           2 :         if (ISIS2Dataset::FlushCache(true) != CE_None)
     106           0 :             eErr = CE_Failure;
     107           2 :         if (fpImage != nullptr)
     108             :         {
     109           2 :             if (VSIFCloseL(fpImage) != 0)
     110           0 :                 eErr = CE_Failure;
     111             :         }
     112           2 :         if (GDALPamDataset::Close() != CE_None)
     113           0 :             eErr = CE_Failure;
     114             :     }
     115           4 :     return eErr;
     116             : }
     117             : 
     118             : /************************************************************************/
     119             : /*                            GetFileList()                             */
     120             : /************************************************************************/
     121             : 
     122           1 : char **ISIS2Dataset::GetFileList()
     123             : 
     124             : {
     125           1 :     char **papszFileList = GDALPamDataset::GetFileList();
     126             : 
     127           1 :     if (!osExternalCube.empty())
     128           0 :         papszFileList = CSLAddString(papszFileList, osExternalCube);
     129             : 
     130           1 :     return papszFileList;
     131             : }
     132             : 
     133             : /************************************************************************/
     134             : /*                         GetSpatialRef()                              */
     135             : /************************************************************************/
     136             : 
     137           1 : const OGRSpatialReference *ISIS2Dataset::GetSpatialRef() const
     138             : {
     139           1 :     if (!m_oSRS.IsEmpty())
     140           1 :         return &m_oSRS;
     141           0 :     return GDALPamDataset::GetSpatialRef();
     142             : }
     143             : 
     144             : /************************************************************************/
     145             : /*                          GetGeoTransform()                           */
     146             : /************************************************************************/
     147             : 
     148           1 : CPLErr ISIS2Dataset::GetGeoTransform(GDALGeoTransform &gt) const
     149             : 
     150             : {
     151           1 :     if (bGotTransform)
     152             :     {
     153           1 :         gt = m_gt;
     154           1 :         return CE_None;
     155             :     }
     156             : 
     157           0 :     return GDALPamDataset::GetGeoTransform(gt);
     158             : }
     159             : 
     160             : /************************************************************************/
     161             : /*                                Open()                                */
     162             : /************************************************************************/
     163             : 
     164           2 : GDALDataset *ISIS2Dataset::Open(GDALOpenInfo *poOpenInfo)
     165             : {
     166             :     /* -------------------------------------------------------------------- */
     167             :     /*      Does this look like a CUBE or an IMAGE Primary Data Object?     */
     168             :     /* -------------------------------------------------------------------- */
     169           2 :     if (!ISIS2DriverIdentify(poOpenInfo) || poOpenInfo->fpL == nullptr)
     170           0 :         return nullptr;
     171             : 
     172           2 :     VSILFILE *fpQube = poOpenInfo->fpL;
     173           2 :     poOpenInfo->fpL = nullptr;
     174             : 
     175           4 :     auto poDS = std::make_unique<ISIS2Dataset>();
     176             : 
     177           2 :     if (!poDS->oKeywords.Ingest(fpQube, 0))
     178             :     {
     179           0 :         VSIFCloseL(fpQube);
     180           0 :         return nullptr;
     181             :     }
     182             : 
     183           2 :     VSIFCloseL(fpQube);
     184             : 
     185             :     /* -------------------------------------------------------------------- */
     186             :     /*      We assume the user is pointing to the label (i.e. .lab) file.   */
     187             :     /* -------------------------------------------------------------------- */
     188             :     // QUBE can be inline or detached and point to an image name
     189             :     // ^QUBE = 76
     190             :     // ^QUBE = ("ui31s015.img",6441<BYTES>) - has another label on the image
     191             :     // ^QUBE = "ui31s015.img" - which implies no label or skip value
     192             : 
     193           2 :     const char *pszQube = poDS->GetKeyword("^QUBE");
     194           2 :     int nQube = 0;
     195           2 :     int bByteLocation = FALSE;
     196           4 :     CPLString osTargetFile = poOpenInfo->pszFilename;
     197             : 
     198           2 :     if (pszQube[0] == '"')
     199             :     {
     200           0 :         const CPLString osTPath = CPLGetPathSafe(poOpenInfo->pszFilename);
     201           0 :         CPLString osFilename = pszQube;
     202           0 :         poDS->CleanString(osFilename);
     203           0 :         osTargetFile = CPLFormCIFilenameSafe(osTPath, osFilename, nullptr);
     204           0 :         poDS->osExternalCube = osTargetFile;
     205             :     }
     206           2 :     else if (pszQube[0] == '(')
     207             :     {
     208           0 :         const CPLString osTPath = CPLGetPathSafe(poOpenInfo->pszFilename);
     209           0 :         CPLString osFilename = poDS->GetKeywordSub("^QUBE", 1, "");
     210           0 :         poDS->CleanString(osFilename);
     211           0 :         osTargetFile = CPLFormCIFilenameSafe(osTPath, osFilename, nullptr);
     212           0 :         poDS->osExternalCube = osTargetFile;
     213             : 
     214           0 :         nQube = atoi(poDS->GetKeywordSub("^QUBE", 2, "1"));
     215           0 :         if (strstr(poDS->GetKeywordSub("^QUBE", 2, "1"), "<BYTES>") != nullptr)
     216           0 :             bByteLocation = true;
     217             :     }
     218             :     else
     219             :     {
     220           2 :         nQube = atoi(pszQube);
     221           2 :         if (strstr(pszQube, "<BYTES>") != nullptr)
     222           0 :             bByteLocation = true;
     223             :     }
     224             : 
     225             :     /* -------------------------------------------------------------------- */
     226             :     /*      Check if file an ISIS2 header file?  Read a few lines of text   */
     227             :     /*      searching for something starting with nrows or ncols.           */
     228             :     /* -------------------------------------------------------------------- */
     229             : 
     230             :     /* -------------------------------------------------------------------- */
     231             :     /*      Checks to see if this is valid ISIS2 cube                       */
     232             :     /*      SUFFIX_ITEM tag in .cub file should be (0,0,0); no side-planes  */
     233             :     /* -------------------------------------------------------------------- */
     234           2 :     const int s_ix = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 1));
     235           2 :     const int s_iy = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 2));
     236           2 :     const int s_iz = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 3));
     237             : 
     238           2 :     if (s_ix != 0 || s_iy != 0 || s_iz != 0)
     239             :     {
     240           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     241             :                  "*** ISIS 2 cube file has invalid SUFFIX_ITEMS parameters:\n"
     242             :                  "*** gdal isis2 driver requires (0, 0, 0), thus no sideplanes "
     243             :                  "or backplanes\n"
     244             :                  "found: (%i, %i, %i)\n\n",
     245             :                  s_ix, s_iy, s_iz);
     246           0 :         return nullptr;
     247             :     }
     248             : 
     249             :     /**************** end SUFFIX_ITEM check ***********************/
     250             : 
     251             :     /***********   Grab layout type (BSQ, BIP, BIL) ************/
     252             :     //  AXIS_NAME = (SAMPLE,LINE,BAND)
     253             :     /***********************************************************/
     254             : 
     255           2 :     char szLayout[10] = "BSQ";  // default to band seq.
     256           2 :     const char *value = poDS->GetKeyword("QUBE.AXIS_NAME", "");
     257           2 :     if (EQUAL(value, "(SAMPLE,LINE,BAND)"))
     258           2 :         strcpy(szLayout, "BSQ");
     259           0 :     else if (EQUAL(value, "(BAND,LINE,SAMPLE)"))
     260           0 :         strcpy(szLayout, "BIP");
     261           0 :     else if (EQUAL(value, "(SAMPLE,BAND,LINE)") || EQUAL(value, ""))
     262           0 :         strcpy(szLayout, "BSQ");
     263             :     else
     264             :     {
     265           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     266             :                  "%s layout not supported. Abort\n\n", value);
     267           0 :         return nullptr;
     268             :     }
     269             : 
     270             :     /***********   Grab samples lines band ************/
     271           2 :     const int nCols = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 1));
     272           2 :     const int nRows = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 2));
     273           2 :     const int nBands = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 3));
     274             : 
     275             :     /***********   Grab Qube record bytes  **********/
     276           2 :     const int record_bytes = atoi(poDS->GetKeyword("RECORD_BYTES"));
     277           2 :     if (record_bytes < 0)
     278             :     {
     279           0 :         return nullptr;
     280             :     }
     281             : 
     282           2 :     GUIntBig nSkipBytes = 0;
     283           2 :     if (nQube > 0 && bByteLocation)
     284           0 :         nSkipBytes = (nQube - 1);
     285           2 :     else if (nQube > 0)
     286           2 :         nSkipBytes = static_cast<GUIntBig>(nQube - 1) * record_bytes;
     287             :     else
     288           0 :         nSkipBytes = 0;
     289             : 
     290             :     /***********   Grab samples lines band ************/
     291           2 :     char chByteOrder = 'M';  // default to MSB
     292           4 :     CPLString osCoreItemType = poDS->GetKeyword("QUBE.CORE_ITEM_TYPE");
     293           2 :     if ((EQUAL(osCoreItemType, "PC_INTEGER")) ||
     294           4 :         (EQUAL(osCoreItemType, "PC_UNSIGNED_INTEGER")) ||
     295           2 :         (EQUAL(osCoreItemType, "PC_REAL")))
     296             :     {
     297           0 :         chByteOrder = 'I';
     298             :     }
     299             : 
     300             :     /********   Grab format type - isis2 only supports 8,16,32 *******/
     301           2 :     GDALDataType eDataType = GDT_Byte;
     302           2 :     bool bNoDataSet = false;
     303           2 :     double dfNoData = 0.0;
     304             : 
     305           2 :     int itype = atoi(poDS->GetKeyword("QUBE.CORE_ITEM_BYTES", ""));
     306           2 :     switch (itype)
     307             :     {
     308           0 :         case 1:
     309           0 :             eDataType = GDT_Byte;
     310           0 :             dfNoData = NULL1;
     311           0 :             bNoDataSet = true;
     312           0 :             break;
     313           0 :         case 2:
     314           0 :             if (strstr(osCoreItemType, "UNSIGNED") != nullptr)
     315             :             {
     316           0 :                 dfNoData = 0;
     317           0 :                 eDataType = GDT_UInt16;
     318             :             }
     319             :             else
     320             :             {
     321           0 :                 dfNoData = NULL2;
     322           0 :                 eDataType = GDT_Int16;
     323             :             }
     324           0 :             bNoDataSet = true;
     325           0 :             break;
     326           2 :         case 4:
     327           2 :             eDataType = GDT_Float32;
     328           2 :             dfNoData = NULL3;
     329           2 :             bNoDataSet = true;
     330           2 :             break;
     331           0 :         case 8:
     332           0 :             eDataType = GDT_Float64;
     333           0 :             dfNoData = NULL3;
     334           0 :             bNoDataSet = true;
     335           0 :             break;
     336           0 :         default:
     337           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     338             :                      "Itype of %d is not supported in ISIS 2.", itype);
     339           0 :             return nullptr;
     340             :     }
     341             : 
     342             :     /***********   Grab Cellsize ************/
     343           2 :     double dfXDim = 1.0;
     344           2 :     double dfYDim = 1.0;
     345             : 
     346           2 :     value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.MAP_SCALE");
     347           2 :     if (strlen(value) > 0)
     348             :     {
     349             :         // Convert km to m
     350           2 :         dfXDim = static_cast<float>(CPLAtof(value) * 1000.0);
     351           2 :         dfYDim = static_cast<float>(CPLAtof(value) * 1000.0 * -1);
     352             :     }
     353             : 
     354             :     /***********   Grab LINE_PROJECTION_OFFSET ************/
     355           2 :     double dfULYMap = 0.5;
     356             : 
     357             :     value =
     358           2 :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.LINE_PROJECTION_OFFSET");
     359           2 :     if (strlen(value) > 0)
     360             :     {
     361           2 :         const double yulcenter = static_cast<float>(CPLAtof(value)) * dfYDim;
     362           2 :         dfULYMap = yulcenter - (dfYDim / 2);
     363             :     }
     364             : 
     365             :     /***********   Grab SAMPLE_PROJECTION_OFFSET ************/
     366           2 :     double dfULXMap = 0.5;
     367             : 
     368             :     value =
     369           2 :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.SAMPLE_PROJECTION_OFFSET");
     370           2 :     if (strlen(value) > 0)
     371             :     {
     372           2 :         const double xulcenter = static_cast<float>(CPLAtof(value)) * dfXDim;
     373           2 :         dfULXMap = xulcenter - (dfXDim / 2);
     374             :     }
     375             : 
     376             :     /***********  Grab TARGET_NAME  ************/
     377             :     /**** This is the planets name i.e. MARS ***/
     378           4 :     CPLString target_name = poDS->GetKeyword("QUBE.TARGET_NAME");
     379             : 
     380             :     /***********   Grab MAP_PROJECTION_TYPE ************/
     381             :     CPLString map_proj_name =
     382           4 :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE");
     383           2 :     poDS->CleanString(map_proj_name);
     384             : 
     385             :     /***********   Grab SEMI-MAJOR ************/
     386             :     const double semi_major =
     387           2 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.A_AXIS_RADIUS")) *
     388           2 :         1000.0;
     389             : 
     390             :     /***********   Grab semi-minor ************/
     391             :     const double semi_minor =
     392           2 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.C_AXIS_RADIUS")) *
     393           2 :         1000.0;
     394             : 
     395             :     /***********   Grab CENTER_LAT ************/
     396             :     const double center_lat =
     397           2 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.CENTER_LATITUDE"));
     398             : 
     399             :     /***********   Grab CENTER_LON ************/
     400             :     const double center_lon =
     401           2 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.CENTER_LONGITUDE"));
     402             : 
     403             :     /***********   Grab 1st std parallel ************/
     404           2 :     const double first_std_parallel = CPLAtof(
     405             :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.FIRST_STANDARD_PARALLEL"));
     406             : 
     407             :     /***********   Grab 2nd std parallel ************/
     408           2 :     const double second_std_parallel = CPLAtof(
     409             :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.SECOND_STANDARD_PARALLEL"));
     410             : 
     411             :     /*** grab  PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/
     412             :     // Need to further study how ocentric/ographic will effect the gdal library.
     413             :     // So far we will use this fact to define a sphere or ellipse for some
     414             :     // projections Frank - may need to talk this over
     415           2 :     bool bIsGeographic = true;
     416             :     value =
     417           2 :         poDS->GetKeyword("CUBE.IMAGE_MAP_PROJECTION.PROJECTION_LATITUDE_TYPE");
     418           2 :     if (EQUAL(value, "\"PLANETOCENTRIC\""))
     419           0 :         bIsGeographic = false;
     420             : 
     421           2 :     CPLDebug("ISIS2", "using projection %s", map_proj_name.c_str());
     422             : 
     423           4 :     OGRSpatialReference oSRS;
     424           2 :     oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     425           2 :     bool bProjectionSet = true;
     426             : 
     427             :     // Set oSRS projection and parameters
     428           2 :     if ((EQUAL(map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL")) ||
     429           4 :         (EQUAL(map_proj_name, "EQUIRECTANGULAR")) ||
     430           2 :         (EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")))
     431             :     {
     432           2 :         oSRS.OGRSpatialReference::SetEquirectangular2(0.0, center_lon,
     433             :                                                       center_lat, 0, 0);
     434             :     }
     435           0 :     else if (EQUAL(map_proj_name, "ORTHOGRAPHIC"))
     436             :     {
     437           0 :         oSRS.OGRSpatialReference::SetOrthographic(center_lat, center_lon, 0, 0);
     438             :     }
     439           0 :     else if ((EQUAL(map_proj_name, "SINUSOIDAL")) ||
     440           0 :              (EQUAL(map_proj_name, "SINUSOIDAL_EQUAL-AREA")))
     441             :     {
     442           0 :         oSRS.OGRSpatialReference::SetSinusoidal(center_lon, 0, 0);
     443             :     }
     444           0 :     else if (EQUAL(map_proj_name, "MERCATOR"))
     445             :     {
     446           0 :         oSRS.OGRSpatialReference::SetMercator(center_lat, center_lon, 1, 0, 0);
     447             :     }
     448           0 :     else if (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC"))
     449             :     {
     450           0 :         oSRS.OGRSpatialReference::SetPS(center_lat, center_lon, 1, 0, 0);
     451             :     }
     452           0 :     else if (EQUAL(map_proj_name, "TRANSVERSE_MERCATOR"))
     453             :     {
     454           0 :         oSRS.OGRSpatialReference::SetTM(center_lat, center_lon, 1, 0, 0);
     455             :     }
     456           0 :     else if (EQUAL(map_proj_name, "LAMBERT_CONFORMAL_CONIC"))
     457             :     {
     458           0 :         oSRS.OGRSpatialReference::SetLCC(first_std_parallel,
     459             :                                          second_std_parallel, center_lat,
     460             :                                          center_lon, 0, 0);
     461             :     }
     462           0 :     else if (EQUAL(map_proj_name, ""))
     463             :     {
     464             :         /* no projection */
     465           0 :         bProjectionSet = false;
     466             :     }
     467             :     else
     468             :     {
     469           0 :         CPLDebug("ISIS2",
     470             :                  "Dataset projection %s is not supported. Continuing...",
     471             :                  map_proj_name.c_str());
     472           0 :         bProjectionSet = false;
     473             :     }
     474             : 
     475           2 :     if (bProjectionSet)
     476             :     {
     477             :         // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
     478           6 :         const CPLString proj_target_name = map_proj_name + " " + target_name;
     479           2 :         oSRS.SetProjCS(proj_target_name);  // set ProjCS keyword
     480             : 
     481             :         // The geographic/geocentric name will be the same basic name as the
     482             :         // body name 'GCS' = Geographic/Geocentric Coordinate System
     483           4 :         const CPLString geog_name = "GCS_" + target_name;
     484             : 
     485             :         // The datum and sphere names will be the same basic name aas the planet
     486           4 :         const CPLString datum_name = "D_" + target_name;
     487             : 
     488           4 :         CPLString sphere_name = std::move(target_name);
     489             : 
     490             :         // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
     491           2 :         double iflattening = 0.0;
     492           2 :         if ((semi_major - semi_minor) < 0.0000001)
     493           2 :             iflattening = 0;
     494             :         else
     495           0 :             iflattening = semi_major / (semi_major - semi_minor);
     496             : 
     497             :         // Set the body size but take into consideration which proj is being
     498             :         // used to help w/ proj4 compatibility The use of a Sphere, polar radius
     499             :         // or ellipse here is based on how ISIS does it internally
     500           2 :         if (((EQUAL(map_proj_name, "STEREOGRAPHIC") &&
     501           4 :               (fabs(center_lat) == 90))) ||
     502           2 :             (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC")))
     503             :         {
     504           0 :             if (bIsGeographic)
     505             :             {
     506             :                 // Geograpraphic, so set an ellipse
     507           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     508             :                                iflattening, "Reference_Meridian", 0.0);
     509             :             }
     510             :             else
     511             :             {
     512             :                 // Geocentric, so force a sphere using the semi-minor axis. I
     513             :                 // hope...
     514           0 :                 sphere_name += "_polarRadius";
     515           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_minor,
     516             :                                0.0, "Reference_Meridian", 0.0);
     517             :             }
     518             :         }
     519           2 :         else if ((EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
     520           0 :                  (EQUAL(map_proj_name, "ORTHOGRAPHIC")) ||
     521           0 :                  (EQUAL(map_proj_name, "STEREOGRAPHIC")) ||
     522           2 :                  (EQUAL(map_proj_name, "SINUSOIDAL_EQUAL-AREA")) ||
     523           0 :                  (EQUAL(map_proj_name, "SINUSOIDAL")))
     524             :         {
     525             :             // ISIS uses the spherical equation for these projections so force
     526             :             // a sphere.
     527           2 :             oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
     528             :                            "Reference_Meridian", 0.0);
     529             :         }
     530           0 :         else if ((EQUAL(map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL")) ||
     531           0 :                  (EQUAL(map_proj_name, "EQUIRECTANGULAR")))
     532             :         {
     533             :             // Calculate localRadius using ISIS3 simple elliptical method
     534             :             //   not the more standard Radius of Curvature method
     535             :             // PI = 4 * atan(1);
     536           0 :             if (center_lon == 0)
     537             :             {  // No need to calculate local radius
     538           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     539             :                                0.0, "Reference_Meridian", 0.0);
     540             :             }
     541             :             else
     542             :             {
     543           0 :                 const double radLat = center_lat * M_PI / 180;  // in radians
     544             :                 const double localRadius =
     545           0 :                     semi_major * semi_minor /
     546           0 :                     sqrt(pow(semi_minor * cos(radLat), 2) +
     547           0 :                          pow(semi_major * sin(radLat), 2));
     548           0 :                 sphere_name += "_localRadius";
     549           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, localRadius,
     550             :                                0.0, "Reference_Meridian", 0.0);
     551           0 :                 CPLDebug("ISIS2", "local radius: %f", localRadius);
     552             :             }
     553             :         }
     554             :         else
     555             :         {
     556             :             // All other projections: Mercator, Transverse Mercator, Lambert
     557             :             // Conformal, etc. Geographic, so set an ellipse
     558           0 :             if (bIsGeographic)
     559             :             {
     560           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     561             :                                iflattening, "Reference_Meridian", 0.0);
     562             :             }
     563             :             else
     564             :             {
     565             :                 // Geocentric, so force a sphere. I hope...
     566           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     567             :                                0.0, "Reference_Meridian", 0.0);
     568             :             }
     569             :         }
     570             : 
     571             :         // translate back into a projection string.
     572           2 :         poDS->m_oSRS = std::move(oSRS);
     573             :     }
     574             : 
     575             :     /* END ISIS2 Label Read */
     576             :     /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
     577             : 
     578             :     /* -------------------------------------------------------------------- */
     579             :     /*      Did we get the required keywords?  If not we return with        */
     580             :     /*      this never having been considered to be a match. This isn't     */
     581             :     /*      an error!                                                       */
     582             :     /* -------------------------------------------------------------------- */
     583           4 :     if (!GDALCheckDatasetDimensions(nCols, nRows) ||
     584           2 :         !GDALCheckBandCount(nBands, false))
     585             :     {
     586           0 :         return nullptr;
     587             :     }
     588             : 
     589             :     /* -------------------------------------------------------------------- */
     590             :     /*      Capture some information from the file that is of interest.     */
     591             :     /* -------------------------------------------------------------------- */
     592           2 :     poDS->nRasterXSize = nCols;
     593           2 :     poDS->nRasterYSize = nRows;
     594             : 
     595             :     /* -------------------------------------------------------------------- */
     596             :     /*      Open target binary file.                                        */
     597             :     /* -------------------------------------------------------------------- */
     598             : 
     599           2 :     if (poOpenInfo->eAccess == GA_ReadOnly)
     600           2 :         poDS->fpImage = VSIFOpenL(osTargetFile, "rb");
     601             :     else
     602           0 :         poDS->fpImage = VSIFOpenL(osTargetFile, "r+b");
     603             : 
     604           2 :     if (poDS->fpImage == nullptr)
     605             :     {
     606           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     607             :                  "Failed to open %s with write permission.\n%s",
     608           0 :                  osTargetFile.c_str(), VSIStrerror(errno));
     609           0 :         return nullptr;
     610             :     }
     611             : 
     612           2 :     poDS->eAccess = poOpenInfo->eAccess;
     613             : 
     614             :     /* -------------------------------------------------------------------- */
     615             :     /*      Compute the line offset.                                        */
     616             :     /* -------------------------------------------------------------------- */
     617           2 :     int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
     618             :     int nLineOffset, nPixelOffset;
     619             :     vsi_l_offset nBandOffset;
     620             : 
     621           2 :     if (EQUAL(szLayout, "BIP"))
     622             :     {
     623           0 :         nPixelOffset = nItemSize * nBands;
     624           0 :         if (nPixelOffset > INT_MAX / nBands)
     625             :         {
     626           0 :             return nullptr;
     627             :         }
     628           0 :         nLineOffset = nPixelOffset * nCols;
     629           0 :         nBandOffset = nItemSize;
     630             :     }
     631           2 :     else if (EQUAL(szLayout, "BSQ"))
     632             :     {
     633           2 :         nPixelOffset = nItemSize;
     634           2 :         if (nPixelOffset > INT_MAX / nCols)
     635             :         {
     636           0 :             return nullptr;
     637             :         }
     638           2 :         nLineOffset = nPixelOffset * nCols;
     639           2 :         nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
     640             :     }
     641             :     else /* assume BIL */
     642             :     {
     643           0 :         nPixelOffset = nItemSize;
     644           0 :         if (nPixelOffset > INT_MAX / nBands ||
     645           0 :             nPixelOffset * nBands > INT_MAX / nCols)
     646             :         {
     647           0 :             return nullptr;
     648             :         }
     649           0 :         nLineOffset = nItemSize * nBands * nCols;
     650           0 :         nBandOffset = static_cast<vsi_l_offset>(nItemSize) * nCols;
     651             :     }
     652             : 
     653             :     /* -------------------------------------------------------------------- */
     654             :     /*      Create band information objects.                                */
     655             :     /* -------------------------------------------------------------------- */
     656           4 :     for (int i = 0; i < nBands; i++)
     657             :     {
     658             :         auto poBand = RawRasterBand::Create(
     659           4 :             poDS.get(), i + 1, poDS->fpImage, nSkipBytes + nBandOffset * i,
     660             :             nPixelOffset, nLineOffset, eDataType,
     661             :             chByteOrder == 'I' || chByteOrder == 'L'
     662           2 :                 ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
     663             :                 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
     664           2 :             RawRasterBand::OwnFP::NO);
     665           2 :         if (!poBand)
     666           0 :             return nullptr;
     667             : 
     668           2 :         if (bNoDataSet)
     669           2 :             poBand->SetNoDataValue(dfNoData);
     670             : 
     671             :         // Set offset/scale values at the PAM level.
     672           2 :         poBand->SetOffset(CPLAtofM(poDS->GetKeyword("QUBE.CORE_BASE", "0.0")));
     673           4 :         poBand->SetScale(
     674           2 :             CPLAtofM(poDS->GetKeyword("QUBE.CORE_MULTIPLIER", "1.0")));
     675             : 
     676           2 :         poDS->SetBand(i + 1, std::move(poBand));
     677             :     }
     678             : 
     679             :     /* -------------------------------------------------------------------- */
     680             :     /*      Check for a .prj file. For isis2 I would like to keep this in   */
     681             :     /* -------------------------------------------------------------------- */
     682           4 :     const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
     683           4 :     const CPLString osName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
     684           4 :     const std::string osPrjFile = CPLFormCIFilenameSafe(osPath, osName, "prj");
     685             : 
     686           2 :     VSILFILE *fp = VSIFOpenL(osPrjFile.c_str(), "r");
     687           2 :     if (fp != nullptr)
     688             :     {
     689           0 :         VSIFCloseL(fp);
     690             : 
     691           0 :         char **papszLines = CSLLoad(osPrjFile.c_str());
     692             : 
     693           0 :         poDS->m_oSRS.importFromESRI(papszLines);
     694             : 
     695           0 :         CSLDestroy(papszLines);
     696             :     }
     697             : 
     698           2 :     if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0)
     699             :     {
     700           2 :         poDS->bGotTransform = TRUE;
     701           2 :         poDS->m_gt[0] = dfULXMap;
     702           2 :         poDS->m_gt[1] = dfXDim;
     703           2 :         poDS->m_gt[2] = 0.0;
     704           2 :         poDS->m_gt[3] = dfULYMap;
     705           2 :         poDS->m_gt[4] = 0.0;
     706           2 :         poDS->m_gt[5] = dfYDim;
     707             :     }
     708             : 
     709           2 :     if (!poDS->bGotTransform)
     710           0 :         poDS->bGotTransform = GDALReadWorldFile(poOpenInfo->pszFilename, "cbw",
     711           0 :                                                 poDS->m_gt.data());
     712             : 
     713           2 :     if (!poDS->bGotTransform)
     714           0 :         poDS->bGotTransform = GDALReadWorldFile(poOpenInfo->pszFilename, "wld",
     715           0 :                                                 poDS->m_gt.data());
     716             : 
     717             :     /* -------------------------------------------------------------------- */
     718             :     /*      Initialize any PAM information.                                 */
     719             :     /* -------------------------------------------------------------------- */
     720           2 :     poDS->SetDescription(poOpenInfo->pszFilename);
     721           2 :     poDS->TryLoadXML();
     722             : 
     723             :     /* -------------------------------------------------------------------- */
     724             :     /*      Check for overviews.                                            */
     725             :     /* -------------------------------------------------------------------- */
     726           2 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     727             : 
     728           2 :     return poDS.release();
     729             : }
     730             : 
     731             : /************************************************************************/
     732             : /*                             GetKeyword()                             */
     733             : /************************************************************************/
     734             : 
     735          38 : const char *ISIS2Dataset::GetKeyword(const char *pszPath,
     736             :                                      const char *pszDefault)
     737             : 
     738             : {
     739          38 :     return oKeywords.GetKeyword(pszPath, pszDefault);
     740             : }
     741             : 
     742             : /************************************************************************/
     743             : /*                            GetKeywordSub()                           */
     744             : /************************************************************************/
     745             : 
     746          12 : const char *ISIS2Dataset::GetKeywordSub(const char *pszPath, int iSubscript,
     747             :                                         const char *pszDefault)
     748             : 
     749             : {
     750          12 :     const char *pszResult = oKeywords.GetKeyword(pszPath, nullptr);
     751             : 
     752          12 :     if (pszResult == nullptr)
     753           0 :         return pszDefault;
     754             : 
     755          12 :     if (pszResult[0] != '(')
     756           0 :         return pszDefault;
     757             : 
     758             :     char **papszTokens =
     759          12 :         CSLTokenizeString2(pszResult, "(,)", CSLT_HONOURSTRINGS);
     760             : 
     761          12 :     if (iSubscript <= CSLCount(papszTokens))
     762             :     {
     763          12 :         oTempResult = papszTokens[iSubscript - 1];
     764          12 :         CSLDestroy(papszTokens);
     765          12 :         return oTempResult.c_str();
     766             :     }
     767             : 
     768           0 :     CSLDestroy(papszTokens);
     769           0 :     return pszDefault;
     770             : }
     771             : 
     772             : /************************************************************************/
     773             : /*                            CleanString()                             */
     774             : /*                                                                      */
     775             : /* Removes single or double quotes, and converts spaces to underscores. */
     776             : /* The change is made in-place to CPLString.                            */
     777             : /************************************************************************/
     778             : 
     779           2 : void ISIS2Dataset::CleanString(CPLString &osInput)
     780             : 
     781             : {
     782           4 :     if ((osInput.size() < 2) ||
     783           2 :         ((osInput.at(0) != '"' || osInput.back() != '"') &&
     784           2 :          (osInput.at(0) != '\'' || osInput.back() != '\'')))
     785           2 :         return;
     786             : 
     787           0 :     char *pszWrk = CPLStrdup(osInput.c_str() + 1);
     788             : 
     789           0 :     pszWrk[strlen(pszWrk) - 1] = '\0';
     790             : 
     791           0 :     for (int i = 0; pszWrk[i] != '\0'; i++)
     792             :     {
     793           0 :         if (pszWrk[i] == ' ')
     794           0 :             pszWrk[i] = '_';
     795             :     }
     796             : 
     797           0 :     osInput = pszWrk;
     798           0 :     CPLFree(pszWrk);
     799             : }
     800             : 
     801             : /************************************************************************/
     802             : /*                         GDALRegister_ISIS2()                         */
     803             : /************************************************************************/
     804             : 
     805        1911 : void GDALRegister_ISIS2()
     806             : 
     807             : {
     808        1911 :     if (GDALGetDriverByName(ISIS2_DRIVER_NAME) != nullptr)
     809         282 :         return;
     810             : 
     811        1629 :     GDALDriver *poDriver = new GDALDriver();
     812        1629 :     ISIS2DriverSetCommonMetadata(poDriver);
     813             : 
     814        1629 :     poDriver->pfnOpen = ISIS2Dataset::Open;
     815             : 
     816        1629 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     817             : }

Generated by: LCOV version 1.14