LCOV - code coverage report
Current view: top level - frmts/pds - pdsdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 510 656 77.7 %
Date: 2024-05-03 15:49:35 Functions: 25 28 89.3 %

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

Generated by: LCOV version 1.14