LCOV - code coverage report
Current view: top level - frmts/pds - pdsdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 494 640 77.2 %
Date: 2025-07-09 17:50:03 Functions: 23 26 88.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  PDS Driver; Planetary Data System Format
       4             :  * Purpose:  Implementation of PDSDataset
       5             :  * Author:   Trent Hare (thare at usgs.gov),
       6             :  *           Robert Soricone (rsoricone at usgs.gov)
       7             :  *
       8             :  * NOTE: Original code authored by Trent and Robert and placed in the public
       9             :  * domain as per US government policy.  I have (within my rights) appropriated
      10             :  * it and placed it under the following license.  This is not intended to
      11             :  * diminish Trent and Roberts contribution.
      12             :  ******************************************************************************
      13             :  * Copyright (c) 2007, Frank Warmerdam <warmerdam at pobox.com>
      14             :  * Copyright (c) 2008-2013, Even Rouault <even dot rouault at spatialys.com>
      15             :  *
      16             :  * SPDX-License-Identifier: MIT
      17             :  ****************************************************************************/
      18             : 
      19             : // Set up PDS NULL values
      20             : constexpr int PDS_NULL1 = 0;
      21             : constexpr int PDS_NULL2 = -32768;
      22             : // #define NULL3 -0.3402822655089E+39
      23             : // Same as ESRI_GRID_FLOAT_NO_DATA
      24             : // #define NULL3 -340282346638528859811704183484516925440.0
      25             : constexpr double PDS_NULL3 = -3.4028226550889044521e+38;
      26             : 
      27             : #include "cpl_string.h"
      28             : #include "gdal_frmts.h"
      29             : #include "gdal_proxy.h"
      30             : #include "nasakeywordhandler.h"
      31             : #include "ogr_spatialref.h"
      32             : #include "rawdataset.h"
      33             : #include "cpl_safemaths.hpp"
      34             : #include "vicardataset.h"
      35             : #include "pdsdrivercore.h"
      36             : 
      37             : #include <array>
      38             : 
      39             : enum PDSLayout
      40             : {
      41             :     PDS_BSQ,
      42             :     PDS_BIP,
      43             :     PDS_BIL
      44             : };
      45             : 
      46             : /************************************************************************/
      47             : /* ==================================================================== */
      48             : /*                             PDSDataset                               */
      49             : /* ==================================================================== */
      50             : /************************************************************************/
      51             : 
      52             : class PDSDataset final : public RawDataset
      53             : {
      54             :     VSILFILE *fpImage{};  // image data file.
      55             :     GDALDataset *poCompressedDS{};
      56             : 
      57             :     NASAKeywordHandler oKeywords{};
      58             : 
      59             :     bool bGotTransform{};
      60             :     GDALGeoTransform m_gt{};
      61             : 
      62             :     OGRSpatialReference m_oSRS{};
      63             : 
      64             :     CPLString osTempResult{};
      65             : 
      66             :     CPLString osExternalCube{};
      67             :     CPLString m_osImageFilename{};
      68             : 
      69             :     CPLStringList m_aosPDSMD{};
      70             : 
      71             :     void ParseSRS();
      72             :     int ParseCompressedImage();
      73             :     int ParseImage(const CPLString &osPrefix,
      74             :                    const CPLString &osFilenamePrefix);
      75             :     static CPLString CleanString(const CPLString &osInput);
      76             : 
      77             :     const char *GetKeyword(const std::string &osPath,
      78             :                            const char *pszDefault = "");
      79             :     const char *GetKeywordSub(const std::string &osPath, int iSubscript,
      80             :                               const char *pszDefault = "");
      81             :     const char *GetKeywordUnit(const char *pszPath, int iSubscript,
      82             :                                const char *pszDefault = "");
      83             : 
      84             :     CPL_DISALLOW_COPY_ASSIGN(PDSDataset)
      85             : 
      86             :   protected:
      87             :     virtual int CloseDependentDatasets() override;
      88             : 
      89             :     CPLErr Close() override;
      90             : 
      91             :   public:
      92             :     PDSDataset();
      93             :     virtual ~PDSDataset();
      94             : 
      95             :     virtual CPLErr GetGeoTransform(GDALGeoTransform &gt) const override;
      96             :     const OGRSpatialReference *GetSpatialRef() const override;
      97             : 
      98             :     virtual char **GetFileList(void) override;
      99             : 
     100             :     virtual CPLErr IBuildOverviews(const char *, int, const int *, int,
     101             :                                    const int *, GDALProgressFunc, void *,
     102             :                                    CSLConstList papszOptions) override;
     103             : 
     104             :     virtual CPLErr IRasterIO(GDALRWFlag, int, int, int, int, void *, int, int,
     105             :                              GDALDataType, int, BANDMAP_TYPE,
     106             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
     107             :                              GSpacing nBandSpace,
     108             :                              GDALRasterIOExtraArg *psExtraArg) override;
     109             : 
     110             :     bool GetRawBinaryLayout(GDALDataset::RawBinaryLayout &) override;
     111             : 
     112             :     char **GetMetadataDomainList() override;
     113             :     char **GetMetadata(const char *pszDomain = "") override;
     114             : 
     115             :     static GDALDataset *Open(GDALOpenInfo *);
     116             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     117             :                                int nBands, GDALDataType eType,
     118             :                                char **papszParamList);
     119             : };
     120             : 
     121             : /************************************************************************/
     122             : /*                            PDSDataset()                            */
     123             : /************************************************************************/
     124             : 
     125          38 : PDSDataset::PDSDataset()
     126             : {
     127          38 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     128          38 : }
     129             : 
     130             : /************************************************************************/
     131             : /*                            ~PDSDataset()                            */
     132             : /************************************************************************/
     133             : 
     134          76 : PDSDataset::~PDSDataset()
     135             : 
     136             : {
     137          38 :     PDSDataset::Close();
     138          76 : }
     139             : 
     140             : /************************************************************************/
     141             : /*                              Close()                                 */
     142             : /************************************************************************/
     143             : 
     144          74 : CPLErr PDSDataset::Close()
     145             : {
     146          74 :     CPLErr eErr = CE_None;
     147          74 :     if (nOpenFlags != OPEN_FLAGS_CLOSED)
     148             :     {
     149          38 :         if (PDSDataset::FlushCache(true) != CE_None)
     150           0 :             eErr = CE_Failure;
     151          38 :         if (fpImage)
     152             :         {
     153          33 :             if (VSIFCloseL(fpImage) != 0)
     154           0 :                 eErr = CE_Failure;
     155             :         }
     156             : 
     157          38 :         PDSDataset::CloseDependentDatasets();
     158          38 :         if (GDALPamDataset::Close() != CE_None)
     159           0 :             eErr = CE_Failure;
     160             :     }
     161          74 :     return eErr;
     162             : }
     163             : 
     164             : /************************************************************************/
     165             : /*                        CloseDependentDatasets()                      */
     166             : /************************************************************************/
     167             : 
     168          38 : int PDSDataset::CloseDependentDatasets()
     169             : {
     170          38 :     int bHasDroppedRef = GDALPamDataset::CloseDependentDatasets();
     171             : 
     172          38 :     if (poCompressedDS)
     173             :     {
     174           3 :         bHasDroppedRef = FALSE;
     175           3 :         delete poCompressedDS;
     176           3 :         poCompressedDS = nullptr;
     177             :     }
     178             : 
     179         286 :     for (int iBand = 0; iBand < nBands; iBand++)
     180             :     {
     181         248 :         delete papoBands[iBand];
     182             :     }
     183          38 :     nBands = 0;
     184             : 
     185          38 :     return bHasDroppedRef;
     186             : }
     187             : 
     188             : /************************************************************************/
     189             : /*                            GetFileList()                             */
     190             : /************************************************************************/
     191             : 
     192          13 : char **PDSDataset::GetFileList()
     193             : 
     194             : {
     195          13 :     char **papszFileList = RawDataset::GetFileList();
     196             : 
     197          13 :     if (poCompressedDS != nullptr)
     198             :     {
     199           2 :         char **papszCFileList = poCompressedDS->GetFileList();
     200             : 
     201           2 :         papszFileList = CSLInsertStrings(papszFileList, -1, papszCFileList);
     202           2 :         CSLDestroy(papszCFileList);
     203             :     }
     204             : 
     205          13 :     if (!osExternalCube.empty())
     206             :     {
     207           6 :         papszFileList = CSLAddString(papszFileList, osExternalCube);
     208             :     }
     209             : 
     210          13 :     return papszFileList;
     211             : }
     212             : 
     213             : /************************************************************************/
     214             : /*                          IBuildOverviews()                           */
     215             : /************************************************************************/
     216             : 
     217           0 : CPLErr PDSDataset::IBuildOverviews(const char *pszResampling, int nOverviews,
     218             :                                    const int *panOverviewList, int nListBands,
     219             :                                    const int *panBandList,
     220             :                                    GDALProgressFunc pfnProgress,
     221             :                                    void *pProgressData,
     222             :                                    CSLConstList papszOptions)
     223             : {
     224           0 :     if (poCompressedDS != nullptr)
     225           0 :         return poCompressedDS->BuildOverviews(
     226             :             pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
     227           0 :             pfnProgress, pProgressData, papszOptions);
     228             : 
     229           0 :     return RawDataset::IBuildOverviews(
     230             :         pszResampling, nOverviews, panOverviewList, nListBands, panBandList,
     231           0 :         pfnProgress, pProgressData, papszOptions);
     232             : }
     233             : 
     234             : /************************************************************************/
     235             : /*                             IRasterIO()                              */
     236             : /************************************************************************/
     237             : 
     238           0 : CPLErr PDSDataset::IRasterIO(GDALRWFlag eRWFlag, int nXOff, int nYOff,
     239             :                              int nXSize, int nYSize, void *pData, int nBufXSize,
     240             :                              int nBufYSize, GDALDataType eBufType,
     241             :                              int nBandCount, BANDMAP_TYPE panBandMap,
     242             :                              GSpacing nPixelSpace, GSpacing nLineSpace,
     243             :                              GSpacing nBandSpace,
     244             :                              GDALRasterIOExtraArg *psExtraArg)
     245             : 
     246             : {
     247           0 :     if (poCompressedDS != nullptr)
     248           0 :         return poCompressedDS->RasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize,
     249             :                                         pData, nBufXSize, nBufYSize, eBufType,
     250             :                                         nBandCount, panBandMap, nPixelSpace,
     251           0 :                                         nLineSpace, nBandSpace, psExtraArg);
     252             : 
     253           0 :     return RawDataset::IRasterIO(eRWFlag, nXOff, nYOff, nXSize, nYSize, pData,
     254             :                                  nBufXSize, nBufYSize, eBufType, nBandCount,
     255             :                                  panBandMap, nPixelSpace, nLineSpace,
     256           0 :                                  nBandSpace, psExtraArg);
     257             : }
     258             : 
     259             : /************************************************************************/
     260             : /*                         GetSpatialRef()                              */
     261             : /************************************************************************/
     262             : 
     263          10 : const OGRSpatialReference *PDSDataset::GetSpatialRef() const
     264             : {
     265          10 :     if (!m_oSRS.IsEmpty())
     266          10 :         return &m_oSRS;
     267           0 :     return GDALPamDataset::GetSpatialRef();
     268             : }
     269             : 
     270             : /************************************************************************/
     271             : /*                          GetGeoTransform()                           */
     272             : /************************************************************************/
     273             : 
     274           9 : CPLErr PDSDataset::GetGeoTransform(GDALGeoTransform &gt) const
     275             : 
     276             : {
     277           9 :     if (bGotTransform)
     278             :     {
     279           8 :         gt = m_gt;
     280           8 :         return CE_None;
     281             :     }
     282             : 
     283           1 :     return GDALPamDataset::GetGeoTransform(gt);
     284             : }
     285             : 
     286             : /************************************************************************/
     287             : /*                              ParseSRS()                              */
     288             : /************************************************************************/
     289             : 
     290          36 : void PDSDataset::ParseSRS()
     291             : 
     292             : {
     293          36 :     const char *pszFilename = GetDescription();
     294             : 
     295          72 :     CPLString osPrefix;
     296          86 :     if (strlen(GetKeyword("IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE")) == 0 &&
     297          50 :         strlen(GetKeyword(
     298          14 :             "UNCOMPRESSED_FILE.IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE")) != 0)
     299           3 :         osPrefix = "UNCOMPRESSED_FILE.";
     300             : 
     301             :     /* ==================================================================== */
     302             :     /*      Get the geotransform.                                           */
     303             :     /* ==================================================================== */
     304             :     /***********   Grab Cellsize ************/
     305             :     // example:
     306             :     // MAP_SCALE   = 14.818 <KM/PIXEL>
     307             :     // added search for unit (only checks for CM, KM - defaults to Meters)
     308             :     // Georef parameters
     309          36 :     double dfXDim = 1.0;
     310          36 :     double dfYDim = 1.0;
     311             : 
     312          36 :     const char *value = GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.MAP_SCALE");
     313          36 :     if (strlen(value) > 0)
     314             :     {
     315          25 :         dfXDim = CPLAtof(value);
     316          25 :         dfYDim = CPLAtof(value) * -1;
     317             : 
     318          50 :         CPLString osKey(osPrefix + "IMAGE_MAP_PROJECTION.MAP_SCALE");
     319          50 :         CPLString unit = GetKeywordUnit(osKey, 2);  // KM
     320             :         // value = GetKeywordUnit("IMAGE_MAP_PROJECTION.MAP_SCALE",3); //PIXEL
     321          50 :         if ((EQUAL(unit, "M")) || (EQUAL(unit, "METER")) ||
     322          25 :             (EQUAL(unit, "METERS")))
     323             :         {
     324             :             // do nothing
     325             :         }
     326          18 :         else if (EQUAL(unit, "CM"))
     327             :         {
     328             :             // convert from cm to m
     329           0 :             dfXDim = dfXDim / 100.0;
     330           0 :             dfYDim = dfYDim / 100.0;
     331             :         }
     332             :         else
     333             :         {
     334             :             // defaults to convert km to m
     335          18 :             dfXDim = dfXDim * 1000.0;
     336          18 :             dfYDim = dfYDim * 1000.0;
     337             :         }
     338             :     }
     339             : 
     340             :     /* -------------------------------------------------------------------- */
     341             :     /*      Calculate upper left corner of pixel in meters from the         */
     342             :     /*      upper  left center pixel sample/line offsets.  It doesn't       */
     343             :     /*      mean the defaults will work for every PDS image, as these       */
     344             :     /*      values are used inconsistently.  Thus we have included          */
     345             :     /*      conversion options to allow the user to override the            */
     346             :     /*      documented PDS3 default. Jan. 2011, for known mapping issues    */
     347             :     /*      see GDAL PDS page or mapping within ISIS3 source (USGS)         */
     348             :     /*      $ISIS3DATA/base/translations/pdsProjectionLineSampToXY.def      */
     349             :     /* -------------------------------------------------------------------- */
     350             : 
     351             :     // defaults should be correct for what is documented in the PDS3 standard
     352             : 
     353             :     // https://trac.osgeo.org/gdal/ticket/5941 has the history of the default
     354             :     /* value of PDS_SampleProjOffset_Shift and PDS_LineProjOffset_Shift */
     355             :     // coverity[tainted_data]
     356             :     double dfSampleOffset_Shift =
     357          36 :         CPLAtof(CPLGetConfigOption("PDS_SampleProjOffset_Shift", "0.5"));
     358             : 
     359             :     // coverity[tainted_data]
     360             :     const double dfLineOffset_Shift =
     361          36 :         CPLAtof(CPLGetConfigOption("PDS_LineProjOffset_Shift", "0.5"));
     362             : 
     363             :     // coverity[tainted_data]
     364             :     const double dfSampleOffset_Mult =
     365          36 :         CPLAtof(CPLGetConfigOption("PDS_SampleProjOffset_Mult", "-1.0"));
     366             : 
     367             :     // coverity[tainted_data]
     368             :     const double dfLineOffset_Mult =
     369          36 :         CPLAtof(CPLGetConfigOption("PDS_LineProjOffset_Mult", "1.0"));
     370             : 
     371             :     /***********   Grab LINE_PROJECTION_OFFSET ************/
     372          36 :     double dfULYMap = 0.5;
     373             : 
     374             :     value =
     375          36 :         GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.LINE_PROJECTION_OFFSET");
     376          36 :     if (strlen(value) > 0)
     377             :     {
     378          25 :         const double yulcenter = CPLAtof(value);
     379          25 :         dfULYMap =
     380          25 :             ((yulcenter + dfLineOffset_Shift) * -dfYDim * dfLineOffset_Mult);
     381             :         // notice dfYDim is negative here which is why it is again negated here
     382             :     }
     383             :     /***********   Grab SAMPLE_PROJECTION_OFFSET ************/
     384          36 :     double dfULXMap = 0.5;
     385             : 
     386             :     value =
     387          36 :         GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.SAMPLE_PROJECTION_OFFSET");
     388          36 :     if (strlen(value) > 0)
     389             :     {
     390          25 :         const double xulcenter = CPLAtof(value);
     391          25 :         dfULXMap =
     392          25 :             ((xulcenter + dfSampleOffset_Shift) * dfXDim * dfSampleOffset_Mult);
     393             :     }
     394             : 
     395             :     /* ==================================================================== */
     396             :     /*      Get the coordinate system.                                      */
     397             :     /* ==================================================================== */
     398             : 
     399             :     /***********  Grab TARGET_NAME  ************/
     400             :     /**** This is the planets name i.e. MARS ***/
     401         108 :     const CPLString target_name = CleanString(GetKeyword("TARGET_NAME"));
     402             : 
     403             :     /**********   Grab MAP_PROJECTION_TYPE *****/
     404             :     const CPLString map_proj_name = CleanString(
     405         108 :         GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.MAP_PROJECTION_TYPE"));
     406             : 
     407             :     /******  Grab semi_major & convert to KM ******/
     408             :     const double semi_major =
     409          36 :         CPLAtof(GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.A_AXIS_RADIUS")) *
     410          36 :         1000.0;
     411             : 
     412             :     /******  Grab semi-minor & convert to KM ******/
     413             :     const double semi_minor =
     414          36 :         CPLAtof(GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.C_AXIS_RADIUS")) *
     415          36 :         1000.0;
     416             : 
     417             :     /***********   Grab CENTER_LAT ************/
     418             :     const double center_lat =
     419          36 :         CPLAtof(GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.CENTER_LATITUDE"));
     420             : 
     421             :     /***********   Grab CENTER_LON ************/
     422             :     const double center_lon =
     423          36 :         CPLAtof(GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.CENTER_LONGITUDE"));
     424             : 
     425             :     /**********   Grab 1st std parallel *******/
     426          36 :     const double first_std_parallel = CPLAtof(
     427          72 :         GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.FIRST_STANDARD_PARALLEL"));
     428             : 
     429             :     /**********   Grab 2nd std parallel *******/
     430          36 :     const double second_std_parallel = CPLAtof(
     431          72 :         GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.SECOND_STANDARD_PARALLEL"));
     432             : 
     433             :     /*** grab  PROJECTION_LATITUDE_TYPE = "PLANETOCENTRIC" ****/
     434             :     // Need to further study how ocentric/ographic will effect the gdal library.
     435             :     // So far we will use this fact to define a sphere or ellipse for some
     436             :     // projections Frank - may need to talk this over
     437          36 :     char bIsGeographic = TRUE;
     438             :     value =
     439          36 :         GetKeyword(osPrefix + "IMAGE_MAP_PROJECTION.COORDINATE_SYSTEM_NAME");
     440          36 :     if (EQUAL(value, "PLANETOCENTRIC"))
     441           8 :         bIsGeographic = FALSE;
     442             : 
     443             :     const double dfLongitudeMulFactor =
     444          72 :         EQUAL(GetKeyword("IMAGE_MAP_PROJECTION.POSITIVE_LONGITUDE_DIRECTION",
     445             :                          "EAST"),
     446             :               "EAST")
     447             :             ? 1
     448          36 :             : -1;
     449             : 
     450             :     /**   Set oSRS projection and parameters --- all PDS supported types added
     451             :     if apparently supported in oSRS "AITOFF",  ** Not supported in GDAL??
     452             :           "ALBERS",
     453             :           "BONNE",
     454             :           "BRIESEMEISTER",   ** Not supported in GDAL??
     455             :           "CYLINDRICAL EQUAL AREA",
     456             :           "EQUIDISTANT",
     457             :           "EQUIRECTANGULAR",
     458             :           "GNOMONIC",
     459             :           "HAMMER",    ** Not supported in GDAL??
     460             :           "HENDU",     ** Not supported in GDAL??
     461             :           "LAMBERT AZIMUTHAL EQUAL AREA",
     462             :           "LAMBERT CONFORMAL",
     463             :           "MERCATOR",
     464             :           "MOLLWEIDE",
     465             :           "OBLIQUE CYLINDRICAL",
     466             :           "ORTHOGRAPHIC",
     467             :           "SIMPLE CYLINDRICAL",
     468             :           "SINUSOIDAL",
     469             :           "STEREOGRAPHIC",
     470             :           "TRANSVERSE MERCATOR",
     471             :           "VAN DER GRINTEN",     ** Not supported in GDAL??
     472             :           "WERNER"     ** Not supported in GDAL??
     473             :     **/
     474          36 :     CPLDebug("PDS", "using projection %s\n\n", map_proj_name.c_str());
     475             : 
     476          36 :     bool bProjectionSet = true;
     477          72 :     OGRSpatialReference oSRS;
     478          36 :     oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     479             : 
     480          36 :     if ((EQUAL(map_proj_name, "EQUIRECTANGULAR")) ||
     481          56 :         (EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
     482          20 :         (EQUAL(map_proj_name, "EQUIDISTANT")))
     483             :     {
     484          16 :         oSRS.SetEquirectangular2(0.0, center_lon, center_lat, 0, 0);
     485             :     }
     486          20 :     else if (EQUAL(map_proj_name, "ORTHOGRAPHIC"))
     487             :     {
     488           0 :         oSRS.SetOrthographic(center_lat, center_lon, 0, 0);
     489             :     }
     490          20 :     else if (EQUAL(map_proj_name, "SINUSOIDAL"))
     491             :     {
     492           4 :         oSRS.SetSinusoidal(center_lon, 0, 0);
     493             :     }
     494          16 :     else if (EQUAL(map_proj_name, "MERCATOR"))
     495             :     {
     496           1 :         if (center_lat == 0.0 && first_std_parallel != 0.0)
     497             :         {
     498           1 :             oSRS.SetMercator2SP(first_std_parallel, center_lat, center_lon, 0,
     499             :                                 0);
     500             :         }
     501             :         else
     502             :         {
     503           0 :             oSRS.SetMercator(center_lat, center_lon, 1, 0, 0);
     504             :         }
     505             :     }
     506          15 :     else if (EQUAL(map_proj_name, "STEREOGRAPHIC"))
     507             :     {
     508           0 :         if ((fabs(center_lat) - 90) < 0.0000001)
     509             :         {
     510           0 :             oSRS.SetPS(center_lat, center_lon, 1, 0, 0);
     511             :         }
     512             :         else
     513           0 :             oSRS.SetStereographic(center_lat, center_lon, 1, 0, 0);
     514             :     }
     515          15 :     else if (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC"))
     516             :     {
     517           0 :         oSRS.SetPS(center_lat, center_lon, 1, 0, 0);
     518             :     }
     519          15 :     else if (EQUAL(map_proj_name, "TRANSVERSE_MERCATOR"))
     520             :     {
     521           0 :         oSRS.SetTM(center_lat, center_lon, 1, 0, 0);
     522             :     }
     523          15 :     else if (EQUAL(map_proj_name, "LAMBERT_CONFORMAL_CONIC"))
     524             :     {
     525           0 :         oSRS.SetLCC(first_std_parallel, second_std_parallel, center_lat,
     526             :                     center_lon, 0, 0);
     527             :     }
     528          15 :     else if (EQUAL(map_proj_name, "LAMBERT_AZIMUTHAL_EQUAL_AREA"))
     529             :     {
     530           0 :         oSRS.SetLAEA(center_lat, center_lon, 0, 0);
     531             :     }
     532          15 :     else if (EQUAL(map_proj_name, "CYLINDRICAL_EQUAL_AREA"))
     533             :     {
     534           0 :         oSRS.SetCEA(first_std_parallel, center_lon, 0, 0);
     535             :     }
     536          15 :     else if (EQUAL(map_proj_name, "MOLLWEIDE"))
     537             :     {
     538           0 :         oSRS.SetMollweide(center_lon, 0, 0);
     539             :     }
     540          15 :     else if (EQUAL(map_proj_name, "ALBERS"))
     541             :     {
     542           0 :         oSRS.SetACEA(first_std_parallel, second_std_parallel, center_lat,
     543             :                      center_lon, 0, 0);
     544             :     }
     545          15 :     else if (EQUAL(map_proj_name, "BONNE"))
     546             :     {
     547           0 :         oSRS.SetBonne(first_std_parallel, center_lon, 0, 0);
     548             :     }
     549          15 :     else if (EQUAL(map_proj_name, "GNOMONIC"))
     550             :     {
     551           0 :         oSRS.SetGnomonic(center_lat, center_lon, 0, 0);
     552             :     }
     553          15 :     else if (EQUAL(map_proj_name, "OBLIQUE_CYLINDRICAL"))
     554             :     {
     555           4 :         const double poleLatitude = CPLAtof(GetKeyword(
     556           8 :             osPrefix + "IMAGE_MAP_PROJECTION.OBLIQUE_PROJ_POLE_LATITUDE"));
     557             :         const double poleLongitude =
     558           4 :             CPLAtof(GetKeyword(
     559           4 :                 osPrefix +
     560             :                 "IMAGE_MAP_PROJECTION.OBLIQUE_PROJ_POLE_LONGITUDE")) *
     561           4 :             dfLongitudeMulFactor;
     562           4 :         const double poleRotation = CPLAtof(GetKeyword(
     563           8 :             osPrefix + "IMAGE_MAP_PROJECTION.OBLIQUE_PROJ_POLE_ROTATION"));
     564           8 :         CPLString oProj4String;
     565             :         // ISIS3 rotated pole doesn't use the same conventions than PROJ ob_tran
     566             :         // Compare the sign difference in
     567             :         // https://github.com/USGS-Astrogeology/ISIS3/blob/3.8.0/isis/src/base/objs/ObliqueCylindrical/ObliqueCylindrical.cpp#L244
     568             :         // and
     569             :         // https://github.com/OSGeo/PROJ/blob/6.2/src/projections/ob_tran.cpp#L34
     570             :         // They can be compensated by modifying the poleLatitude to
     571             :         // 180-poleLatitude There's also a sign difference for the poleRotation
     572             :         // parameter The existence of those different conventions is
     573             :         // acknowledged in
     574             :         // https://pds-imaging.jpl.nasa.gov/documentation/Cassini_BIDRSIS.PDF in
     575             :         // the middle of page 10
     576             :         oProj4String.Printf("+proj=ob_tran +o_proj=eqc +o_lon_p=%.17g "
     577             :                             "+o_lat_p=%.17g +lon_0=%.17g",
     578           4 :                             -poleRotation, 180 - poleLatitude, poleLongitude);
     579           4 :         oSRS.SetFromUserInput(oProj4String);
     580             :     }
     581             :     else
     582             :     {
     583          11 :         CPLDebug("PDS", "Dataset projection %s is not supported. Continuing...",
     584             :                  map_proj_name.c_str());
     585          11 :         bProjectionSet = false;
     586             :     }
     587             : 
     588          36 :     if (bProjectionSet)
     589             :     {
     590             :         // Create projection name, i.e. MERCATOR MARS and set as ProjCS keyword
     591          75 :         CPLString proj_target_name = map_proj_name + " " + target_name;
     592          25 :         oSRS.SetProjCS(proj_target_name);  // set ProjCS keyword
     593             : 
     594             :         // The geographic/geocentric name will be the same basic name as the
     595             :         // body name 'GCS' = Geographic/Geocentric Coordinate System
     596          50 :         const CPLString geog_name = "GCS_" + target_name;
     597             : 
     598             :         // The datum and sphere names will be the same basic name aas the planet
     599          50 :         const CPLString datum_name = "D_" + target_name;
     600             : 
     601          50 :         CPLString sphere_name = std::move(target_name);
     602             : 
     603             :         // calculate inverse flattening from major and minor axis: 1/f = a/(a-b)
     604             :         double iflattening;
     605          25 :         if ((semi_major - semi_minor) < 0.0000001)
     606          18 :             iflattening = 0;
     607             :         else
     608           7 :             iflattening = semi_major / (semi_major - semi_minor);
     609             : 
     610             :         // Set the body size but take into consideration which proj is being
     611             :         // used to help w/ compatibility Notice that most PDS projections are
     612             :         // spherical based on the fact that ISIS/PICS are spherical Set the body
     613             :         // size but take into consideration which proj is being used to help w/
     614             :         // proj4 compatibility The use of a Sphere, polar radius or ellipse here
     615             :         // is based on how ISIS does it internally
     616          25 :         if (((EQUAL(map_proj_name, "STEREOGRAPHIC") &&
     617          50 :               (fabs(center_lat) == 90))) ||
     618          25 :             (EQUAL(map_proj_name, "POLAR_STEREOGRAPHIC")))
     619             :         {
     620           0 :             if (bIsGeographic)
     621             :             {
     622             :                 // Geograpraphic, so set an ellipse
     623           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     624             :                                iflattening, "Reference_Meridian", 0.0);
     625             :             }
     626             :             else
     627             :             {
     628             :                 // Geocentric, so force a sphere using the semi-minor axis. I
     629             :                 // hope...
     630           0 :                 sphere_name += "_polarRadius";
     631           0 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_minor,
     632             :                                0.0, "Reference_Meridian", 0.0);
     633             :             }
     634             :         }
     635          25 :         else if ((EQUAL(map_proj_name, "SIMPLE_CYLINDRICAL")) ||
     636          16 :                  (EQUAL(map_proj_name, "EQUIDISTANT")) ||
     637          16 :                  (EQUAL(map_proj_name, "ORTHOGRAPHIC")) ||
     638          57 :                  (EQUAL(map_proj_name, "STEREOGRAPHIC")) ||
     639          16 :                  (EQUAL(map_proj_name, "SINUSOIDAL")))
     640             :         {
     641             :             // isis uses the spherical equation for these projections so force a
     642             :             // sphere
     643          13 :             oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
     644             :                            "Reference_Meridian", 0.0);
     645             :         }
     646          12 :         else if (EQUAL(map_proj_name, "EQUIRECTANGULAR"))
     647             :         {
     648             :             // isis uses local radius as a sphere, which is pre-calculated in
     649             :             // the PDS label as the semi-major
     650           7 :             sphere_name += "_localRadius";
     651           7 :             oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major, 0.0,
     652             :                            "Reference_Meridian", 0.0);
     653             :         }
     654             :         else
     655             :         {
     656             :             // All other projections: Mercator, Transverse Mercator, Lambert
     657             :             // Conformal, etc. Geographic, so set an ellipse
     658           5 :             if (bIsGeographic)
     659             :             {
     660           4 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     661             :                                iflattening, "Reference_Meridian", 0.0);
     662             :             }
     663             :             else
     664             :             {
     665             :                 // Geocentric, so force a sphere. I hope...
     666           1 :                 oSRS.SetGeogCS(geog_name, datum_name, sphere_name, semi_major,
     667             :                                0.0, "Reference_Meridian", 0.0);
     668             :             }
     669             :         }
     670             : 
     671             :         // translate back into a projection string.
     672          25 :         m_oSRS = std::move(oSRS);
     673             :     }
     674             : 
     675             :     /* ==================================================================== */
     676             :     /*      Check for a .prj and world file to override the georeferencing. */
     677             :     /* ==================================================================== */
     678             :     {
     679          72 :         const CPLString osPath = CPLGetPathSafe(pszFilename);
     680          72 :         const CPLString osName = CPLGetBasenameSafe(pszFilename);
     681             :         const std::string osPrjFile =
     682          72 :             CPLFormCIFilenameSafe(osPath, osName, "prj");
     683             : 
     684          36 :         VSILFILE *fp = VSIFOpenL(osPrjFile.c_str(), "r");
     685          36 :         if (fp != nullptr)
     686             :         {
     687           0 :             VSIFCloseL(fp);
     688             : 
     689           0 :             char **papszLines = CSLLoad(osPrjFile.c_str());
     690             : 
     691           0 :             m_oSRS.importFromESRI(papszLines);
     692           0 :             CSLDestroy(papszLines);
     693             :         }
     694             :     }
     695             : 
     696          36 :     if (dfULXMap != 0.5 || dfULYMap != 0.5 || dfXDim != 1.0 || dfYDim != 1.0)
     697             :     {
     698          25 :         bGotTransform = TRUE;
     699          25 :         m_gt[0] = dfULXMap;
     700          25 :         m_gt[1] = dfXDim;
     701          25 :         m_gt[2] = 0.0;
     702          25 :         m_gt[3] = dfULYMap;
     703          25 :         m_gt[4] = 0.0;
     704          25 :         m_gt[5] = dfYDim;
     705             : 
     706          25 :         const double rotation = CPLAtof(GetKeyword(
     707          50 :             osPrefix + "IMAGE_MAP_PROJECTION.MAP_PROJECTION_ROTATION"));
     708          25 :         if (rotation != 0)
     709             :         {
     710           4 :             const double sin_rot =
     711           4 :                 rotation == 90 ? 1.0 : sin(rotation / 180 * M_PI);
     712           4 :             const double cos_rot =
     713           4 :                 rotation == 90 ? 0.0 : cos(rotation / 180 * M_PI);
     714           4 :             const double gt_1 = cos_rot * m_gt[1] - sin_rot * m_gt[4];
     715           4 :             const double gt_2 = cos_rot * m_gt[2] - sin_rot * m_gt[5];
     716           4 :             const double gt_0 = cos_rot * m_gt[0] - sin_rot * m_gt[3];
     717           4 :             const double gt_4 = sin_rot * m_gt[1] + cos_rot * m_gt[4];
     718           4 :             const double gt_5 = sin_rot * m_gt[2] + cos_rot * m_gt[5];
     719           4 :             const double gt_3 = sin_rot * m_gt[0] + cos_rot * m_gt[3];
     720           4 :             m_gt[1] = gt_1;
     721           4 :             m_gt[2] = gt_2;
     722           4 :             m_gt[0] = gt_0;
     723           4 :             m_gt[4] = gt_4;
     724           4 :             m_gt[5] = gt_5;
     725           4 :             m_gt[3] = gt_3;
     726             :         }
     727             :     }
     728             : 
     729          36 :     if (!bGotTransform)
     730          11 :         bGotTransform = GDALReadWorldFile(pszFilename, "psw", m_gt.data());
     731             : 
     732          36 :     if (!bGotTransform)
     733          11 :         bGotTransform = GDALReadWorldFile(pszFilename, "wld", m_gt.data());
     734          36 : }
     735             : 
     736             : /************************************************************************/
     737             : /*                        GetRawBinaryLayout()                          */
     738             : /************************************************************************/
     739             : 
     740           2 : bool PDSDataset::GetRawBinaryLayout(GDALDataset::RawBinaryLayout &sLayout)
     741             : {
     742           2 :     if (!RawDataset::GetRawBinaryLayout(sLayout))
     743           0 :         return false;
     744           2 :     sLayout.osRawFilename = m_osImageFilename;
     745           2 :     return true;
     746             : }
     747             : 
     748             : /************************************************************************/
     749             : /*                        PDSConvertFromHex()                           */
     750             : /************************************************************************/
     751             : 
     752           3 : static GUInt32 PDSConvertFromHex(const char *pszVal)
     753             : {
     754           3 :     if (!STARTS_WITH_CI(pszVal, "16#"))
     755           0 :         return 0;
     756             : 
     757           3 :     pszVal += 3;
     758           3 :     GUInt32 nVal = 0;
     759          27 :     while (*pszVal != '#' && *pszVal != '\0')
     760             :     {
     761          24 :         nVal <<= 4;
     762          24 :         if (*pszVal >= '0' && *pszVal <= '9')
     763           3 :             nVal += *pszVal - '0';
     764          21 :         else if (*pszVal >= 'A' && *pszVal <= 'F')
     765          21 :             nVal += *pszVal - 'A' + 10;
     766             :         else
     767           0 :             return 0;
     768          24 :         pszVal++;
     769             :     }
     770             : 
     771           3 :     return nVal;
     772             : }
     773             : 
     774             : /************************************************************************/
     775             : /*                             ParseImage()                             */
     776             : /************************************************************************/
     777             : 
     778          33 : int PDSDataset::ParseImage(const CPLString &osPrefix,
     779             :                            const CPLString &osFilenamePrefix)
     780             : {
     781             :     /* ------------------------------------------------------------------- */
     782             :     /*      We assume the user is pointing to the label (i.e. .lbl) file.  */
     783             :     /* ------------------------------------------------------------------- */
     784             :     // IMAGE can be inline or detached and point to an image name
     785             :     // ^IMAGE = 3
     786             :     // ^IMAGE             = "GLOBAL_ALBEDO_8PPD.IMG"
     787             :     // ^IMAGE             = "MEGT90N000CB.IMG"
     788             :     // ^IMAGE             = ("FOO.IMG",1)       -- start at record 1 (1 based)
     789             :     // ^IMAGE             = ("FOO.IMG")         -- start at record 1 equiv of
     790             :     // ("FOO.IMG",1) ^IMAGE             = ("FOO.IMG", 5 <BYTES>) -- start at
     791             :     // byte 5 (the fifth byte in the file) ^IMAGE             = 10851 <BYTES>
     792             :     // ^SPECTRAL_QUBE = 5  for multi-band images
     793             :     // ^QUBE = 5  for multi-band images
     794             : 
     795          66 :     CPLString osImageKeyword = "IMAGE";
     796          99 :     CPLString osQube = GetKeyword(osPrefix + "^" + osImageKeyword, "");
     797          33 :     m_osImageFilename = GetDescription();
     798             : 
     799          33 :     if (EQUAL(osQube, ""))
     800             :     {
     801           0 :         osImageKeyword = "SPECTRAL_QUBE";
     802           0 :         osQube = GetKeyword(osPrefix + "^" + osImageKeyword);
     803             :     }
     804             : 
     805          33 :     if (EQUAL(osQube, ""))
     806             :     {
     807           0 :         osImageKeyword = "QUBE";
     808           0 :         osQube = GetKeyword(osPrefix + "^" + osImageKeyword);
     809             :     }
     810             : 
     811          33 :     const int nQube = atoi(osQube);
     812          33 :     int nDetachedOffset = 0;
     813          33 :     bool bDetachedOffsetInBytes = false;
     814             : 
     815          33 :     if (!osQube.empty() && osQube[0] == '(')
     816             :     {
     817           9 :         osQube = "\"";
     818           9 :         osQube += GetKeywordSub(osPrefix + "^" + osImageKeyword, 1);
     819           9 :         osQube += "\"";
     820             :         nDetachedOffset =
     821           9 :             atoi(GetKeywordSub(osPrefix + "^" + osImageKeyword, 2, "1"));
     822           9 :         if (nDetachedOffset >= 1)
     823           9 :             nDetachedOffset -= 1;
     824             : 
     825             :         // If this is not explicitly in bytes, then it is assumed to be in
     826             :         // records, and we need to translate to bytes.
     827          18 :         if (strstr(GetKeywordSub(osPrefix + "^" + osImageKeyword, 2),
     828           9 :                    "<BYTES>") != nullptr)
     829           2 :             bDetachedOffsetInBytes = true;
     830             :     }
     831             : 
     832          33 :     if (!osQube.empty() && osQube[0] == '"')
     833             :     {
     834          26 :         const CPLString osFilename = CleanString(osQube);
     835          13 :         if (!osFilenamePrefix.empty())
     836             :         {
     837           3 :             m_osImageFilename = osFilenamePrefix + osFilename;
     838             :         }
     839             :         else
     840             :         {
     841          20 :             CPLString osTPath = CPLGetPathSafe(GetDescription());
     842             :             m_osImageFilename =
     843          10 :                 CPLFormCIFilenameSafe(osTPath, osFilename, nullptr);
     844          10 :             osExternalCube = m_osImageFilename;
     845             :         }
     846             :     }
     847             : 
     848             :     /* -------------------------------------------------------------------- */
     849             :     /*      Checks to see if this is raw PDS image not compressed image     */
     850             :     /*      so ENCODING_TYPE either does not exist or it equals "N/A".      */
     851             :     /*      or "DCT_DECOMPRESSED".                                          */
     852             :     /*      Compressed types will not be supported in this routine          */
     853             :     /* -------------------------------------------------------------------- */
     854             : 
     855             :     const CPLString osEncodingType =
     856          99 :         CleanString(GetKeyword(osPrefix + "IMAGE.ENCODING_TYPE", "N/A"));
     857          33 :     if (!EQUAL(osEncodingType, "N/A") &&
     858           0 :         !EQUAL(osEncodingType, "DCT_DECOMPRESSED"))
     859             :     {
     860           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     861             :                  "*** PDS image file has an ENCODING_TYPE parameter:\n"
     862             :                  "*** GDAL PDS driver does not support compressed image types\n"
     863             :                  "found: (%s)\n\n",
     864             :                  osEncodingType.c_str());
     865           0 :         return FALSE;
     866             :     }
     867             :     /**************** end ENCODING_TYPE check ***********************/
     868             : 
     869             :     /***********   Grab layout type (BSQ, BIP, BIL) ************/
     870             :     //  AXIS_NAME = (SAMPLE,LINE,BAND)
     871             :     /***********   Grab samples lines band        **************/
     872             :     /** if AXIS_NAME = "" then Bands=1 and Sample and Lines   **/
     873             :     /** are there own keywords  "LINES" and "LINE_SAMPLES"    **/
     874             :     /** if not NULL then CORE_ITEMS keyword i.e. (234,322,2)  **/
     875             :     /***********************************************************/
     876          33 :     int eLayout = PDS_BSQ;  // default to band seq.
     877          33 :     int nRows, nCols, l_nBands = 1;
     878             : 
     879          99 :     CPLString value = GetKeyword(osPrefix + osImageKeyword + ".AXIS_NAME", "");
     880          33 :     if (EQUAL(value, "(SAMPLE,LINE,BAND)"))
     881             :     {
     882           0 :         eLayout = PDS_BSQ;
     883             :         nCols =
     884           0 :             atoi(GetKeywordSub(osPrefix + osImageKeyword + ".CORE_ITEMS", 1));
     885             :         nRows =
     886           0 :             atoi(GetKeywordSub(osPrefix + osImageKeyword + ".CORE_ITEMS", 2));
     887             :         l_nBands =
     888           0 :             atoi(GetKeywordSub(osPrefix + osImageKeyword + ".CORE_ITEMS", 3));
     889             :     }
     890          33 :     else if (EQUAL(value, "(BAND,LINE,SAMPLE)"))
     891             :     {
     892           0 :         eLayout = PDS_BIP;
     893             :         l_nBands =
     894           0 :             atoi(GetKeywordSub(osPrefix + osImageKeyword + ".CORE_ITEMS", 1));
     895             :         nRows =
     896           0 :             atoi(GetKeywordSub(osPrefix + osImageKeyword + ".CORE_ITEMS", 2));
     897             :         nCols =
     898           0 :             atoi(GetKeywordSub(osPrefix + osImageKeyword + ".CORE_ITEMS", 3));
     899             :     }
     900          33 :     else if (EQUAL(value, "(SAMPLE,BAND,LINE)"))
     901             :     {
     902           0 :         eLayout = PDS_BIL;
     903             :         nCols =
     904           0 :             atoi(GetKeywordSub(osPrefix + osImageKeyword + ".CORE_ITEMS", 1));
     905             :         l_nBands =
     906           0 :             atoi(GetKeywordSub(osPrefix + osImageKeyword + ".CORE_ITEMS", 2));
     907             :         nRows =
     908           0 :             atoi(GetKeywordSub(osPrefix + osImageKeyword + ".CORE_ITEMS", 3));
     909             :     }
     910          33 :     else if (EQUAL(value, ""))
     911             :     {
     912          33 :         eLayout = PDS_BSQ;
     913             :         nCols =
     914          33 :             atoi(GetKeyword(osPrefix + osImageKeyword + ".LINE_SAMPLES", ""));
     915          33 :         nRows = atoi(GetKeyword(osPrefix + osImageKeyword + ".LINES", ""));
     916          33 :         l_nBands = atoi(GetKeyword(osPrefix + osImageKeyword + ".BANDS", "1"));
     917             :     }
     918             :     else
     919             :     {
     920           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
     921             :                  "%s layout not supported. Abort\n\n", value.c_str());
     922           0 :         return FALSE;
     923             :     }
     924             : 
     925             :     CPLString osBAND_STORAGE_TYPE =
     926          66 :         GetKeyword(osPrefix + "IMAGE.BAND_STORAGE_TYPE", "");
     927          33 :     if (EQUAL(osBAND_STORAGE_TYPE, "BAND_SEQUENTIAL"))
     928             :     {
     929          12 :         eLayout = PDS_BSQ;
     930             :     }
     931          21 :     else if (EQUAL(osBAND_STORAGE_TYPE, "PIXEL_INTERLEAVED"))
     932             :     {
     933           0 :         eLayout = PDS_BIP;
     934             :     }
     935          21 :     else if (EQUAL(osBAND_STORAGE_TYPE, "LINE_INTERLEAVED"))
     936             :     {
     937           2 :         eLayout = PDS_BIL;
     938             :     }
     939          19 :     else if (!osBAND_STORAGE_TYPE.empty())
     940             :     {
     941           4 :         CPLDebug("PDS", "Unhandled BAND_STORAGE_TYPE = %s",
     942             :                  osBAND_STORAGE_TYPE.c_str());
     943             :     }
     944             : 
     945             :     /***********   Grab Qube record bytes  **********/
     946          33 :     int record_bytes = atoi(GetKeyword(osPrefix + "IMAGE.RECORD_BYTES"));
     947          33 :     if (record_bytes == 0)
     948          33 :         record_bytes = atoi(GetKeyword(osPrefix + "RECORD_BYTES"));
     949             : 
     950             :     // this can happen with "record_type = undefined".
     951          33 :     if (record_bytes < 0)
     952           0 :         return FALSE;
     953          33 :     if (record_bytes == 0)
     954           3 :         record_bytes = 1;
     955             : 
     956          33 :     int nSkipBytes = 0;
     957             :     try
     958             :     {
     959          33 :         if (nQube > 0)
     960             :         {
     961          20 :             if (osQube.find("<BYTES>") != CPLString::npos)
     962           3 :                 nSkipBytes = (CPLSM(nQube) - CPLSM(1)).v();
     963             :             else
     964          17 :                 nSkipBytes = (CPLSM(nQube - 1) * CPLSM(record_bytes)).v();
     965             :         }
     966          13 :         else if (nDetachedOffset > 0)
     967             :         {
     968           4 :             if (bDetachedOffsetInBytes)
     969           2 :                 nSkipBytes = nDetachedOffset;
     970             :             else
     971             :             {
     972           2 :                 nSkipBytes = (CPLSM(nDetachedOffset) * CPLSM(record_bytes)).v();
     973             :             }
     974             :         }
     975             :         else
     976           9 :             nSkipBytes = 0;
     977             :     }
     978           0 :     catch (const CPLSafeIntOverflow &)
     979             :     {
     980           0 :         return FALSE;
     981             :     }
     982             : 
     983             :     const int nLinePrefixBytes =
     984          33 :         atoi(GetKeyword(osPrefix + "IMAGE.LINE_PREFIX_BYTES", ""));
     985          33 :     if (nLinePrefixBytes < 0)
     986           0 :         return false;
     987          33 :     nSkipBytes += nLinePrefixBytes;
     988             : 
     989             :     /***********   Grab SAMPLE_TYPE *****************/
     990             :     /** if keyword not found leave as "M" or "MSB" **/
     991             : 
     992          66 :     CPLString osST = GetKeyword(osPrefix + "IMAGE.SAMPLE_TYPE");
     993          33 :     if (osST.size() >= 2 && osST[0] == '"' && osST.back() == '"')
     994           4 :         osST = osST.substr(1, osST.size() - 2);
     995             : 
     996          33 :     char chByteOrder = 'M';  // default to MSB
     997          64 :     if ((EQUAL(osST, "LSB_INTEGER")) || (EQUAL(osST, "LSB")) ||  // just in case
     998          31 :         (EQUAL(osST, "LSB_UNSIGNED_INTEGER")) ||
     999          27 :         (EQUAL(osST, "LSB_SIGNED_INTEGER")) ||
    1000          27 :         (EQUAL(osST, "UNSIGNED_INTEGER")) || (EQUAL(osST, "VAX_REAL")) ||
    1001          12 :         (EQUAL(osST, "VAX_INTEGER")) ||
    1002          76 :         (EQUAL(osST, "PC_INTEGER")) ||  // just in case
    1003          12 :         (EQUAL(osST, "PC_REAL")))
    1004             :     {
    1005          26 :         chByteOrder = 'I';
    1006             :     }
    1007             : 
    1008             :     /**** Grab format type - pds supports 1,2,4,8,16,32,64 (in theory) **/
    1009             :     /**** I have only seen 8, 16, 32 (float) in released datasets      **/
    1010          33 :     GDALDataType eDataType = GDT_Byte;
    1011          33 :     int nSuffixItems = 0;
    1012          33 :     int nSuffixLines = 0;
    1013          33 :     int nSuffixBytes = 4;  // Default as per PDS specification
    1014          33 :     double dfNoData = 0.0;
    1015          33 :     double dfScale = 1.0;
    1016          33 :     double dfOffset = 0.0;
    1017          33 :     const char *pszUnit = nullptr;
    1018          33 :     const char *pszDesc = nullptr;
    1019             : 
    1020          66 :     CPLString osSB = GetKeyword(osPrefix + "IMAGE.SAMPLE_BITS", "");
    1021          33 :     if (!osSB.empty())
    1022             :     {
    1023          33 :         const int itype = atoi(osSB);
    1024          33 :         switch (itype)
    1025             :         {
    1026          23 :             case 8:
    1027          23 :                 eDataType = GDT_Byte;
    1028          23 :                 dfNoData = PDS_NULL1;
    1029          23 :                 break;
    1030           5 :             case 16:
    1031           5 :                 if (strstr(osST, "UNSIGNED") != nullptr)
    1032             :                 {
    1033           3 :                     dfNoData = PDS_NULL1;
    1034           3 :                     eDataType = GDT_UInt16;
    1035             :                 }
    1036             :                 else
    1037             :                 {
    1038           2 :                     eDataType = GDT_Int16;
    1039           2 :                     dfNoData = PDS_NULL2;
    1040             :                 }
    1041           5 :                 break;
    1042           5 :             case 32:
    1043           5 :                 eDataType = GDT_Float32;
    1044           5 :                 dfNoData = PDS_NULL3;
    1045           5 :                 break;
    1046           0 :             case 64:
    1047           0 :                 eDataType = GDT_Float64;
    1048           0 :                 dfNoData = PDS_NULL3;
    1049           0 :                 break;
    1050           0 :             default:
    1051           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1052             :                          "Sample_bits of %d is not supported in this gdal PDS "
    1053             :                          "reader.",
    1054             :                          itype);
    1055           0 :                 return FALSE;
    1056             :         }
    1057             : 
    1058          33 :         dfOffset = CPLAtofM(GetKeyword(osPrefix + "IMAGE.OFFSET", "0.0"));
    1059             :         dfScale =
    1060          33 :             CPLAtofM(GetKeyword(osPrefix + "IMAGE.SCALING_FACTOR", "1.0"));
    1061             :     }
    1062             :     else /* No IMAGE object, search for the QUBE. */
    1063             :     {
    1064           0 :         osSB = GetKeyword(osPrefix + "SPECTRAL_QUBE.CORE_ITEM_BYTES", "");
    1065           0 :         const int itype = atoi(osSB);
    1066           0 :         switch (itype)
    1067             :         {
    1068           0 :             case 1:
    1069           0 :                 eDataType = GDT_Byte;
    1070           0 :                 break;
    1071           0 :             case 2:
    1072           0 :                 if (strstr(osST, "UNSIGNED") != nullptr)
    1073           0 :                     eDataType = GDT_UInt16;
    1074             :                 else
    1075           0 :                     eDataType = GDT_Int16;
    1076           0 :                 break;
    1077           0 :             case 4:
    1078           0 :                 eDataType = GDT_Float32;
    1079           0 :                 break;
    1080           0 :             default:
    1081           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    1082             :                          "CORE_ITEM_BYTES of %d is not supported in this gdal "
    1083             :                          "PDS reader.",
    1084             :                          itype);
    1085           0 :                 return FALSE;
    1086             :         }
    1087             : 
    1088             :         /* Parse suffix dimensions if defined. */
    1089           0 :         value = GetKeyword(osPrefix + "SPECTRAL_QUBE.SUFFIX_ITEMS", "");
    1090           0 :         if (!value.empty())
    1091             :         {
    1092           0 :             value = GetKeyword(osPrefix + "SPECTRAL_QUBE.SUFFIX_BYTES", "");
    1093           0 :             if (!value.empty())
    1094           0 :                 nSuffixBytes = atoi(value);
    1095             : 
    1096             :             nSuffixItems =
    1097           0 :                 atoi(GetKeywordSub(osPrefix + "SPECTRAL_QUBE.SUFFIX_ITEMS", 1));
    1098             :             nSuffixLines =
    1099           0 :                 atoi(GetKeywordSub(osPrefix + "SPECTRAL_QUBE.SUFFIX_ITEMS", 2));
    1100             :         }
    1101             : 
    1102           0 :         value = GetKeyword(osPrefix + "SPECTRAL_QUBE.CORE_NULL", "");
    1103           0 :         if (!value.empty())
    1104           0 :             dfNoData = CPLAtofM(value);
    1105             : 
    1106             :         dfOffset =
    1107           0 :             CPLAtofM(GetKeyword(osPrefix + "SPECTRAL_QUBE.CORE_BASE", "0.0"));
    1108           0 :         dfScale = CPLAtofM(
    1109           0 :             GetKeyword(osPrefix + "SPECTRAL_QUBE.CORE_MULTIPLIER", "1.0"));
    1110           0 :         pszUnit = GetKeyword(osPrefix + "SPECTRAL_QUBE.CORE_UNIT", nullptr);
    1111           0 :         pszDesc = GetKeyword(osPrefix + "SPECTRAL_QUBE.CORE_NAME", nullptr);
    1112             :     }
    1113             : 
    1114             :     /* -------------------------------------------------------------------- */
    1115             :     /*      Is there a specific nodata value in the file? Either the        */
    1116             :     /*      MISSING or MISSING_CONSTANT keywords are nodata.                */
    1117             :     /* -------------------------------------------------------------------- */
    1118             : 
    1119          33 :     const char *pszMissing = GetKeyword(osPrefix + "IMAGE.MISSING", nullptr);
    1120          33 :     if (pszMissing == nullptr)
    1121          30 :         pszMissing = GetKeyword(osPrefix + "IMAGE.MISSING_CONSTANT", nullptr);
    1122             : 
    1123          33 :     if (pszMissing != nullptr)
    1124             :     {
    1125           9 :         if (*pszMissing == '"')
    1126           3 :             pszMissing++;
    1127             : 
    1128             :         /* For example : MISSING_CONSTANT             = "16#FF7FFFFB#" */
    1129           9 :         if (STARTS_WITH_CI(pszMissing, "16#") &&
    1130           3 :             strlen(pszMissing) >= 3 + 8 + 1 && pszMissing[3 + 8] == '#' &&
    1131           0 :             (eDataType == GDT_Float32 || eDataType == GDT_Float64))
    1132             :         {
    1133           3 :             GUInt32 nVal = PDSConvertFromHex(pszMissing);
    1134             :             float fVal;
    1135           3 :             memcpy(&fVal, &nVal, 4);
    1136           3 :             dfNoData = fVal;
    1137             :         }
    1138             :         else
    1139           6 :             dfNoData = CPLAtofM(pszMissing);
    1140             :     }
    1141             : 
    1142             :     /* -------------------------------------------------------------------- */
    1143             :     /*      Did we get the required keywords?  If not we return with        */
    1144             :     /*      this never having been considered to be a match. This isn't     */
    1145             :     /*      an error!                                                       */
    1146             :     /* -------------------------------------------------------------------- */
    1147          66 :     if (!GDALCheckDatasetDimensions(nCols, nRows) ||
    1148          33 :         !GDALCheckBandCount(l_nBands, false))
    1149             :     {
    1150           0 :         return FALSE;
    1151             :     }
    1152             : 
    1153             :     /* -------------------------------------------------------------------- */
    1154             :     /*      Capture some information from the file that is of interest.     */
    1155             :     /* -------------------------------------------------------------------- */
    1156          33 :     nRasterXSize = nCols;
    1157          33 :     nRasterYSize = nRows;
    1158             : 
    1159             :     /* -------------------------------------------------------------------- */
    1160             :     /*      Open target binary file.                                        */
    1161             :     /* -------------------------------------------------------------------- */
    1162             : 
    1163          33 :     if (eAccess == GA_ReadOnly)
    1164             :     {
    1165          33 :         fpImage = VSIFOpenL(m_osImageFilename, "rb");
    1166          33 :         if (fpImage == nullptr)
    1167             :         {
    1168           0 :             CPLError(CE_Failure, CPLE_OpenFailed, "Failed to open %s.\n%s",
    1169           0 :                      m_osImageFilename.c_str(), VSIStrerror(errno));
    1170           0 :             return FALSE;
    1171             :         }
    1172             :     }
    1173             :     else
    1174             :     {
    1175           0 :         fpImage = VSIFOpenL(m_osImageFilename, "r+b");
    1176           0 :         if (fpImage == nullptr)
    1177             :         {
    1178           0 :             CPLError(CE_Failure, CPLE_OpenFailed,
    1179             :                      "Failed to open %s with write permission.\n%s",
    1180           0 :                      m_osImageFilename.c_str(), VSIStrerror(errno));
    1181           0 :             return FALSE;
    1182             :         }
    1183             :     }
    1184             : 
    1185             :     /* -------------------------------------------------------------------- */
    1186             :     /*      Compute the line offset.                                        */
    1187             :     /* -------------------------------------------------------------------- */
    1188          33 :     const int nItemSize = GDALGetDataTypeSizeBytes(eDataType);
    1189             : 
    1190             :     // Needed for N1349177584_2.LBL from
    1191             :     // https://trac.osgeo.org/gdal/attachment/ticket/3355/PDS-TestFiles.zip
    1192          33 :     int nLineOffset = nLinePrefixBytes;
    1193             : 
    1194             :     int nPixelOffset;
    1195             :     vsi_l_offset nBandOffset;
    1196             : 
    1197         186 :     const auto CPLSM64 = [](int x) { return CPLSM(static_cast<int64_t>(x)); };
    1198             : 
    1199             :     try
    1200             :     {
    1201          33 :         if (eLayout == PDS_BIP)
    1202             :         {
    1203           0 :             nPixelOffset = (CPLSM(nItemSize) * CPLSM(l_nBands)).v();
    1204           0 :             nBandOffset = nItemSize;
    1205             :             nLineOffset =
    1206           0 :                 (CPLSM(nLineOffset) + CPLSM(nPixelOffset) * CPLSM(nCols)).v();
    1207             :         }
    1208          33 :         else if (eLayout == PDS_BSQ)
    1209             :         {
    1210          31 :             nPixelOffset = nItemSize;
    1211             :             nLineOffset =
    1212          31 :                 (CPLSM(nLineOffset) + CPLSM(nPixelOffset) * CPLSM(nCols)).v();
    1213          31 :             nBandOffset = static_cast<vsi_l_offset>(
    1214          31 :                 (CPLSM64(nLineOffset) * CPLSM64(nRows) +
    1215          31 :                  CPLSM64(nSuffixLines) *
    1216          62 :                      (CPLSM64(nCols) + CPLSM64(nSuffixItems)) *
    1217         124 :                      CPLSM64(nSuffixBytes))
    1218          31 :                     .v());
    1219             :         }
    1220             :         else /* assume BIL */
    1221             :         {
    1222           2 :             nPixelOffset = nItemSize;
    1223           2 :             nBandOffset = (CPLSM(nItemSize) * CPLSM(nCols)).v();
    1224             :             nLineOffset =
    1225           2 :                 (CPLSM(nLineOffset) +
    1226           6 :                  CPLSM(static_cast<int>(nBandOffset)) * CPLSM(l_nBands))
    1227           2 :                     .v();
    1228             :         }
    1229             :     }
    1230           0 :     catch (const CPLSafeIntOverflow &)
    1231             :     {
    1232           0 :         CPLError(CE_Failure, CPLE_AppDefined, "Integer overflow");
    1233           0 :         return FALSE;
    1234             :     }
    1235             : 
    1236             :     /* -------------------------------------------------------------------- */
    1237             :     /*      Create band information objects.                                */
    1238             :     /* -------------------------------------------------------------------- */
    1239         278 :     for (int i = 0; i < l_nBands; i++)
    1240             :     {
    1241             :         auto poBand = RawRasterBand::Create(
    1242             :             this, i + 1, fpImage,
    1243         245 :             nSkipBytes + static_cast<vsi_l_offset>(nBandOffset) * i,
    1244             :             nPixelOffset, nLineOffset, eDataType,
    1245             :             chByteOrder == 'I' || chByteOrder == 'L'
    1246         245 :                 ? RawRasterBand::ByteOrder::ORDER_LITTLE_ENDIAN
    1247             :                 : RawRasterBand::ByteOrder::ORDER_BIG_ENDIAN,
    1248         245 :             RawRasterBand::OwnFP::NO);
    1249         245 :         if (!poBand)
    1250           0 :             return FALSE;
    1251             : 
    1252         245 :         if (l_nBands == 1)
    1253             :         {
    1254             :             const char *pszMin =
    1255          31 :                 GetKeyword(osPrefix + "IMAGE.MINIMUM", nullptr);
    1256             :             const char *pszMax =
    1257          31 :                 GetKeyword(osPrefix + "IMAGE.MAXIMUM", nullptr);
    1258          31 :             const char *pszMean = GetKeyword(osPrefix + "IMAGE.MEAN", nullptr);
    1259             :             const char *pszStdDev =
    1260          31 :                 GetKeyword(osPrefix + "IMAGE.STANDARD_DEVIATION", nullptr);
    1261          31 :             if (pszMin != nullptr && pszMax != nullptr && pszMean != nullptr &&
    1262             :                 pszStdDev != nullptr)
    1263             :             {
    1264           0 :                 poBand->SetStatistics(CPLAtofM(pszMin), CPLAtofM(pszMax),
    1265           0 :                                       CPLAtofM(pszMean), CPLAtofM(pszStdDev));
    1266             :             }
    1267             :         }
    1268             : 
    1269         245 :         poBand->SetNoDataValue(dfNoData);
    1270             : 
    1271             :         // Set offset/scale values at the PAM level.
    1272         245 :         poBand->SetOffset(dfOffset);
    1273         245 :         poBand->SetScale(dfScale);
    1274         245 :         if (pszUnit)
    1275           0 :             poBand->SetUnitType(pszUnit);
    1276         245 :         if (pszDesc)
    1277           0 :             poBand->SetDescription(pszDesc);
    1278             : 
    1279         245 :         SetBand(i + 1, std::move(poBand));
    1280             :     }
    1281             : 
    1282          33 :     return TRUE;
    1283             : }
    1284             : 
    1285             : /************************************************************************/
    1286             : /* ==================================================================== */
    1287             : /*                         PDSWrapperRasterBand                         */
    1288             : /*                                                                      */
    1289             : /*      proxy for the jp2 or other compressed bands.                    */
    1290             : /* ==================================================================== */
    1291             : /************************************************************************/
    1292             : class PDSWrapperRasterBand final : public GDALProxyRasterBand
    1293             : {
    1294             :     GDALRasterBand *poBaseBand{};
    1295             : 
    1296             :     CPL_DISALLOW_COPY_ASSIGN(PDSWrapperRasterBand)
    1297             : 
    1298             :   protected:
    1299             :     virtual GDALRasterBand *
    1300             :     RefUnderlyingRasterBand(bool /*bForceOpen*/) const override;
    1301             : 
    1302             :   public:
    1303           3 :     explicit PDSWrapperRasterBand(GDALRasterBand *poBaseBandIn)
    1304           3 :     {
    1305           3 :         this->poBaseBand = poBaseBandIn;
    1306           3 :         eDataType = poBaseBand->GetRasterDataType();
    1307           3 :         poBaseBand->GetBlockSize(&nBlockXSize, &nBlockYSize);
    1308           3 :     }
    1309             : };
    1310             : 
    1311             : GDALRasterBand *
    1312           4 : PDSWrapperRasterBand::RefUnderlyingRasterBand(bool /*bForceOpen*/) const
    1313             : {
    1314           4 :     return poBaseBand;
    1315             : }
    1316             : 
    1317             : /************************************************************************/
    1318             : /*                       ParseCompressedImage()                         */
    1319             : /************************************************************************/
    1320             : 
    1321           3 : int PDSDataset::ParseCompressedImage()
    1322             : 
    1323             : {
    1324             :     const CPLString osFileName =
    1325           9 :         CleanString(GetKeyword("COMPRESSED_FILE.FILE_NAME", ""));
    1326             : 
    1327           6 :     const CPLString osPath = CPLGetPathSafe(GetDescription());
    1328             :     const CPLString osFullFileName =
    1329           6 :         CPLFormFilenameSafe(osPath, osFileName, nullptr);
    1330             : 
    1331           3 :     poCompressedDS =
    1332           3 :         GDALDataset::FromHandle(GDALOpen(osFullFileName, GA_ReadOnly));
    1333             : 
    1334           3 :     if (poCompressedDS == nullptr)
    1335           0 :         return FALSE;
    1336             : 
    1337           3 :     nRasterXSize = poCompressedDS->GetRasterXSize();
    1338           3 :     nRasterYSize = poCompressedDS->GetRasterYSize();
    1339             : 
    1340           6 :     for (int iBand = 0; iBand < poCompressedDS->GetRasterCount(); iBand++)
    1341             :     {
    1342           3 :         SetBand(iBand + 1, new PDSWrapperRasterBand(
    1343           3 :                                poCompressedDS->GetRasterBand(iBand + 1)));
    1344             :     }
    1345             : 
    1346           3 :     return TRUE;
    1347             : }
    1348             : 
    1349             : /************************************************************************/
    1350             : /*                                Open()                                */
    1351             : /************************************************************************/
    1352             : 
    1353          38 : GDALDataset *PDSDataset::Open(GDALOpenInfo *poOpenInfo)
    1354             : {
    1355          38 :     if (!PDSDriverIdentify(poOpenInfo))
    1356           0 :         return nullptr;
    1357             : 
    1358          38 :     const char *pszHdr = reinterpret_cast<char *>(poOpenInfo->pabyHeader);
    1359          38 :     if (strstr(pszHdr, "PDS_VERSION_ID") != nullptr &&
    1360          38 :         strstr(pszHdr, "PDS3") == nullptr)
    1361             :     {
    1362           0 :         CPLError(
    1363             :             CE_Failure, CPLE_OpenFailed,
    1364             :             "It appears this is an older PDS image type.  Only PDS_VERSION_ID "
    1365             :             "= PDS3 are currently supported by this gdal PDS reader.");
    1366           0 :         return nullptr;
    1367             :     }
    1368             : 
    1369             :     /* -------------------------------------------------------------------- */
    1370             :     /*      Parse the keyword header.  Sometimes there is stuff             */
    1371             :     /*      before the PDS_VERSION_ID, which we want to ignore.             */
    1372             :     /* -------------------------------------------------------------------- */
    1373          38 :     VSILFILE *fpQube = poOpenInfo->fpL;
    1374          38 :     poOpenInfo->fpL = nullptr;
    1375             : 
    1376          38 :     PDSDataset *poDS = new PDSDataset();
    1377          38 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1378          38 :     poDS->eAccess = poOpenInfo->eAccess;
    1379             : 
    1380          38 :     const char *pszPDSVersionID = strstr(pszHdr, "PDS_VERSION_ID");
    1381          38 :     int nOffset = 0;
    1382          38 :     if (pszPDSVersionID)
    1383          38 :         nOffset = static_cast<int>(pszPDSVersionID - pszHdr);
    1384             : 
    1385          38 :     if (!poDS->oKeywords.Ingest(fpQube, nOffset))
    1386             :     {
    1387           2 :         delete poDS;
    1388           2 :         VSIFCloseL(fpQube);
    1389           2 :         return nullptr;
    1390             :     }
    1391             :     poDS->m_aosPDSMD.InsertString(
    1392           0 :         0, poDS->oKeywords.GetJsonObject()
    1393          72 :                .Format(CPLJSONObject::PrettyFormat::Pretty)
    1394          72 :                .c_str());
    1395          36 :     VSIFCloseL(fpQube);
    1396             : 
    1397             :     /* -------------------------------------------------------------------- */
    1398             :     /*      Is this a compressed image with COMPRESSED_FILE subdomain?      */
    1399             :     /*                                                                      */
    1400             :     /*      The corresponding parse operations will read keywords,          */
    1401             :     /*      establish bands and raster size.                                */
    1402             :     /* -------------------------------------------------------------------- */
    1403             :     CPLString osEncodingType =
    1404         108 :         poDS->GetKeyword("COMPRESSED_FILE.ENCODING_TYPE", "");
    1405             : 
    1406             :     CPLString osCompressedFilename =
    1407         108 :         CleanString(poDS->GetKeyword("COMPRESSED_FILE.FILE_NAME", ""));
    1408             : 
    1409             :     const char *pszImageName =
    1410          36 :         poDS->GetKeyword("UNCOMPRESSED_FILE.IMAGE.NAME", "");
    1411             :     CPLString osUncompressedFilename =
    1412          36 :         CleanString(!EQUAL(pszImageName, "")
    1413             :                         ? pszImageName
    1414         142 :                         : poDS->GetKeyword("UNCOMPRESSED_FILE.FILE_NAME", ""));
    1415             : 
    1416             :     VSIStatBufL sStat;
    1417          72 :     CPLString osFilenamePrefix;
    1418             : 
    1419          39 :     if (EQUAL(osEncodingType, "ZIP") && !osCompressedFilename.empty() &&
    1420           3 :         !osUncompressedFilename.empty())
    1421             :     {
    1422           3 :         const CPLString osPath = CPLGetPathSafe(poDS->GetDescription());
    1423             :         osCompressedFilename =
    1424           3 :             CPLFormFilenameSafe(osPath, osCompressedFilename, nullptr);
    1425             :         osUncompressedFilename =
    1426           3 :             CPLFormFilenameSafe(osPath, osUncompressedFilename, nullptr);
    1427           3 :         if (VSIStatExL(osCompressedFilename, &sStat, VSI_STAT_EXISTS_FLAG) ==
    1428           6 :                 0 &&
    1429           3 :             VSIStatExL(osUncompressedFilename, &sStat, VSI_STAT_EXISTS_FLAG) !=
    1430             :                 0)
    1431             :         {
    1432           3 :             osFilenamePrefix = "/vsizip/" + osCompressedFilename + "/";
    1433           3 :             poDS->osExternalCube = std::move(osCompressedFilename);
    1434             :         }
    1435           3 :         osEncodingType = "";
    1436             :     }
    1437             : 
    1438          36 :     if (!osEncodingType.empty())
    1439             :     {
    1440           3 :         if (!poDS->ParseCompressedImage())
    1441             :         {
    1442           0 :             delete poDS;
    1443           0 :             return nullptr;
    1444             :         }
    1445             :     }
    1446             :     else
    1447             :     {
    1448          33 :         CPLString osPrefix;
    1449             : 
    1450          33 :         if (osUncompressedFilename != "")
    1451           5 :             osPrefix = "UNCOMPRESSED_FILE.";
    1452             : 
    1453             :         // Added ability to see into OBJECT = FILE section to support
    1454             :         // CRISM. Example file: hsp00017ba0_01_ra218s_trr3.lbl and *.img
    1455          73 :         if (strlen(poDS->GetKeyword("IMAGE.LINE_SAMPLES")) == 0 &&
    1456          40 :             strlen(poDS->GetKeyword("FILE.IMAGE.LINE_SAMPLES")) != 0)
    1457           2 :             osPrefix = "FILE.";
    1458             : 
    1459          33 :         if (!poDS->ParseImage(osPrefix, osFilenamePrefix))
    1460             :         {
    1461           0 :             delete poDS;
    1462           0 :             return nullptr;
    1463             :         }
    1464             :     }
    1465             : 
    1466             :     /* -------------------------------------------------------------------- */
    1467             :     /*      Set the coordinate system and geotransform.                     */
    1468             :     /* -------------------------------------------------------------------- */
    1469          36 :     poDS->ParseSRS();
    1470             : 
    1471             :     /* -------------------------------------------------------------------- */
    1472             :     /*      Transfer a few interesting keywords as metadata.                */
    1473             :     /* -------------------------------------------------------------------- */
    1474             :     static const char *const apszKeywords[] = {"FILTER_NAME",
    1475             :                                                "DATA_SET_ID",
    1476             :                                                "PRODUCT_ID",
    1477             :                                                "PRODUCER_INSTITUTION_NAME",
    1478             :                                                "PRODUCT_TYPE",
    1479             :                                                "MISSION_NAME",
    1480             :                                                "SPACECRAFT_NAME",
    1481             :                                                "INSTRUMENT_NAME",
    1482             :                                                "INSTRUMENT_ID",
    1483             :                                                "TARGET_NAME",
    1484             :                                                "CENTER_FILTER_WAVELENGTH",
    1485             :                                                "BANDWIDTH",
    1486             :                                                "PRODUCT_CREATION_TIME",
    1487             :                                                "START_TIME",
    1488             :                                                "STOP_TIME",
    1489             :                                                "NOTE",
    1490             :                                                nullptr};
    1491             : 
    1492         612 :     for (int i = 0; apszKeywords[i] != nullptr; i++)
    1493             :     {
    1494         576 :         const char *pszKeywordValue = poDS->GetKeyword(apszKeywords[i]);
    1495             : 
    1496         576 :         if (pszKeywordValue != nullptr)
    1497         576 :             poDS->SetMetadataItem(apszKeywords[i], pszKeywordValue);
    1498             :     }
    1499             : 
    1500             :     /* -------------------------------------------------------------------- */
    1501             :     /*      Initialize any PAM information.                                 */
    1502             :     /* -------------------------------------------------------------------- */
    1503          36 :     poDS->TryLoadXML();
    1504             : 
    1505             :     /* -------------------------------------------------------------------- */
    1506             :     /*      Check for overviews.                                            */
    1507             :     /* -------------------------------------------------------------------- */
    1508          36 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename);
    1509             : 
    1510          36 :     return poDS;
    1511             : }
    1512             : 
    1513             : /************************************************************************/
    1514             : /*                             GetKeyword()                             */
    1515             : /************************************************************************/
    1516             : 
    1517        1965 : const char *PDSDataset::GetKeyword(const std::string &osPath,
    1518             :                                    const char *pszDefault)
    1519             : 
    1520             : {
    1521        1965 :     return oKeywords.GetKeyword(osPath.c_str(), pszDefault);
    1522             : }
    1523             : 
    1524             : /************************************************************************/
    1525             : /*                            GetKeywordSub()                           */
    1526             : /************************************************************************/
    1527             : 
    1528          27 : const char *PDSDataset::GetKeywordSub(const std::string &osPath, int iSubscript,
    1529             :                                       const char *pszDefault)
    1530             : 
    1531             : {
    1532          27 :     const char *pszResult = oKeywords.GetKeyword(osPath.c_str(), nullptr);
    1533             : 
    1534          27 :     if (pszResult == nullptr)
    1535           0 :         return pszDefault;
    1536             : 
    1537          27 :     if (pszResult[0] != '(')
    1538           0 :         return pszDefault;
    1539             : 
    1540             :     char **papszTokens =
    1541          27 :         CSLTokenizeString2(pszResult, "(,)", CSLT_HONOURSTRINGS);
    1542             : 
    1543          27 :     if (iSubscript <= CSLCount(papszTokens))
    1544             :     {
    1545          27 :         osTempResult = papszTokens[iSubscript - 1];
    1546          27 :         CSLDestroy(papszTokens);
    1547          27 :         return osTempResult.c_str();
    1548             :     }
    1549             : 
    1550           0 :     CSLDestroy(papszTokens);
    1551           0 :     return pszDefault;
    1552             : }
    1553             : 
    1554             : /************************************************************************/
    1555             : /*                            GetKeywordUnit()                          */
    1556             : /************************************************************************/
    1557             : 
    1558          25 : const char *PDSDataset::GetKeywordUnit(const char *pszPath, int iSubscript,
    1559             :                                        const char *pszDefault)
    1560             : 
    1561             : {
    1562          25 :     const char *pszResult = oKeywords.GetKeyword(pszPath, nullptr);
    1563             : 
    1564          25 :     if (pszResult == nullptr)
    1565           0 :         return pszDefault;
    1566             : 
    1567             :     char **papszTokens =
    1568          25 :         CSLTokenizeString2(pszResult, "</>", CSLT_HONOURSTRINGS);
    1569             : 
    1570          25 :     if (iSubscript <= CSLCount(papszTokens))
    1571             :     {
    1572          18 :         osTempResult = papszTokens[iSubscript - 1];
    1573          18 :         CSLDestroy(papszTokens);
    1574          18 :         return osTempResult.c_str();
    1575             :     }
    1576             : 
    1577           7 :     CSLDestroy(papszTokens);
    1578           7 :     return pszDefault;
    1579             : }
    1580             : 
    1581             : /************************************************************************/
    1582             : /*                            CleanString()                             */
    1583             : /*                                                                      */
    1584             : /* Removes single or double quotes, and converts spaces to underscores. */
    1585             : /************************************************************************/
    1586             : 
    1587         193 : CPLString PDSDataset::CleanString(const CPLString &osInput)
    1588             : 
    1589             : {
    1590         310 :     if ((osInput.size() < 2) ||
    1591         117 :         ((osInput.at(0) != '"' || osInput.back() != '"') &&
    1592          55 :          (osInput.at(0) != '\'' || osInput.back() != '\'')))
    1593         131 :         return osInput;
    1594             : 
    1595          62 :     char *pszWrk = CPLStrdup(osInput.c_str() + 1);
    1596             : 
    1597          62 :     pszWrk[strlen(pszWrk) - 1] = '\0';
    1598             : 
    1599         967 :     for (int i = 0; pszWrk[i] != '\0'; i++)
    1600             :     {
    1601         905 :         if (pszWrk[i] == ' ')
    1602          16 :             pszWrk[i] = '_';
    1603             :     }
    1604             : 
    1605         124 :     CPLString osOutput = pszWrk;
    1606          62 :     CPLFree(pszWrk);
    1607          62 :     return osOutput;
    1608             : }
    1609             : 
    1610             : /************************************************************************/
    1611             : /*                      GetMetadataDomainList()                         */
    1612             : /************************************************************************/
    1613             : 
    1614           0 : char **PDSDataset::GetMetadataDomainList()
    1615             : {
    1616           0 :     return BuildMetadataDomainList(nullptr, FALSE, "", "json:PDS", nullptr);
    1617             : }
    1618             : 
    1619             : /************************************************************************/
    1620             : /*                             GetMetadata()                            */
    1621             : /************************************************************************/
    1622             : 
    1623           1 : char **PDSDataset::GetMetadata(const char *pszDomain)
    1624             : {
    1625           1 :     if (pszDomain != nullptr && EQUAL(pszDomain, "json:PDS"))
    1626             :     {
    1627           0 :         return m_aosPDSMD.List();
    1628             :     }
    1629           1 :     return GDALPamDataset::GetMetadata(pszDomain);
    1630             : }
    1631             : 
    1632             : /************************************************************************/
    1633             : /*                         GDALRegister_PDS()                           */
    1634             : /************************************************************************/
    1635             : 
    1636        1935 : void GDALRegister_PDS()
    1637             : 
    1638             : {
    1639        1935 :     if (GDALGetDriverByName(PDS_DRIVER_NAME) != nullptr)
    1640         282 :         return;
    1641             : 
    1642        1653 :     GDALDriver *poDriver = new GDALDriver();
    1643        1653 :     PDSDriverSetCommonMetadata(poDriver);
    1644             : 
    1645        1653 :     poDriver->pfnOpen = PDSDataset::Open;
    1646             : 
    1647        1653 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    1648             : 
    1649             : #ifdef PDS_PLUGIN
    1650             :     GDALRegister_ISIS3();
    1651             :     GDALRegister_ISIS2();
    1652             :     GDALRegister_PDS4();
    1653             :     GDALRegister_VICAR();
    1654             : #endif
    1655             : }

Generated by: LCOV version 1.14