LCOV - code coverage report
Current view: top level - frmts/pds - isis2dataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 194 333 58.3 %
Date: 2025-09-10 17:48:50 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             :     ~ISIS2Dataset() override;
      68             : 
      69             :     CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
      70             :     const OGRSpatialReference *GetSpatialRef() const override;
      71             : 
      72             :     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 :         CleanString(osFilename);
     203           0 :         if (CPLHasPathTraversal(osFilename.c_str()))
     204             :         {
     205           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     206             :                      "Path traversal detected in %s", osFilename.c_str());
     207           0 :             return nullptr;
     208             :         }
     209           0 :         osTargetFile = CPLFormCIFilenameSafe(osTPath, osFilename, nullptr);
     210           0 :         poDS->osExternalCube = osTargetFile;
     211             :     }
     212           2 :     else if (pszQube[0] == '(')
     213             :     {
     214           0 :         const CPLString osTPath = CPLGetPathSafe(poOpenInfo->pszFilename);
     215           0 :         CPLString osFilename = poDS->GetKeywordSub("^QUBE", 1, "");
     216           0 :         CleanString(osFilename);
     217           0 :         if (CPLHasPathTraversal(osFilename.c_str()))
     218             :         {
     219           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     220             :                      "Path traversal detected in %s", osFilename.c_str());
     221           0 :             return nullptr;
     222             :         }
     223           0 :         osTargetFile = CPLFormCIFilenameSafe(osTPath, osFilename, nullptr);
     224           0 :         poDS->osExternalCube = osTargetFile;
     225             : 
     226           0 :         nQube = atoi(poDS->GetKeywordSub("^QUBE", 2, "1"));
     227           0 :         if (strstr(poDS->GetKeywordSub("^QUBE", 2, "1"), "<BYTES>") != nullptr)
     228           0 :             bByteLocation = true;
     229             :     }
     230             :     else
     231             :     {
     232           2 :         nQube = atoi(pszQube);
     233           2 :         if (strstr(pszQube, "<BYTES>") != nullptr)
     234           0 :             bByteLocation = true;
     235             :     }
     236             : 
     237             :     /* -------------------------------------------------------------------- */
     238             :     /*      Check if file an ISIS2 header file?  Read a few lines of text   */
     239             :     /*      searching for something starting with nrows or ncols.           */
     240             :     /* -------------------------------------------------------------------- */
     241             : 
     242             :     /* -------------------------------------------------------------------- */
     243             :     /*      Checks to see if this is valid ISIS2 cube                       */
     244             :     /*      SUFFIX_ITEM tag in .cub file should be (0,0,0); no side-planes  */
     245             :     /* -------------------------------------------------------------------- */
     246           2 :     const int s_ix = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 1));
     247           2 :     const int s_iy = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 2));
     248           2 :     const int s_iz = atoi(poDS->GetKeywordSub("QUBE.SUFFIX_ITEMS", 3));
     249             : 
     250           2 :     if (s_ix != 0 || s_iy != 0 || s_iz != 0)
     251             :     {
     252           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     253             :                  "*** ISIS 2 cube file has invalid SUFFIX_ITEMS parameters:\n"
     254             :                  "*** gdal isis2 driver requires (0, 0, 0), thus no sideplanes "
     255             :                  "or backplanes\n"
     256             :                  "found: (%i, %i, %i)\n\n",
     257             :                  s_ix, s_iy, s_iz);
     258           0 :         return nullptr;
     259             :     }
     260             : 
     261             :     /**************** end SUFFIX_ITEM check ***********************/
     262             : 
     263             :     /***********   Grab layout type (BSQ, BIP, BIL) ************/
     264             :     //  AXIS_NAME = (SAMPLE,LINE,BAND)
     265             :     /***********************************************************/
     266             : 
     267           2 :     char szLayout[10] = "BSQ";  // default to band seq.
     268           2 :     const char *value = poDS->GetKeyword("QUBE.AXIS_NAME", "");
     269           2 :     if (EQUAL(value, "(SAMPLE,LINE,BAND)"))
     270           2 :         strcpy(szLayout, "BSQ");
     271           0 :     else if (EQUAL(value, "(BAND,LINE,SAMPLE)"))
     272           0 :         strcpy(szLayout, "BIP");
     273           0 :     else if (EQUAL(value, "(SAMPLE,BAND,LINE)") || EQUAL(value, ""))
     274           0 :         strcpy(szLayout, "BSQ");
     275             :     else
     276             :     {
     277           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     278             :                  "%s layout not supported. Abort\n\n", value);
     279           0 :         return nullptr;
     280             :     }
     281             : 
     282             :     /***********   Grab samples lines band ************/
     283           2 :     const int nCols = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 1));
     284           2 :     const int nRows = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 2));
     285           2 :     const int nBands = atoi(poDS->GetKeywordSub("QUBE.CORE_ITEMS", 3));
     286             : 
     287             :     /***********   Grab Qube record bytes  **********/
     288           2 :     const int record_bytes = atoi(poDS->GetKeyword("RECORD_BYTES"));
     289           2 :     if (record_bytes < 0)
     290             :     {
     291           0 :         return nullptr;
     292             :     }
     293             : 
     294           2 :     GUIntBig nSkipBytes = 0;
     295           2 :     if (nQube > 0 && bByteLocation)
     296           0 :         nSkipBytes = (nQube - 1);
     297           2 :     else if (nQube > 0)
     298           2 :         nSkipBytes = static_cast<GUIntBig>(nQube - 1) * record_bytes;
     299             :     else
     300           0 :         nSkipBytes = 0;
     301             : 
     302             :     /***********   Grab samples lines band ************/
     303           2 :     char chByteOrder = 'M';  // default to MSB
     304           4 :     CPLString osCoreItemType = poDS->GetKeyword("QUBE.CORE_ITEM_TYPE");
     305           2 :     if ((EQUAL(osCoreItemType, "PC_INTEGER")) ||
     306           4 :         (EQUAL(osCoreItemType, "PC_UNSIGNED_INTEGER")) ||
     307           2 :         (EQUAL(osCoreItemType, "PC_REAL")))
     308             :     {
     309           0 :         chByteOrder = 'I';
     310             :     }
     311             : 
     312             :     /********   Grab format type - isis2 only supports 8,16,32 *******/
     313           2 :     GDALDataType eDataType = GDT_Byte;
     314           2 :     bool bNoDataSet = false;
     315           2 :     double dfNoData = 0.0;
     316             : 
     317           2 :     int itype = atoi(poDS->GetKeyword("QUBE.CORE_ITEM_BYTES", ""));
     318           2 :     switch (itype)
     319             :     {
     320           0 :         case 1:
     321           0 :             eDataType = GDT_Byte;
     322           0 :             dfNoData = NULL1;
     323           0 :             bNoDataSet = true;
     324           0 :             break;
     325           0 :         case 2:
     326           0 :             if (strstr(osCoreItemType, "UNSIGNED") != nullptr)
     327             :             {
     328           0 :                 dfNoData = 0;
     329           0 :                 eDataType = GDT_UInt16;
     330             :             }
     331             :             else
     332             :             {
     333           0 :                 dfNoData = NULL2;
     334           0 :                 eDataType = GDT_Int16;
     335             :             }
     336           0 :             bNoDataSet = true;
     337           0 :             break;
     338           2 :         case 4:
     339           2 :             eDataType = GDT_Float32;
     340           2 :             dfNoData = NULL3;
     341           2 :             bNoDataSet = true;
     342           2 :             break;
     343           0 :         case 8:
     344           0 :             eDataType = GDT_Float64;
     345           0 :             dfNoData = NULL3;
     346           0 :             bNoDataSet = true;
     347           0 :             break;
     348           0 :         default:
     349           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     350             :                      "Itype of %d is not supported in ISIS 2.", itype);
     351           0 :             return nullptr;
     352             :     }
     353             : 
     354             :     /***********   Grab Cellsize ************/
     355           2 :     double dfXDim = 1.0;
     356           2 :     double dfYDim = 1.0;
     357             : 
     358           2 :     value = poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.MAP_SCALE");
     359           2 :     if (strlen(value) > 0)
     360             :     {
     361             :         // Convert km to m
     362           2 :         dfXDim = static_cast<float>(CPLAtof(value) * 1000.0);
     363           2 :         dfYDim = static_cast<float>(CPLAtof(value) * 1000.0 * -1);
     364             :     }
     365             : 
     366             :     /***********   Grab LINE_PROJECTION_OFFSET ************/
     367           2 :     double dfULYMap = 0.5;
     368             : 
     369             :     value =
     370           2 :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.LINE_PROJECTION_OFFSET");
     371           2 :     if (strlen(value) > 0)
     372             :     {
     373           2 :         const double yulcenter = static_cast<float>(CPLAtof(value)) * dfYDim;
     374           2 :         dfULYMap = yulcenter - (dfYDim / 2);
     375             :     }
     376             : 
     377             :     /***********   Grab SAMPLE_PROJECTION_OFFSET ************/
     378           2 :     double dfULXMap = 0.5;
     379             : 
     380             :     value =
     381           2 :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.SAMPLE_PROJECTION_OFFSET");
     382           2 :     if (strlen(value) > 0)
     383             :     {
     384           2 :         const double xulcenter = static_cast<float>(CPLAtof(value)) * dfXDim;
     385           2 :         dfULXMap = xulcenter - (dfXDim / 2);
     386             :     }
     387             : 
     388             :     /***********  Grab TARGET_NAME  ************/
     389             :     /**** This is the planets name i.e. MARS ***/
     390           4 :     CPLString target_name = poDS->GetKeyword("QUBE.TARGET_NAME");
     391             : 
     392             :     /***********   Grab MAP_PROJECTION_TYPE ************/
     393             :     CPLString map_proj_name =
     394           4 :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE");
     395           2 :     CleanString(map_proj_name);
     396             : 
     397             :     /***********   Grab SEMI-MAJOR ************/
     398             :     const double semi_major =
     399           2 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.A_AXIS_RADIUS")) *
     400           2 :         1000.0;
     401             : 
     402             :     /***********   Grab semi-minor ************/
     403             :     const double semi_minor =
     404           2 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.C_AXIS_RADIUS")) *
     405           2 :         1000.0;
     406             : 
     407             :     /***********   Grab CENTER_LAT ************/
     408             :     const double center_lat =
     409           2 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.CENTER_LATITUDE"));
     410             : 
     411             :     /***********   Grab CENTER_LON ************/
     412             :     const double center_lon =
     413           2 :         CPLAtof(poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.CENTER_LONGITUDE"));
     414             : 
     415             :     /***********   Grab 1st std parallel ************/
     416           2 :     const double first_std_parallel = CPLAtof(
     417             :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.FIRST_STANDARD_PARALLEL"));
     418             : 
     419             :     /***********   Grab 2nd std parallel ************/
     420           2 :     const double second_std_parallel = CPLAtof(
     421             :         poDS->GetKeyword("QUBE.IMAGE_MAP_PROJECTION.SECOND_STANDARD_PARALLEL"));
     422             : 
     423             :     /*** grab  PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/
     424             :     // Need to further study how ocentric/ographic will effect the gdal library.
     425             :     // So far we will use this fact to define a sphere or ellipse for some
     426             :     // projections Frank - may need to talk this over
     427           2 :     bool bIsGeographic = true;
     428             :     value =
     429           2 :         poDS->GetKeyword("CUBE.IMAGE_MAP_PROJECTION.PROJECTION_LATITUDE_TYPE");
     430           2 :     if (EQUAL(value, "\"PLANETOCENTRIC\""))
     431           0 :         bIsGeographic = false;
     432             : 
     433           2 :     CPLDebug("ISIS2", "using projection %s", map_proj_name.c_str());
     434             : 
     435           4 :     OGRSpatialReference oSRS;
     436           2 :     oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     437           2 :     bool bProjectionSet = true;
     438             : 
     439             :     // Set oSRS projection and parameters
     440           2 :     if ((EQUAL(map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL")) ||
     441           4 :         (EQUAL(map_proj_name, "EQUIRECTANGULAR")) ||
     442           2 :         (EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")))
     443             :     {
     444           2 :         oSRS.OGRSpatialReference::SetEquirectangular2(0.0, center_lon,
     445             :                                                       center_lat, 0, 0);
     446             :     }
     447           0 :     else if (EQUAL(map_proj_name, "ORTHOGRAPHIC"))
     448             :     {
     449           0 :         oSRS.OGRSpatialReference::SetOrthographic(center_lat, center_lon, 0, 0);
     450             :     }
     451           0 :     else if ((EQUAL(map_proj_name, "SINUSOIDAL")) ||
     452           0 :              (EQUAL(map_proj_name, "SINUSOIDAL_EQUAL-AREA")))
     453             :     {
     454           0 :         oSRS.OGRSpatialReference::SetSinusoidal(center_lon, 0, 0);
     455             :     }
     456           0 :     else if (EQUAL(map_proj_name, "MERCATOR"))
     457             :     {
     458           0 :         oSRS.OGRSpatialReference::SetMercator(center_lat, center_lon, 1, 0, 0);
     459             :     }
     460           0 :     else if (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC"))
     461             :     {
     462           0 :         oSRS.OGRSpatialReference::SetPS(center_lat, center_lon, 1, 0, 0);
     463             :     }
     464           0 :     else if (EQUAL(map_proj_name, "TRANSVERSE_MERCATOR"))
     465             :     {
     466           0 :         oSRS.OGRSpatialReference::SetTM(center_lat, center_lon, 1, 0, 0);
     467             :     }
     468           0 :     else if (EQUAL(map_proj_name, "LAMBERT_CONFORMAL_CONIC"))
     469             :     {
     470           0 :         oSRS.OGRSpatialReference::SetLCC(first_std_parallel,
     471             :                                          second_std_parallel, center_lat,
     472             :                                          center_lon, 0, 0);
     473             :     }
     474           0 :     else if (EQUAL(map_proj_name, ""))
     475             :     {
     476             :         /* no projection */
     477           0 :         bProjectionSet = false;
     478             :     }
     479             :     else
     480             :     {
     481           0 :         CPLDebug("ISIS2",
     482             :                  "Dataset projection %s is not supported. Continuing...",
     483             :                  map_proj_name.c_str());
     484           0 :         bProjectionSet = false;
     485             :     }
     486             : 
     487           2 :     if (bProjectionSet)
     488             :     {
     489             :         // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
     490           6 :         const CPLString proj_target_name = map_proj_name + " " + target_name;
     491           2 :         oSRS.SetProjCS(proj_target_name);  // set ProjCS keyword
     492             : 
     493             :         // The geographic/geocentric name will be the same basic name as the
     494             :         // body name 'GCS' = Geographic/Geocentric Coordinate System
     495           4 :         const CPLString geog_name = "GCS_" + target_name;
     496             : 
     497             :         // The datum and sphere names will be the same basic name aas the planet
     498           4 :         const CPLString datum_name = "D_" + target_name;
     499             : 
     500           4 :         CPLString sphere_name = std::move(target_name);
     501             : 
     502             :         // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
     503           2 :         double iflattening = 0.0;
     504           2 :         if ((semi_major - semi_minor) < 0.0000001)
     505           2 :             iflattening = 0;
     506             :         else
     507           0 :             iflattening = semi_major / (semi_major - semi_minor);
     508             : 
     509             :         // Set the body size but take into consideration which proj is being
     510             :         // used to help w/ proj4 compatibility The use of a Sphere, polar radius
     511             :         // or ellipse here is based on how ISIS does it internally
     512           2 :         if (((EQUAL(map_proj_name, "STEREOGRAPHIC") &&
     513           4 :               (fabs(center_lat) == 90))) ||
     514           2 :             (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC")))
     515             :         {
     516           0 :             if (bIsGeographic)
     517             :             {
     518             :                 // Geograpraphic, so set an ellipse
     519           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     520             :                                iflattening, "Reference_Meridian", 0.0);
     521             :             }
     522             :             else
     523             :             {
     524             :                 // Geocentric, so force a sphere using the semi-minor axis. I
     525             :                 // hope...
     526           0 :                 sphere_name += "_polarRadius";
     527           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_minor,
     528             :                                0.0, "Reference_Meridian", 0.0);
     529             :             }
     530             :         }
     531           2 :         else if ((EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
     532           0 :                  (EQUAL(map_proj_name, "ORTHOGRAPHIC")) ||
     533           0 :                  (EQUAL(map_proj_name, "STEREOGRAPHIC")) ||
     534           2 :                  (EQUAL(map_proj_name, "SINUSOIDAL_EQUAL-AREA")) ||
     535           0 :                  (EQUAL(map_proj_name, "SINUSOIDAL")))
     536             :         {
     537             :             // ISIS uses the spherical equation for these projections so force
     538             :             // a sphere.
     539           2 :             oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
     540             :                            "Reference_Meridian", 0.0);
     541             :         }
     542           0 :         else if ((EQUAL(map_proj_name, "EQUIRECTANGULAR_CYLINDRICAL")) ||
     543           0 :                  (EQUAL(map_proj_name, "EQUIRECTANGULAR")))
     544             :         {
     545             :             // Calculate localRadius using ISIS3 simple elliptical method
     546             :             //   not the more standard Radius of Curvature method
     547             :             // PI = 4 * atan(1);
     548           0 :             if (center_lon == 0)
     549             :             {  // No need to calculate local radius
     550           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     551             :                                0.0, "Reference_Meridian", 0.0);
     552             :             }
     553             :             else
     554             :             {
     555           0 :                 const double radLat = center_lat * M_PI / 180;  // in radians
     556             :                 const double localRadius =
     557           0 :                     semi_major * semi_minor /
     558           0 :                     sqrt(pow(semi_minor * cos(radLat), 2) +
     559           0 :                          pow(semi_major * sin(radLat), 2));
     560           0 :                 sphere_name += "_localRadius";
     561           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, localRadius,
     562             :                                0.0, "Reference_Meridian", 0.0);
     563           0 :                 CPLDebug("ISIS2", "local radius: %f", localRadius);
     564             :             }
     565             :         }
     566             :         else
     567             :         {
     568             :             // All other projections: Mercator, Transverse Mercator, Lambert
     569             :             // Conformal, etc. Geographic, so set an ellipse
     570           0 :             if (bIsGeographic)
     571             :             {
     572           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     573             :                                iflattening, "Reference_Meridian", 0.0);
     574             :             }
     575             :             else
     576             :             {
     577             :                 // Geocentric, so force a sphere. I hope...
     578           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     579             :                                0.0, "Reference_Meridian", 0.0);
     580             :             }
     581             :         }
     582             : 
     583             :         // translate back into a projection string.
     584           2 :         poDS->m_oSRS = std::move(oSRS);
     585             :     }
     586             : 
     587             :     /* END ISIS2 Label Read */
     588             :     /*++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++*/
     589             : 
     590             :     /* -------------------------------------------------------------------- */
     591             :     /*      Did we get the required keywords?  If not we return with        */
     592             :     /*      this never having been considered to be a match. This isn't     */
     593             :     /*      an error!                                                       */
     594             :     /* -------------------------------------------------------------------- */
     595           4 :     if (!GDALCheckDatasetDimensions(nCols, nRows) ||
     596           2 :         !GDALCheckBandCount(nBands, false))
     597             :     {
     598           0 :         return nullptr;
     599             :     }
     600             : 
     601             :     /* -------------------------------------------------------------------- */
     602             :     /*      Capture some information from the file that is of interest.     */
     603             :     /* -------------------------------------------------------------------- */
     604           2 :     poDS->nRasterXSize = nCols;
     605           2 :     poDS->nRasterYSize = nRows;
     606             : 
     607             :     /* -------------------------------------------------------------------- */
     608             :     /*      Open target binary file.                                        */
     609             :     /* -------------------------------------------------------------------- */
     610             : 
     611           2 :     if (poOpenInfo->eAccess == GA_ReadOnly)
     612           2 :         poDS->fpImage = VSIFOpenL(osTargetFile, "rb");
     613             :     else
     614           0 :         poDS->fpImage = VSIFOpenL(osTargetFile, "r+b");
     615             : 
     616           2 :     if (poDS->fpImage == nullptr)
     617             :     {
     618           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     619             :                  "Failed to open %s with write permission.\n%s",
     620           0 :                  osTargetFile.c_str(), VSIStrerror(errno));
     621           0 :         return nullptr;
     622             :     }
     623             : 
     624           2 :     poDS->eAccess = poOpenInfo->eAccess;
     625             : 
     626             :     /* -------------------------------------------------------------------- */
     627             :     /*      Compute the line offset.                                        */
     628             :     /* -------------------------------------------------------------------- */
     629           2 :     int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
     630             :     int nLineOffset, nPixelOffset;
     631             :     vsi_l_offset nBandOffset;
     632             : 
     633           2 :     if (EQUAL(szLayout, "BIP"))
     634             :     {
     635           0 :         nPixelOffset = nItemSize * nBands;
     636           0 :         if (nPixelOffset > INT_MAX / nBands)
     637             :         {
     638           0 :             return nullptr;
     639             :         }
     640           0 :         nLineOffset = nPixelOffset * nCols;
     641           0 :         nBandOffset = nItemSize;
     642             :     }
     643           2 :     else if (EQUAL(szLayout, "BSQ"))
     644             :     {
     645           2 :         nPixelOffset = nItemSize;
     646           2 :         if (nPixelOffset > INT_MAX / nCols)
     647             :         {
     648           0 :             return nullptr;
     649             :         }
     650           2 :         nLineOffset = nPixelOffset * nCols;
     651           2 :         nBandOffset = static_cast<vsi_l_offset>(nLineOffset) * nRows;
     652             :     }
     653             :     else /* assume BIL */
     654             :     {
     655           0 :         nPixelOffset = nItemSize;
     656           0 :         if (nPixelOffset > INT_MAX / nBands ||
     657           0 :             nPixelOffset * nBands > INT_MAX / nCols)
     658             :         {
     659           0 :             return nullptr;
     660             :         }
     661           0 :         nLineOffset = nItemSize * nBands * nCols;
     662           0 :         nBandOffset = static_cast<vsi_l_offset>(nItemSize) * nCols;
     663             :     }
     664             : 
     665             :     /* -------------------------------------------------------------------- */
     666             :     /*      Create band information objects.                                */
     667             :     /* -------------------------------------------------------------------- */
     668           4 :     for (int i = 0; i < nBands; i++)
     669             :     {
     670             :         auto poBand = RawRasterBand::Create(
     671           4 :             poDS.get(), i + 1, poDS->fpImage, nSkipBytes + nBandOffset * i,
     672             :             nPixelOffset, nLineOffset, eDataType,
     673             :             chByteOrder == 'I' || chByteOrder == 'L'
     674           2 :                 ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
     675             :                 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
     676           2 :             RawRasterBand::OwnFP::NO);
     677           2 :         if (!poBand)
     678           0 :             return nullptr;
     679             : 
     680           2 :         if (bNoDataSet)
     681           2 :             poBand->SetNoDataValue(dfNoData);
     682             : 
     683             :         // Set offset/scale values at the PAM level.
     684           2 :         poBand->SetOffset(CPLAtofM(poDS->GetKeyword("QUBE.CORE_BASE", "0.0")));
     685           4 :         poBand->SetScale(
     686           2 :             CPLAtofM(poDS->GetKeyword("QUBE.CORE_MULTIPLIER", "1.0")));
     687             : 
     688           2 :         poDS->SetBand(i + 1, std::move(poBand));
     689             :     }
     690             : 
     691             :     /* -------------------------------------------------------------------- */
     692             :     /*      Check for a .prj file. For isis2 I would like to keep this in   */
     693             :     /* -------------------------------------------------------------------- */
     694           4 :     const CPLString osPath = CPLGetPathSafe(poOpenInfo->pszFilename);
     695           4 :     const CPLString osName = CPLGetBasenameSafe(poOpenInfo->pszFilename);
     696           4 :     const std::string osPrjFile = CPLFormCIFilenameSafe(osPath, osName, "prj");
     697             : 
     698           2 :     VSILFILE *fp = VSIFOpenL(osPrjFile.c_str(), "r");
     699           2 :     if (fp != nullptr)
     700             :     {
     701           0 :         VSIFCloseL(fp);
     702             : 
     703           0 :         char **papszLines = CSLLoad(osPrjFile.c_str());
     704             : 
     705           0 :         poDS->m_oSRS.importFromESRI(papszLines);
     706             : 
     707           0 :         CSLDestroy(papszLines);
     708             :     }
     709             : 
     710           2 :     if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0)
     711             :     {
     712           2 :         poDS->bGotTransform = TRUE;
     713           2 :         poDS->m_gt[0] = dfULXMap;
     714           2 :         poDS->m_gt[1] = dfXDim;
     715           2 :         poDS->m_gt[2] = 0.0;
     716           2 :         poDS->m_gt[3] = dfULYMap;
     717           2 :         poDS->m_gt[4] = 0.0;
     718           2 :         poDS->m_gt[5] = dfYDim;
     719             :     }
     720             : 
     721           2 :     if (!poDS->bGotTransform)
     722           0 :         poDS->bGotTransform = GDALReadWorldFile(poOpenInfo->pszFilename, "cbw",
     723           0 :                                                 poDS->m_gt.data());
     724             : 
     725           2 :     if (!poDS->bGotTransform)
     726           0 :         poDS->bGotTransform = GDALReadWorldFile(poOpenInfo->pszFilename, "wld",
     727           0 :                                                 poDS->m_gt.data());
     728             : 
     729             :     /* -------------------------------------------------------------------- */
     730             :     /*      Initialize any PAM information.                                 */
     731             :     /* -------------------------------------------------------------------- */
     732           2 :     poDS->SetDescription(poOpenInfo->pszFilename);
     733           2 :     poDS->TryLoadXML();
     734             : 
     735             :     /* -------------------------------------------------------------------- */
     736             :     /*      Check for overviews.                                            */
     737             :     /* -------------------------------------------------------------------- */
     738           2 :     poDS->oOvManager.Initialize(poDS.get(), poOpenInfo->pszFilename);
     739             : 
     740           2 :     return poDS.release();
     741             : }
     742             : 
     743             : /************************************************************************/
     744             : /*                             GetKeyword()                             */
     745             : /************************************************************************/
     746             : 
     747          38 : const char *ISIS2Dataset::GetKeyword(const char *pszPath,
     748             :                                      const char *pszDefault)
     749             : 
     750             : {
     751          38 :     return oKeywords.GetKeyword(pszPath, pszDefault);
     752             : }
     753             : 
     754             : /************************************************************************/
     755             : /*                            GetKeywordSub()                           */
     756             : /************************************************************************/
     757             : 
     758          12 : const char *ISIS2Dataset::GetKeywordSub(const char *pszPath, int iSubscript,
     759             :                                         const char *pszDefault)
     760             : 
     761             : {
     762          12 :     const char *pszResult = oKeywords.GetKeyword(pszPath, nullptr);
     763             : 
     764          12 :     if (pszResult == nullptr)
     765           0 :         return pszDefault;
     766             : 
     767          12 :     if (pszResult[0] != '(')
     768           0 :         return pszDefault;
     769             : 
     770             :     char **papszTokens =
     771          12 :         CSLTokenizeString2(pszResult, "(,)", CSLT_HONOURSTRINGS);
     772             : 
     773          12 :     if (iSubscript <= CSLCount(papszTokens))
     774             :     {
     775          12 :         oTempResult = papszTokens[iSubscript - 1];
     776          12 :         CSLDestroy(papszTokens);
     777          12 :         return oTempResult.c_str();
     778             :     }
     779             : 
     780           0 :     CSLDestroy(papszTokens);
     781           0 :     return pszDefault;
     782             : }
     783             : 
     784             : /************************************************************************/
     785             : /*                            CleanString()                             */
     786             : /*                                                                      */
     787             : /* Removes single or double quotes, and converts spaces to underscores. */
     788             : /* The change is made in-place to CPLString.                            */
     789             : /************************************************************************/
     790             : 
     791           2 : void ISIS2Dataset::CleanString(CPLString &osInput)
     792             : 
     793             : {
     794           4 :     if ((osInput.size() < 2) ||
     795           2 :         ((osInput.at(0) != '"' || osInput.back() != '"') &&
     796           2 :          (osInput.at(0) != '\'' || osInput.back() != '\'')))
     797           2 :         return;
     798             : 
     799           0 :     char *pszWrk = CPLStrdup(osInput.c_str() + 1);
     800             : 
     801           0 :     pszWrk[strlen(pszWrk) - 1] = '\0';
     802             : 
     803           0 :     for (int i = 0; pszWrk[i] != '\0'; i++)
     804             :     {
     805           0 :         if (pszWrk[i] == ' ')
     806           0 :             pszWrk[i] = '_';
     807             :     }
     808             : 
     809           0 :     osInput = pszWrk;
     810           0 :     CPLFree(pszWrk);
     811             : }
     812             : 
     813             : /************************************************************************/
     814             : /*                         GDALRegister_ISIS2()                         */
     815             : /************************************************************************/
     816             : 
     817        2024 : void GDALRegister_ISIS2()
     818             : 
     819             : {
     820        2024 :     if (GDALGetDriverByName(ISIS2_DRIVER_NAME) != nullptr)
     821         283 :         return;
     822             : 
     823        1741 :     GDALDriver *poDriver = new GDALDriver();
     824        1741 :     ISIS2DriverSetCommonMetadata(poDriver);
     825             : 
     826        1741 :     poDriver->pfnOpen = ISIS2Dataset::Open;
     827             : 
     828        1741 :     GetGDALDriverManager()->RegisterDriver(poDriver);
     829             : }

Generated by: LCOV version 1.14