LCOV - code coverage report
Current view: top level - frmts/hdf4 - hdf4imagedataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 519 1721 30.2 %
Date: 2024-11-21 22:18:42 Functions: 23 38 60.5 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  Hierarchical Data Format Release 4 (HDF4)
       4             :  * Purpose:  Read subdatasets of HDF4 file.
       5             :  *           This driver initially based on code supplied by Markus Neteler
       6             :  * Author:   Andrey Kiselev, dron@ak4719.spb.edu
       7             :  *
       8             :  ******************************************************************************
       9             :  * Copyright (c) 2002, Andrey Kiselev <dron@ak4719.spb.edu>
      10             :  * Copyright (c) 2009-2013, Even Rouault <even dot rouault at spatialys.com>
      11             :  *
      12             :  * SPDX-License-Identifier: MIT
      13             :  ****************************************************************************/
      14             : 
      15             : #if defined(_WIN32) && !defined(NOMINMAX)
      16             : // min/max are defined here on Windows, so block them.
      17             : // TODO: Move this to someplace more appropriate.
      18             : #define NOMINMAX
      19             : #endif
      20             : 
      21             : #include <string.h>
      22             : #include <math.h>
      23             : 
      24             : #include "cpl_multiproc.h"
      25             : #include "cpl_string.h"
      26             : #include "gdal_frmts.h"
      27             : #include "gdal_priv.h"
      28             : #include "ogr_spatialref.h"
      29             : 
      30             : #include "hdf.h"
      31             : #include "mfhdf.h"
      32             : 
      33             : #include "HdfEosDef.h"
      34             : 
      35             : #include "hdf4compat.h"
      36             : #include "hdf4dataset.h"
      37             : 
      38             : #include "nasakeywordhandler.h"
      39             : 
      40             : #include "hdf4drivercore.h"
      41             : 
      42             : #include <algorithm>
      43             : 
      44             : constexpr int HDF4_SDS_MAXNAMELEN = 65;
      45             : 
      46             : extern const char *const pszGDALSignature;
      47             : 
      48             : // Signature to recognize files written by GDAL.
      49             : const char *const pszGDALSignature =
      50             :     "Created with GDAL (http://www.remotesensing.org/gdal/)";
      51             : 
      52             : extern CPLMutex *hHDF4Mutex;
      53             : 
      54             : constexpr int N_BUF_SIZE = 8192;
      55             : 
      56             : /************************************************************************/
      57             : /* ==================================================================== */
      58             : /*  List of HDF-EOS Swath product types.                                */
      59             : /* ==================================================================== */
      60             : /************************************************************************/
      61             : 
      62             : enum HDF4EOSProduct
      63             : {
      64             :     PROD_UNKNOWN,
      65             :     PROD_ASTER_L1A,
      66             :     PROD_ASTER_L1B,
      67             :     PROD_ASTER_L2,
      68             :     PROD_ASTER_L3,
      69             :     PROD_AST14DEM,
      70             :     PROD_MODIS_L1B,
      71             :     PROD_MODIS_L2
      72             : };
      73             : 
      74             : /************************************************************************/
      75             : /* ==================================================================== */
      76             : /*                              HDF4ImageDataset                        */
      77             : /* ==================================================================== */
      78             : /************************************************************************/
      79             : 
      80             : constexpr int N_COLOR_ENTRIES = 256;
      81             : 
      82             : class HDF4ImageDataset final : public HDF4Dataset
      83             : {
      84             :     friend class HDF4ImageRasterBand;
      85             : 
      86             :     char *pszFilename;
      87             :     int32 hHDF4;
      88             :     int32 iGR;
      89             :     int32 iPal;
      90             :     int32 iDataset;
      91             :     int32 iRank;
      92             :     int32 iNumType;
      93             :     int32 nAttrs;
      94             :     int32 iInterlaceMode;
      95             :     int32 iPalInterlaceMode;
      96             :     int32 iPalDataType;
      97             :     int32 nComps;
      98             :     int32 nPalEntries;
      99             :     int32 aiDimSizes[H4_MAX_VAR_DIMS];
     100             :     int iXDim;
     101             :     int iYDim;
     102             :     int iBandDim;
     103             :     int i4Dim;
     104             :     int nBandCount;
     105             :     char **papszLocalMetadata;
     106             :     uint8 aiPaletteData[N_COLOR_ENTRIES][3];  // XXX: Static array for now
     107             :     char szName[HDF4_SDS_MAXNAMELEN];
     108             :     char *pszSubdatasetName;
     109             :     char *pszFieldName;
     110             : 
     111             :     GDALColorTable *poColorTable;
     112             : 
     113             :     OGRSpatialReference m_oSRS{};
     114             :     OGRSpatialReference m_oGCPSRS{};
     115             :     bool bHasGeoTransform;
     116             :     double adfGeoTransform[6];
     117             :     std::vector<gdal::GCP> m_aoGCPs{};
     118             : 
     119             :     HDF4DatasetType iDatasetType;
     120             : 
     121             :     int32 iSDS;
     122             : 
     123             :     int nBlockPreferredXSize;
     124             :     int nBlockPreferredYSize;
     125             :     bool bReadTile;
     126             : 
     127             :     void ToGeoref(double *, double *);
     128             :     void GetImageDimensions(char *);
     129             :     void GetSwatAttrs(int32);
     130             :     void GetGridAttrs(int32 hGD);
     131             :     void CaptureNRLGeoTransform(void);
     132             :     void CaptureL1GMTLInfo(void);
     133             :     void CaptureCoastwatchGCTPInfo(void);
     134             :     void ProcessModisSDSGeolocation(void);
     135             :     int ProcessSwathGeolocation(int32, char **);
     136             : 
     137             :     static long USGSMnemonicToCode(const char *);
     138             :     static void ReadCoordinates(const char *, double *, double *);
     139             : 
     140             :   public:
     141             :     HDF4ImageDataset();
     142             :     virtual ~HDF4ImageDataset();
     143             : 
     144             :     static GDALDataset *Open(GDALOpenInfo *);
     145             :     static GDALDataset *Create(const char *pszFilename, int nXSize, int nYSize,
     146             :                                int nBandsIn, GDALDataType eType,
     147             :                                char **papszParamList);
     148             :     virtual CPLErr FlushCache(bool bAtClosing) override;
     149             :     CPLErr GetGeoTransform(double *padfTransform) override;
     150             :     virtual CPLErr SetGeoTransform(double *) override;
     151             :     const OGRSpatialReference *GetSpatialRef() const override;
     152             :     CPLErr SetSpatialRef(const OGRSpatialReference *poSRS) override;
     153             :     virtual int GetGCPCount() override;
     154             :     const OGRSpatialReference *GetGCPSpatialRef() const override;
     155             :     virtual const GDAL_GCP *GetGCPs() override;
     156             : };
     157             : 
     158             : /************************************************************************/
     159             : /* ==================================================================== */
     160             : /*                            HDF4ImageRasterBand                       */
     161             : /* ==================================================================== */
     162             : /************************************************************************/
     163             : 
     164             : class HDF4ImageRasterBand final : public GDALPamRasterBand
     165             : {
     166             :     friend class HDF4ImageDataset;
     167             : 
     168             :     bool bNoDataSet;
     169             :     double dfNoDataValue;
     170             : 
     171             :     bool bHaveScale;
     172             :     bool bHaveOffset;
     173             :     double dfScale;
     174             :     double dfOffset;
     175             : 
     176             :     CPLString osUnitType;
     177             : 
     178             :   public:
     179             :     HDF4ImageRasterBand(HDF4ImageDataset *, int, GDALDataType);
     180             : 
     181        1088 :     virtual ~HDF4ImageRasterBand()
     182         544 :     {
     183        1088 :     }
     184             : 
     185             :     virtual CPLErr IReadBlock(int, int, void *) override;
     186             :     virtual CPLErr IWriteBlock(int, int, void *) override;
     187             :     virtual GDALColorInterp GetColorInterpretation() override;
     188             :     virtual GDALColorTable *GetColorTable() override;
     189             :     virtual double GetNoDataValue(int *) override;
     190             :     virtual CPLErr SetNoDataValue(double) override;
     191             :     virtual double GetOffset(int *pbSuccess) override;
     192             :     virtual double GetScale(int *pbSuccess) override;
     193             :     virtual const char *GetUnitType() override;
     194             : };
     195             : 
     196             : /************************************************************************/
     197             : /*                           HDF4ImageRasterBand()                      */
     198             : /************************************************************************/
     199             : 
     200         544 : HDF4ImageRasterBand::HDF4ImageRasterBand(HDF4ImageDataset *poDSIn, int nBandIn,
     201         544 :                                          GDALDataType eType)
     202             :     : bNoDataSet(false), dfNoDataValue(-9999.0), bHaveScale(false),
     203         544 :       bHaveOffset(false), dfScale(1.0), dfOffset(0.0)
     204             : {
     205         544 :     poDS = poDSIn;
     206         544 :     nBand = nBandIn;
     207         544 :     eDataType = eType;
     208             : 
     209         544 :     nBlockXSize = poDSIn->GetRasterXSize();
     210             : 
     211             :     // Aim for a block of about 1000000 pixels.  Chunking up substantially
     212             :     // improves performance in some situations.  For now we only chunk up for
     213             :     // SDS and EOS based datasets since other variations haven't been
     214             :     // tested. #2208
     215         544 :     if (poDSIn->iDatasetType == HDF4_SDS || poDSIn->iDatasetType == HDF4_EOS)
     216             :     {
     217             :         const int nChunkSize =
     218         541 :             atoi(CPLGetConfigOption("HDF4_BLOCK_PIXELS", "1000000"));
     219             : 
     220         541 :         nBlockYSize = nChunkSize / poDSIn->GetRasterXSize();
     221         541 :         nBlockYSize =
     222         541 :             std::max(1, std::min(nBlockYSize, poDSIn->GetRasterYSize()));
     223             :     }
     224             :     else
     225             :     {
     226           3 :         nBlockYSize = 1;
     227             :     }
     228             : 
     229             :     // HDF4_EOS:EOS_GRID case. We ensure that the block size matches
     230             :     // the raster width, as the IReadBlock() code can only handle multiple
     231             :     // blocks per row.
     232         544 :     if (poDSIn->nBlockPreferredXSize == nBlockXSize &&
     233           0 :         poDSIn->nBlockPreferredYSize > 0)
     234             :     {
     235           0 :         if (poDSIn->nBlockPreferredYSize == 1)
     236             :         {
     237             :             // Avoid defaulting to tile reading when the preferred height is 1
     238             :             // as it leads to very poor performance with:
     239             :             // ftp://e4ftl01u.ecs.nasa.gov/MODIS_Composites/MOLT/MOD13Q1.005/2006.06.10/MOD13Q1.A2006161.h21v13.005.2008234103220.hd
     240           0 :             poDSIn->bReadTile = false;
     241             :         }
     242             :         else
     243             :         {
     244           0 :             nBlockYSize = poDSIn->nBlockPreferredYSize;
     245             :         }
     246             :     }
     247             : 
     248             :     /* -------------------------------------------------------------------- */
     249             :     /*      We need to avoid using the tile based api if we aren't          */
     250             :     /*      matching the tile size. (#4672)                                 */
     251             :     /* -------------------------------------------------------------------- */
     252         544 :     if (nBlockXSize != poDSIn->nBlockPreferredXSize ||
     253           0 :         nBlockYSize != poDSIn->nBlockPreferredYSize)
     254             :     {
     255         544 :         poDSIn->bReadTile = false;
     256             :     }
     257         544 : }
     258             : 
     259             : /************************************************************************/
     260             : /*                             IReadBlock()                             */
     261             : /************************************************************************/
     262             : 
     263          62 : CPLErr HDF4ImageRasterBand::IReadBlock(int nBlockXOff, int nBlockYOff,
     264             :                                        void *pImage)
     265             : {
     266          62 :     CPLAssert(nBlockXOff == 0);
     267          62 :     HDF4ImageDataset *poGDS = reinterpret_cast<HDF4ImageDataset *>(poDS);
     268             : 
     269         124 :     CPLMutexHolderD(&hHDF4Mutex);
     270             : 
     271          62 :     if (poGDS->eAccess == GA_Update)
     272             :     {
     273           0 :         memset(pImage, 0,
     274           0 :                nBlockXSize * nBlockYSize * GDALGetDataTypeSizeBytes(eDataType));
     275           0 :         return CE_None;
     276             :     }
     277             : 
     278             :     /* -------------------------------------------------------------------- */
     279             :     /*      Work out some block oriented details.                           */
     280             :     /* -------------------------------------------------------------------- */
     281          62 :     const int nYOff = nBlockYOff * nBlockYSize;
     282             :     const int nYSize =
     283          62 :         std::min(nYOff + nBlockYSize, poDS->GetRasterYSize()) - nYOff;
     284             : 
     285             :     /* -------------------------------------------------------------------- */
     286             :     /*      HDF files with external data files, such as some landsat        */
     287             :     /*      products (eg. data/hdf/L1G) need to be told what directory      */
     288             :     /*      to look in to find the external files.  Normally this is the    */
     289             :     /*      directory holding the hdf file.                                 */
     290             :     /* -------------------------------------------------------------------- */
     291          62 :     HXsetdir(CPLGetPath(poGDS->pszFilename));
     292             : 
     293             :     /* -------------------------------------------------------------------- */
     294             :     /*      Handle different configurations.                                */
     295             :     /* -------------------------------------------------------------------- */
     296          62 :     CPLErr eErr = CE_None;
     297          62 :     int32 aiStart[H4_MAX_NC_DIMS] = {};
     298          62 :     int32 aiEdges[H4_MAX_NC_DIMS] = {};
     299             : 
     300          62 :     switch (poGDS->iDatasetType)
     301             :     {
     302          52 :         case HDF4_SDS:
     303             :         {
     304             :             // We avoid doing SDselect() / SDendaccess() for each block access
     305             :             // as this is very slow when zlib compression is used.
     306             : 
     307          52 :             if (poGDS->iSDS == FAIL)
     308          52 :                 poGDS->iSDS = SDselect(poGDS->hSD, poGDS->iDataset);
     309             : 
     310             :             /* HDF rank:
     311             :                A rank 2 dataset is an image read in scan-line order (2D).
     312             :                A rank 3 dataset is a series of images which are read in
     313             :                an image at a time to form a volume.
     314             :                A rank 4 dataset may be thought of as a series of volumes.
     315             : 
     316             :                The "aiStart" array specifies the multi-dimensional index of the
     317             :                starting corner of the hyperslab to read. The values are zero
     318             :                based.
     319             : 
     320             :                The "edge" array specifies the number of values to read along
     321             :                each dimension of the hyperslab.
     322             : 
     323             :                The "iStride" array allows for sub-sampling along each
     324             :                dimension. If a iStride value is specified for a dimension,
     325             :                that many values will be skipped over when reading along that
     326             :                dimension. Specifying iStride = NULL in the C interface or
     327             :                iStride = 1 in either interface specifies contiguous reading
     328             :                of data. If the iStride values are set to 0, SDreaddata
     329             :                returns FAIL (or -1). No matter what iStride value is
     330             :                provided, data is always placed contiguously in buffer.
     331             :             */
     332          52 :             switch (poGDS->iRank)
     333             :             {
     334           0 :                 case 4:  // 4Dim: volume-time
     335             :                          // FIXME: needs sample file. Does not work currently.
     336           0 :                     aiStart[3] = 0;  // range: 0--aiDimSizes[3]-1
     337           0 :                     aiEdges[3] = 1;
     338           0 :                     aiStart[2] = 0;  // range: 0--aiDimSizes[2]-1
     339           0 :                     aiEdges[2] = 1;
     340           0 :                     aiStart[1] = nYOff;
     341           0 :                     aiEdges[1] = nYSize;
     342           0 :                     aiStart[0] = nBlockXOff;
     343           0 :                     aiEdges[0] = nBlockXSize;
     344           0 :                     break;
     345          25 :                 case 3:  // 3Dim: volume
     346          25 :                     aiStart[poGDS->iBandDim] = nBand - 1;
     347          25 :                     aiEdges[poGDS->iBandDim] = 1;
     348             : 
     349          25 :                     aiStart[poGDS->iYDim] = nYOff;
     350          25 :                     aiEdges[poGDS->iYDim] = nYSize;
     351             : 
     352          25 :                     aiStart[poGDS->iXDim] = nBlockXOff;
     353          25 :                     aiEdges[poGDS->iXDim] = nBlockXSize;
     354          25 :                     break;
     355          27 :                 case 2:  // 2Dim: rows/cols
     356          27 :                     aiStart[poGDS->iYDim] = nYOff;
     357          27 :                     aiEdges[poGDS->iYDim] = nYSize;
     358             : 
     359          27 :                     aiStart[poGDS->iXDim] = nBlockXOff;
     360          27 :                     aiEdges[poGDS->iXDim] = nBlockXSize;
     361          27 :                     break;
     362           0 :                 case 1:  // 1Dim:
     363           0 :                     aiStart[poGDS->iXDim] = nBlockXOff;
     364           0 :                     aiEdges[poGDS->iXDim] = nBlockXSize;
     365           0 :                     break;
     366             :             }
     367             : 
     368             :             // Read HDF SDS array
     369          52 :             if (SDreaddata(poGDS->iSDS, aiStart, nullptr, aiEdges, pImage) < 0)
     370             :             {
     371           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     372             :                          "SDreaddata() failed for block.");
     373           0 :                 eErr = CE_Failure;
     374             :             }
     375             : 
     376             :             // SDendaccess( l_iSDS );
     377             :         }
     378          52 :         break;
     379             : 
     380          10 :         case HDF4_GR:
     381             :         {
     382             :             const int nDataTypeSize =
     383          10 :                 GDALGetDataTypeSizeBytes(poGDS->GetDataType(poGDS->iNumType));
     384          20 :             GByte *pbBuffer = reinterpret_cast<GByte *>(CPLMalloc(
     385          10 :                 nBlockXSize * nBlockYSize * poGDS->iRank * nDataTypeSize));
     386             : 
     387          10 :             aiStart[poGDS->iYDim] = nYOff;
     388          10 :             aiEdges[poGDS->iYDim] = nYSize;
     389             : 
     390          10 :             aiStart[poGDS->iXDim] = nBlockXOff;
     391          10 :             aiEdges[poGDS->iXDim] = nBlockXSize;
     392             : 
     393          10 :             if (GRreadimage(poGDS->iGR, aiStart, nullptr, aiEdges, pbBuffer) <
     394             :                 0)
     395             :             {
     396           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
     397             :                          "GRreaddata() failed for block.");
     398           0 :                 eErr = CE_Failure;
     399             :             }
     400             :             else
     401             :             {
     402          10 :                 for (int i = 0, j = (nBand - 1) * nDataTypeSize;
     403         110 :                      i < nBlockXSize * nDataTypeSize;
     404         100 :                      i += nDataTypeSize, j += poGDS->nBands * nDataTypeSize)
     405         100 :                     memcpy(reinterpret_cast<GByte *>(pImage) + i, pbBuffer + j,
     406             :                            nDataTypeSize);
     407             :             }
     408             : 
     409          10 :             CPLFree(pbBuffer);
     410             :         }
     411          10 :         break;
     412             : 
     413           0 :         case HDF4_EOS:
     414             :         {
     415           0 :             switch (poGDS->iSubdatasetType)
     416             :             {
     417           0 :                 case H4ST_EOS_GRID:
     418             :                 {
     419             :                     const int32 hGD =
     420           0 :                         GDattach(poGDS->hHDF4, poGDS->pszSubdatasetName);
     421           0 :                     switch (poGDS->iRank)
     422             :                     {
     423           0 :                         case 4:  // 4Dim: volume
     424           0 :                             aiStart[poGDS->i4Dim] =
     425           0 :                                 (nBand - 1) /
     426           0 :                                 poGDS->aiDimSizes[poGDS->iBandDim];
     427           0 :                             aiEdges[poGDS->i4Dim] = 1;
     428             : 
     429           0 :                             aiStart[poGDS->iBandDim] =
     430           0 :                                 (nBand - 1) %
     431           0 :                                 poGDS->aiDimSizes[poGDS->iBandDim];
     432           0 :                             aiEdges[poGDS->iBandDim] = 1;
     433             : 
     434           0 :                             aiStart[poGDS->iYDim] = nYOff;
     435           0 :                             aiEdges[poGDS->iYDim] = nYSize;
     436             : 
     437           0 :                             aiStart[poGDS->iXDim] = nBlockXOff;
     438           0 :                             aiEdges[poGDS->iXDim] = nBlockXSize;
     439           0 :                             break;
     440           0 :                         case 3:  // 3Dim: volume
     441           0 :                             aiStart[poGDS->iBandDim] = nBand - 1;
     442           0 :                             aiEdges[poGDS->iBandDim] = 1;
     443             : 
     444           0 :                             aiStart[poGDS->iYDim] = nYOff;
     445           0 :                             aiEdges[poGDS->iYDim] = nYSize;
     446             : 
     447           0 :                             aiStart[poGDS->iXDim] = nBlockXOff;
     448           0 :                             aiEdges[poGDS->iXDim] = nBlockXSize;
     449           0 :                             break;
     450           0 :                         case 2:  // 2Dim: rows/cols
     451           0 :                             aiStart[poGDS->iYDim] = nYOff;
     452           0 :                             aiEdges[poGDS->iYDim] = nYSize;
     453             : 
     454           0 :                             aiStart[poGDS->iXDim] = nBlockXOff;
     455           0 :                             aiEdges[poGDS->iXDim] = nBlockXSize;
     456           0 :                             break;
     457             :                     }
     458             : 
     459             :                     /* Ensure that we don't overlap the bottom or right edges */
     460             :                     /* of the dataset in order to use the GDreadtile() API */
     461           0 :                     if (poGDS->bReadTile &&
     462           0 :                         (nBlockXOff + 1) * nBlockXSize <= nRasterXSize &&
     463           0 :                         (nBlockYOff + 1) * nBlockYSize <= nRasterYSize)
     464             :                     {
     465           0 :                         int32 tilecoords[] = {nBlockYOff, nBlockXOff};
     466           0 :                         if (GDreadtile(hGD, poGDS->pszFieldName, tilecoords,
     467           0 :                                        pImage) != 0)
     468             :                         {
     469           0 :                             CPLError(CE_Failure, CPLE_AppDefined,
     470             :                                      "GDreadtile() failed for block.");
     471           0 :                             eErr = CE_Failure;
     472           0 :                         }
     473             :                     }
     474           0 :                     else if (GDreadfield(hGD, poGDS->pszFieldName, aiStart,
     475           0 :                                          nullptr, aiEdges, pImage) < 0)
     476             :                     {
     477           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     478             :                                  "GDreadfield() failed for block.");
     479           0 :                         eErr = CE_Failure;
     480             :                     }
     481           0 :                     GDdetach(hGD);
     482             :                 }
     483           0 :                 break;
     484             : 
     485           0 :                 case H4ST_EOS_SWATH:
     486             :                 case H4ST_EOS_SWATH_GEOL:
     487             :                 {
     488             :                     const int32 hSW =
     489           0 :                         SWattach(poGDS->hHDF4, poGDS->pszSubdatasetName);
     490           0 :                     switch (poGDS->iRank)
     491             :                     {
     492           0 :                         case 3:  // 3Dim: volume
     493           0 :                             aiStart[poGDS->iBandDim] = nBand - 1;
     494           0 :                             aiEdges[poGDS->iBandDim] = 1;
     495             : 
     496           0 :                             aiStart[poGDS->iYDim] = nYOff;
     497           0 :                             aiEdges[poGDS->iYDim] = nYSize;
     498             : 
     499           0 :                             aiStart[poGDS->iXDim] = nBlockXOff;
     500           0 :                             aiEdges[poGDS->iXDim] = nBlockXSize;
     501           0 :                             break;
     502           0 :                         case 2:  // 2Dim: rows/cols
     503           0 :                             aiStart[poGDS->iYDim] = nYOff;
     504           0 :                             aiEdges[poGDS->iYDim] = nYSize;
     505             : 
     506           0 :                             aiStart[poGDS->iXDim] = nBlockXOff;
     507           0 :                             aiEdges[poGDS->iXDim] = nBlockXSize;
     508           0 :                             break;
     509             :                     }
     510           0 :                     if (SWreadfield(hSW, poGDS->pszFieldName, aiStart, nullptr,
     511           0 :                                     aiEdges, pImage) < 0)
     512             :                     {
     513           0 :                         CPLError(CE_Failure, CPLE_AppDefined,
     514             :                                  "SWreadfield() failed for block.");
     515           0 :                         eErr = CE_Failure;
     516             :                     }
     517           0 :                     SWdetach(hSW);
     518             :                 }
     519           0 :                 break;
     520           0 :                 default:
     521           0 :                     break;
     522             :             }
     523             :         }
     524           0 :         break;
     525             : 
     526           0 :         default:
     527           0 :             eErr = CE_Failure;
     528           0 :             break;
     529             :     }
     530             : 
     531          62 :     return eErr;
     532             : }
     533             : 
     534             : /************************************************************************/
     535             : /*                            IWriteBlock()                             */
     536             : /************************************************************************/
     537             : 
     538          57 : CPLErr HDF4ImageRasterBand::IWriteBlock(int nBlockXOff, int nBlockYOff,
     539             :                                         void *pImage)
     540             : {
     541          57 :     CPLAssert(nBlockXOff == 0);
     542          57 :     CPLAssert(nBlockYOff >= 0);
     543          57 :     CPLAssert(pImage != nullptr);
     544             : 
     545          57 :     HDF4ImageDataset *poGDS = reinterpret_cast<HDF4ImageDataset *>(poDS);
     546          57 :     CPLAssert(poGDS != nullptr);
     547             : 
     548          57 :     int32 aiStart[H4_MAX_NC_DIMS] = {};
     549          57 :     int32 aiEdges[H4_MAX_NC_DIMS] = {};
     550          57 :     CPLErr eErr = CE_None;
     551             : 
     552          57 :     CPLMutexHolderD(&hHDF4Mutex);
     553             : 
     554             :     /* -------------------------------------------------------------------- */
     555             :     /*      Work out some block oriented details.                           */
     556             :     /* -------------------------------------------------------------------- */
     557          57 :     const int nYOff = nBlockYOff * nBlockYSize;
     558             :     const int nYSize =
     559          57 :         std::min(nYOff + nBlockYSize, poDS->GetRasterYSize()) - nYOff;
     560             : 
     561             :     /* -------------------------------------------------------------------- */
     562             :     /*      Process based on rank.                                          */
     563             :     /* -------------------------------------------------------------------- */
     564          57 :     switch (poGDS->iRank)
     565             :     {
     566          39 :         case 3:
     567             :         {
     568          39 :             const int32 l_iSDS = SDselect(poGDS->hSD, poGDS->iDataset);
     569             : 
     570          39 :             aiStart[poGDS->iBandDim] = nBand - 1;
     571          39 :             aiEdges[poGDS->iBandDim] = 1;
     572             : 
     573          39 :             aiStart[poGDS->iYDim] = nYOff;
     574          39 :             aiEdges[poGDS->iYDim] = nYSize;
     575             : 
     576          39 :             aiStart[poGDS->iXDim] = nBlockXOff;
     577          39 :             aiEdges[poGDS->iXDim] = nBlockXSize;
     578             : 
     579          39 :             if ((SDwritedata(l_iSDS, aiStart, nullptr, aiEdges,
     580          39 :                              (VOIDP)pImage)) < 0)
     581           0 :                 eErr = CE_Failure;
     582             : 
     583          39 :             SDendaccess(l_iSDS);
     584             :         }
     585          39 :         break;
     586             : 
     587          18 :         case 2:
     588             :         {
     589          18 :             const int32 l_iSDS = SDselect(poGDS->hSD, nBand - 1);
     590          18 :             aiStart[poGDS->iYDim] = nYOff;
     591          18 :             aiEdges[poGDS->iYDim] = nYSize;
     592             : 
     593          18 :             aiStart[poGDS->iXDim] = nBlockXOff;
     594          18 :             aiEdges[poGDS->iXDim] = nBlockXSize;
     595             : 
     596          18 :             if ((SDwritedata(l_iSDS, aiStart, nullptr, aiEdges,
     597          18 :                              (VOIDP)pImage)) < 0)
     598           0 :                 eErr = CE_Failure;
     599             : 
     600          18 :             SDendaccess(l_iSDS);
     601             :         }
     602          18 :         break;
     603             : 
     604           0 :         default:
     605           0 :             eErr = CE_Failure;
     606           0 :             break;
     607             :     }
     608             : 
     609         114 :     return eErr;
     610             : }
     611             : 
     612             : /************************************************************************/
     613             : /*                           GetColorTable()                            */
     614             : /************************************************************************/
     615             : 
     616           2 : GDALColorTable *HDF4ImageRasterBand::GetColorTable()
     617             : {
     618           2 :     HDF4ImageDataset *poGDS = reinterpret_cast<HDF4ImageDataset *>(poDS);
     619             : 
     620           2 :     return poGDS->poColorTable;
     621             : }
     622             : 
     623             : /************************************************************************/
     624             : /*                       GetColorInterpretation()                       */
     625             : /************************************************************************/
     626             : 
     627          18 : GDALColorInterp HDF4ImageRasterBand::GetColorInterpretation()
     628             : {
     629          18 :     HDF4ImageDataset *poGDS = reinterpret_cast<HDF4ImageDataset *>(poDS);
     630             : 
     631          18 :     if (poGDS->iDatasetType == HDF4_SDS)
     632             :     {
     633          18 :         return GCI_GrayIndex;
     634             :     }
     635           0 :     else if (poGDS->iDatasetType == HDF4_GR)
     636             :     {
     637           0 :         if (poGDS->poColorTable != nullptr)
     638             :         {
     639           0 :             return GCI_PaletteIndex;
     640             :         }
     641           0 :         else if (poGDS->nBands != 1)
     642             :         {
     643           0 :             if (nBand == 1)
     644           0 :                 return GCI_RedBand;
     645           0 :             else if (nBand == 2)
     646           0 :                 return GCI_GreenBand;
     647           0 :             else if (nBand == 3)
     648           0 :                 return GCI_BlueBand;
     649           0 :             else if (nBand == 4)
     650           0 :                 return GCI_AlphaBand;
     651             :             else
     652           0 :                 return GCI_Undefined;
     653             :         }
     654             :         else
     655             :         {
     656           0 :             return GCI_GrayIndex;
     657             :         }
     658             :     }
     659             : 
     660           0 :     return GCI_GrayIndex;
     661             : }
     662             : 
     663             : /************************************************************************/
     664             : /*                           GetNoDataValue()                           */
     665             : /************************************************************************/
     666             : 
     667         162 : double HDF4ImageRasterBand::GetNoDataValue(int *pbSuccess)
     668             : 
     669             : {
     670         162 :     if (pbSuccess)
     671         162 :         *pbSuccess = bNoDataSet;
     672             : 
     673         162 :     return dfNoDataValue;
     674             : }
     675             : 
     676             : /************************************************************************/
     677             : /*                           SetNoDataValue()                           */
     678             : /************************************************************************/
     679             : 
     680          54 : CPLErr HDF4ImageRasterBand::SetNoDataValue(double dfNoData)
     681             : 
     682             : {
     683          54 :     bNoDataSet = true;
     684          54 :     dfNoDataValue = dfNoData;
     685             : 
     686          54 :     return CE_None;
     687             : }
     688             : 
     689             : /************************************************************************/
     690             : /*                            GetUnitType()                             */
     691             : /************************************************************************/
     692             : 
     693           0 : const char *HDF4ImageRasterBand::GetUnitType()
     694             : 
     695             : {
     696           0 :     if (!osUnitType.empty())
     697           0 :         return osUnitType;
     698             : 
     699           0 :     return GDALRasterBand::GetUnitType();
     700             : }
     701             : 
     702             : /************************************************************************/
     703             : /*                             GetOffset()                              */
     704             : /************************************************************************/
     705             : 
     706           0 : double HDF4ImageRasterBand::GetOffset(int *pbSuccess)
     707             : 
     708             : {
     709           0 :     if (bHaveOffset)
     710             :     {
     711           0 :         if (pbSuccess != nullptr)
     712           0 :             *pbSuccess = TRUE;
     713           0 :         return dfOffset;
     714             :     }
     715             : 
     716           0 :     return GDALRasterBand::GetOffset(pbSuccess);
     717             : }
     718             : 
     719             : /************************************************************************/
     720             : /*                              GetScale()                              */
     721             : /************************************************************************/
     722             : 
     723           0 : double HDF4ImageRasterBand::GetScale(int *pbSuccess)
     724             : 
     725             : {
     726           0 :     if (bHaveScale)
     727             :     {
     728           0 :         if (pbSuccess != nullptr)
     729           0 :             *pbSuccess = TRUE;
     730           0 :         return dfScale;
     731             :     }
     732             : 
     733           0 :     return GDALRasterBand::GetScale(pbSuccess);
     734             : }
     735             : 
     736             : /************************************************************************/
     737             : /* ==================================================================== */
     738             : /*                              HDF4ImageDataset                        */
     739             : /* ==================================================================== */
     740             : /************************************************************************/
     741             : 
     742             : /************************************************************************/
     743             : /*                           HDF4ImageDataset()                         */
     744             : /************************************************************************/
     745             : 
     746         473 : HDF4ImageDataset::HDF4ImageDataset()
     747             :     : pszFilename(nullptr), hHDF4(0), iGR(0), iPal(0), iDataset(0), iRank(0),
     748             :       iNumType(0), nAttrs(0), iInterlaceMode(0), iPalInterlaceMode(0),
     749             :       iPalDataType(0), nComps(0), nPalEntries(0), iXDim(0), iYDim(0),
     750             :       iBandDim(-1), i4Dim(0), nBandCount(0), pszSubdatasetName(nullptr),
     751             :       pszFieldName(nullptr), poColorTable(nullptr), bHasGeoTransform(false),
     752             :       iDatasetType(HDF4_UNKNOWN), iSDS(FAIL), nBlockPreferredXSize(-1),
     753         473 :       nBlockPreferredYSize(-1), bReadTile(false)
     754             : {
     755         473 :     m_oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     756         473 :     m_oGCPSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
     757         473 :     memset(aiDimSizes, 0, sizeof(aiDimSizes));
     758         473 :     papszLocalMetadata = nullptr;
     759         473 :     memset(aiPaletteData, 0, sizeof(aiPaletteData));
     760         473 :     memset(szName, 0, sizeof(szName));
     761         473 :     adfGeoTransform[0] = 0.0;
     762         473 :     adfGeoTransform[1] = 1.0;
     763         473 :     adfGeoTransform[2] = 0.0;
     764         473 :     adfGeoTransform[3] = 0.0;
     765         473 :     adfGeoTransform[4] = 0.0;
     766         473 :     adfGeoTransform[5] = 1.0;
     767         473 : }
     768             : 
     769             : /************************************************************************/
     770             : /*                            ~HDF4ImageDataset()                       */
     771             : /************************************************************************/
     772             : 
     773         946 : HDF4ImageDataset::~HDF4ImageDataset()
     774             : {
     775         946 :     CPLMutexHolderD(&hHDF4Mutex);
     776             : 
     777         473 :     HDF4ImageDataset::FlushCache(true);
     778             : 
     779         473 :     CPLFree(pszFilename);
     780         473 :     if (iSDS != FAIL)
     781          52 :         SDendaccess(iSDS);
     782         473 :     if (hSD > 0)
     783         471 :         SDend(hSD);
     784         473 :     hSD = 0;
     785         473 :     if (iGR > 0)
     786           2 :         GRendaccess(iGR);
     787         473 :     if (hGR > 0)
     788           2 :         GRend(hGR);
     789         473 :     hGR = 0;
     790         473 :     CPLFree(pszSubdatasetName);
     791         473 :     CPLFree(pszFieldName);
     792         473 :     if (papszLocalMetadata)
     793         299 :         CSLDestroy(papszLocalMetadata);
     794         473 :     if (poColorTable != nullptr)
     795           1 :         delete poColorTable;
     796             : 
     797         473 :     if (hHDF4 > 0)
     798             :     {
     799         301 :         switch (iDatasetType)
     800             :         {
     801           0 :             case HDF4_EOS:
     802           0 :                 switch (iSubdatasetType)
     803             :                 {
     804           0 :                     case H4ST_EOS_SWATH:
     805             :                     case H4ST_EOS_SWATH_GEOL:
     806           0 :                         SWclose(hHDF4);
     807           0 :                         break;
     808           0 :                     case H4ST_EOS_GRID:
     809           0 :                         GDclose(hHDF4);
     810           0 :                         break;
     811           0 :                     default:
     812           0 :                         break;
     813             :                 }
     814           0 :                 break;
     815         301 :             case HDF4_SDS:
     816             :             case HDF4_GR:
     817         301 :                 hHDF4 = Hclose(hHDF4);
     818         301 :                 break;
     819           0 :             default:
     820           0 :                 break;
     821             :         }
     822             :     }
     823         946 : }
     824             : 
     825             : /************************************************************************/
     826             : /*                          GetGeoTransform()                           */
     827             : /************************************************************************/
     828             : 
     829          45 : CPLErr HDF4ImageDataset::GetGeoTransform(double *padfTransform)
     830             : {
     831          45 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
     832             : 
     833          45 :     if (!bHasGeoTransform)
     834           0 :         return CE_Failure;
     835             : 
     836          45 :     return CE_None;
     837             : }
     838             : 
     839             : /************************************************************************/
     840             : /*                          SetGeoTransform()                           */
     841             : /************************************************************************/
     842             : 
     843          96 : CPLErr HDF4ImageDataset::SetGeoTransform(double *padfTransform)
     844             : {
     845          96 :     bHasGeoTransform = true;
     846          96 :     memcpy(adfGeoTransform, padfTransform, sizeof(double) * 6);
     847             : 
     848          96 :     return CE_None;
     849             : }
     850             : 
     851             : /************************************************************************/
     852             : /*                          GetSpatialRef()                             */
     853             : /************************************************************************/
     854             : 
     855          18 : const OGRSpatialReference *HDF4ImageDataset::GetSpatialRef() const
     856             : 
     857             : {
     858          18 :     return m_oSRS.IsEmpty() ? nullptr : &m_oSRS;
     859             : }
     860             : 
     861             : /************************************************************************/
     862             : /*                          SetSpatialRef()                             */
     863             : /************************************************************************/
     864             : 
     865          78 : CPLErr HDF4ImageDataset::SetSpatialRef(const OGRSpatialReference *poSRS)
     866             : 
     867             : {
     868          78 :     m_oSRS.Clear();
     869          78 :     if (poSRS)
     870          78 :         m_oSRS = *poSRS;
     871             : 
     872          78 :     return CE_None;
     873             : }
     874             : 
     875             : /************************************************************************/
     876             : /*                            GetGCPCount()                             */
     877             : /************************************************************************/
     878             : 
     879           0 : int HDF4ImageDataset::GetGCPCount()
     880             : 
     881             : {
     882           0 :     return static_cast<int>(m_aoGCPs.size());
     883             : }
     884             : 
     885             : /************************************************************************/
     886             : /*                          GetGCPSpatialRef()                          */
     887             : /************************************************************************/
     888             : 
     889           0 : const OGRSpatialReference *HDF4ImageDataset::GetGCPSpatialRef() const
     890             : 
     891             : {
     892           0 :     return m_oSRS.IsEmpty() || m_aoGCPs.empty() ? nullptr : &m_oGCPSRS;
     893             : }
     894             : 
     895             : /************************************************************************/
     896             : /*                               GetGCPs()                              */
     897             : /************************************************************************/
     898             : 
     899           0 : const GDAL_GCP *HDF4ImageDataset::GetGCPs()
     900             : {
     901           0 :     return gdal::GCP::c_ptr(m_aoGCPs);
     902             : }
     903             : 
     904             : /************************************************************************/
     905             : /*                             FlushCache()                             */
     906             : /************************************************************************/
     907             : 
     908         491 : CPLErr HDF4ImageDataset::FlushCache(bool bAtClosing)
     909             : 
     910             : {
     911         982 :     CPLMutexHolderD(&hHDF4Mutex);
     912             : 
     913         491 :     CPLErr eErr = GDALDataset::FlushCache(bAtClosing);
     914             : 
     915         491 :     if (eAccess == GA_ReadOnly)
     916         323 :         return eErr;
     917             : 
     918             :     // Write out transformation matrix.
     919             :     const char *pszValue =
     920         168 :         CPLSPrintf("%f, %f, %f, %f, %f, %f", adfGeoTransform[0],
     921             :                    adfGeoTransform[1], adfGeoTransform[2], adfGeoTransform[3],
     922             :                    adfGeoTransform[4], adfGeoTransform[5]);
     923         336 :     if ((SDsetattr(hSD, "TransformationMatrix", DFNT_CHAR8,
     924         168 :                    static_cast<int>(strlen(pszValue)) + 1, pszValue)) < 0)
     925             :     {
     926           0 :         eErr = CE_Failure;
     927           0 :         CPLDebug("HDF4Image",
     928             :                  "Cannot write transformation matrix to output file");
     929             :     }
     930             : 
     931             :     // Write out projection
     932         168 :     if (!m_oSRS.IsEmpty())
     933             :     {
     934          78 :         char *pszWKT = nullptr;
     935          78 :         m_oSRS.exportToWkt(&pszWKT);
     936          78 :         if (pszWKT)
     937             :         {
     938         156 :             if ((SDsetattr(hSD, "Projection", DFNT_CHAR8,
     939          78 :                            static_cast<int>(strlen(pszWKT)) + 1, pszWKT)) < 0)
     940             :             {
     941           0 :                 eErr = CE_Failure;
     942           0 :                 CPLDebug("HDF4Image",
     943             :                          "Cannot write projection information to output file");
     944             :             }
     945          78 :             CPLFree(pszWKT);
     946             :         }
     947             :     }
     948             : 
     949             :     // Store all metadata from source dataset as HDF attributes
     950         168 :     if (GetMetadata())
     951             :     {
     952          51 :         char **papszMeta = GetMetadata();
     953             : 
     954         102 :         while (*papszMeta)
     955             :         {
     956          51 :             char *pszName = nullptr;
     957          51 :             pszValue = CPLParseNameValue(*papszMeta++, &pszName);
     958          87 :             if (pszName != nullptr &&
     959          36 :                 (SDsetattr(hSD, pszName, DFNT_CHAR8,
     960          36 :                            static_cast<int>(strlen(pszValue)) + 1, pszValue)) <
     961             :                     0)
     962             :             {
     963           0 :                 eErr = CE_Failure;
     964           0 :                 CPLDebug("HDF4Image",
     965             :                          "Cannot write metadata information to output file");
     966             :             }
     967             : 
     968          51 :             CPLFree(pszName);
     969             :         }
     970             :     }
     971             : 
     972             :     // Write out NoData values
     973         378 :     for (int iBand = 1; iBand <= nBands; iBand++)
     974             :     {
     975             :         HDF4ImageRasterBand *poBand =
     976         210 :             reinterpret_cast<HDF4ImageRasterBand *>(GetRasterBand(iBand));
     977             : 
     978         210 :         if (poBand->bNoDataSet)
     979             :         {
     980          18 :             char *pszName = CPLStrdup(CPLSPrintf("NoDataValue%d", iBand));
     981          18 :             pszValue = CPLSPrintf("%f", poBand->dfNoDataValue);
     982          36 :             if ((SDsetattr(hSD, pszName, DFNT_CHAR8,
     983          18 :                            static_cast<int>(strlen(pszValue)) + 1, pszValue)) <
     984             :                 0)
     985             :             {
     986           0 :                 eErr = CE_Failure;
     987           0 :                 CPLDebug("HDF4Image",
     988             :                          "Cannot write NoData value for band %d "
     989             :                          "to output file",
     990             :                          iBand);
     991             :             }
     992             : 
     993          18 :             CPLFree(pszName);
     994             :         }
     995             :     }
     996             : 
     997             :     // Write out band descriptions
     998         378 :     for (int iBand = 1; iBand <= nBands; iBand++)
     999             :     {
    1000             :         HDF4ImageRasterBand *poBand =
    1001         210 :             reinterpret_cast<HDF4ImageRasterBand *>(GetRasterBand(iBand));
    1002             : 
    1003         210 :         char *pszName = CPLStrdup(CPLSPrintf("BandDesc%d", iBand));
    1004         210 :         pszValue = poBand->GetDescription();
    1005         210 :         if (pszValue != nullptr && !EQUAL(pszValue, ""))
    1006             :         {
    1007          36 :             if (SDsetattr(hSD, pszName, DFNT_CHAR8,
    1008          18 :                           static_cast<int>(strlen(pszValue)) + 1, pszValue) < 0)
    1009             :             {
    1010           0 :                 eErr = CE_Failure;
    1011           0 :                 CPLDebug("HDF4Image",
    1012             :                          "Cannot write band's %d description to output file",
    1013             :                          iBand);
    1014             :             }
    1015             :         }
    1016             : 
    1017         210 :         CPLFree(pszName);
    1018             :     }
    1019         168 :     return eErr;
    1020             : }
    1021             : 
    1022             : /************************************************************************/
    1023             : /*                        USGSMnemonicToCode()                          */
    1024             : /************************************************************************/
    1025             : 
    1026           0 : long HDF4ImageDataset::USGSMnemonicToCode(const char *pszMnemonic)
    1027             : {
    1028           0 :     if (EQUAL(pszMnemonic, "UTM"))
    1029           0 :         return 1L;
    1030           0 :     else if (EQUAL(pszMnemonic, "LAMCC"))
    1031           0 :         return 4L;
    1032           0 :     else if (EQUAL(pszMnemonic, "PS"))
    1033           0 :         return 6L;
    1034           0 :     else if (EQUAL(pszMnemonic, "PC"))
    1035           0 :         return 7L;
    1036           0 :     else if (EQUAL(pszMnemonic, "TM"))
    1037           0 :         return 9L;
    1038           0 :     else if (EQUAL(pszMnemonic, "EQRECT"))
    1039           0 :         return 17L;
    1040           0 :     else if (EQUAL(pszMnemonic, "OM"))
    1041           0 :         return 20L;
    1042           0 :     else if (EQUAL(pszMnemonic, "SOM"))
    1043           0 :         return 22L;
    1044             :     else
    1045           0 :         return 1L;  // UTM by default
    1046             : }
    1047             : 
    1048             : /************************************************************************/
    1049             : /*                              ToGeoref()                              */
    1050             : /************************************************************************/
    1051             : 
    1052           0 : void HDF4ImageDataset::ToGeoref(double *pdfGeoX, double *pdfGeoY)
    1053             : {
    1054           0 :     OGRSpatialReference *poLatLong = m_oSRS.CloneGeogCS();
    1055           0 :     OGRCoordinateTransformation *poTransform = nullptr;
    1056           0 :     if (poLatLong)
    1057             :     {
    1058           0 :         poLatLong->SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1059           0 :         poTransform = OGRCreateCoordinateTransformation(poLatLong, &m_oSRS);
    1060             :     }
    1061             : 
    1062           0 :     if (poTransform != nullptr)
    1063           0 :         poTransform->Transform(1, pdfGeoX, pdfGeoY, nullptr);
    1064             : 
    1065           0 :     if (poTransform != nullptr)
    1066           0 :         delete poTransform;
    1067             : 
    1068           0 :     if (poLatLong != nullptr)
    1069           0 :         delete poLatLong;
    1070           0 : }
    1071             : 
    1072             : /************************************************************************/
    1073             : /*                            ReadCoordinates()                         */
    1074             : /************************************************************************/
    1075             : 
    1076           0 : void HDF4ImageDataset::ReadCoordinates(const char *pszString,
    1077             :                                        double *pdfCenterY, double *pdfCenterX)
    1078             : {
    1079           0 :     char **papszStrList = CSLTokenizeString2(pszString, ", ", 0);
    1080           0 :     *pdfCenterY = CPLAtof(papszStrList[0]); /* lat */
    1081           0 :     *pdfCenterX = CPLAtof(papszStrList[1]); /* lon */
    1082           0 :     CSLDestroy(papszStrList);
    1083           0 : }
    1084             : 
    1085             : /************************************************************************/
    1086             : /*                         CaptureL1GMTLInfo()                          */
    1087             : /************************************************************************/
    1088             : 
    1089             : /*  FILE L71002025_02520010722_M_MTL.L1G
    1090             : 
    1091             : GROUP = L1_METADATA_FILE
    1092             :   ...
    1093             :   GROUP = PRODUCT_METADATA
    1094             :     PRODUCT_TYPE = "L1G"
    1095             :     PROCESSING_SOFTWARE = "IAS_5.1"
    1096             :     EPHEMERIS_TYPE = "DEFINITIVE"
    1097             :     SPACECRAFT_ID = "Landsat7"
    1098             :     SENSOR_ID = "ETM+"
    1099             :     ACQUISITION_DATE = 2001-07-22
    1100             :     WRS_PATH = 002
    1101             :     STARTING_ROW = 025
    1102             :     ENDING_ROW = 025
    1103             :     BAND_COMBINATION = "12345--7-"
    1104             :     PRODUCT_UL_CORNER_LAT = 51.2704805
    1105             :     PRODUCT_UL_CORNER_LON = -53.8914311
    1106             :     PRODUCT_UR_CORNER_LAT = 50.8458100
    1107             :     PRODUCT_UR_CORNER_LON = -50.9869091
    1108             :     PRODUCT_LL_CORNER_LAT = 49.6960897
    1109             :     PRODUCT_LL_CORNER_LON = -54.4047933
    1110             :     PRODUCT_LR_CORNER_LAT = 49.2841436
    1111             :     PRODUCT_LR_CORNER_LON = -51.5900428
    1112             :     PRODUCT_UL_CORNER_MAPX = 298309.894
    1113             :     PRODUCT_UL_CORNER_MAPY = 5683875.631
    1114             :     PRODUCT_UR_CORNER_MAPX = 500921.624
    1115             :     PRODUCT_UR_CORNER_MAPY = 5632678.683
    1116             :     PRODUCT_LL_CORNER_MAPX = 254477.193
    1117             :     PRODUCT_LL_CORNER_MAPY = 5510407.880
    1118             :     PRODUCT_LR_CORNER_MAPX = 457088.923
    1119             :     PRODUCT_LR_CORNER_MAPY = 5459210.932
    1120             :     PRODUCT_SAMPLES_REF = 6967
    1121             :     PRODUCT_LINES_REF = 5965
    1122             :     BAND1_FILE_NAME = "L71002025_02520010722_B10.L1G"
    1123             :     BAND2_FILE_NAME = "L71002025_02520010722_B20.L1G"
    1124             :     BAND3_FILE_NAME = "L71002025_02520010722_B30.L1G"
    1125             :     BAND4_FILE_NAME = "L71002025_02520010722_B40.L1G"
    1126             :     BAND5_FILE_NAME = "L71002025_02520010722_B50.L1G"
    1127             :     BAND7_FILE_NAME = "L72002025_02520010722_B70.L1G"
    1128             :     METADATA_L1_FILE_NAME = "L71002025_02520010722_MTL.L1G"
    1129             :     CPF_FILE_NAME = "L7CPF20010701_20010930_06"
    1130             :     HDF_DIR_FILE_NAME = "L71002025_02520010722_HDF.L1G"
    1131             :   END_GROUP = PRODUCT_METADATA
    1132             :   ...
    1133             :   GROUP = PROJECTION_PARAMETERS
    1134             :     REFERENCE_DATUM = "NAD83"
    1135             :     REFERENCE_ELLIPSOID = "GRS80"
    1136             :     GRID_CELL_SIZE_PAN = 15.000000
    1137             :     GRID_CELL_SIZE_THM = 60.000000
    1138             :     GRID_CELL_SIZE_REF = 30.000000
    1139             :     ORIENTATION = "NOM"
    1140             :     RESAMPLING_OPTION = "CC"
    1141             :     MAP_PROJECTION = "UTM"
    1142             :   END_GROUP = PROJECTION_PARAMETERS
    1143             :   GROUP = UTM_PARAMETERS
    1144             :     ZONE_NUMBER = 22
    1145             :   END_GROUP = UTM_PARAMETERS
    1146             : END_GROUP = L1_METADATA_FILE
    1147             : END
    1148             : */
    1149             : 
    1150         299 : void HDF4ImageDataset::CaptureL1GMTLInfo()
    1151             : 
    1152             : {
    1153             :     /* -------------------------------------------------------------------- */
    1154             :     /*      Does the physical file look like it matches our expected        */
    1155             :     /*      name pattern?                                                   */
    1156             :     /* -------------------------------------------------------------------- */
    1157         299 :     if (strlen(pszFilename) < 8 ||
    1158         299 :         !EQUAL(pszFilename + strlen(pszFilename) - 8, "_HDF.L1G"))
    1159         299 :         return;
    1160             : 
    1161             :     /* -------------------------------------------------------------------- */
    1162             :     /*      Construct the name of the corresponding MTL file.  We should    */
    1163             :     /*      likely be able to extract that from the HDF itself but I'm      */
    1164             :     /*      not sure where to find it.                                      */
    1165             :     /* -------------------------------------------------------------------- */
    1166           0 :     CPLString osMTLFilename = pszFilename;
    1167           0 :     osMTLFilename.resize(osMTLFilename.length() - 8);
    1168           0 :     osMTLFilename += "_MTL.L1G";
    1169             : 
    1170             :     /* -------------------------------------------------------------------- */
    1171             :     /*      Ingest the MTL using the NASAKeywordHandler written for the     */
    1172             :     /*      PDS driver.                                                     */
    1173             :     /* -------------------------------------------------------------------- */
    1174           0 :     VSILFILE *fp = VSIFOpenL(osMTLFilename, "r");
    1175           0 :     if (fp == nullptr)
    1176           0 :         return;
    1177             : 
    1178           0 :     NASAKeywordHandler oMTL;
    1179           0 :     if (!oMTL.Ingest(fp, 0))
    1180             :     {
    1181           0 :         VSIFCloseL(fp);
    1182           0 :         return;
    1183             :     }
    1184             : 
    1185           0 :     VSIFCloseL(fp);
    1186             : 
    1187             :     /* -------------------------------------------------------------------- */
    1188             :     /*  Note: Different variation of MTL files use different group names.   */
    1189             :     /*            Check for LPGS_METADATA_FILE and L1_METADATA_FILE.        */
    1190             :     /* -------------------------------------------------------------------- */
    1191           0 :     CPLString osPrefix;
    1192             : 
    1193           0 :     if (oMTL.GetKeyword("LPGS_METADATA_FILE.PRODUCT_METADATA"
    1194             :                         ".PRODUCT_UL_CORNER_LON",
    1195           0 :                         nullptr))
    1196           0 :         osPrefix = "LPGS_METADATA_FILE.PRODUCT_METADATA.PRODUCT_";
    1197           0 :     else if (oMTL.GetKeyword("L1_METADATA_FILE.PRODUCT_METADATA"
    1198             :                              ".PRODUCT_UL_CORNER_LON",
    1199           0 :                              nullptr))
    1200           0 :         osPrefix = "L1_METADATA_FILE.PRODUCT_METADATA.PRODUCT_";
    1201             :     else
    1202           0 :         return;
    1203             : 
    1204             :     const double dfULX =
    1205           0 :         CPLAtof(oMTL.GetKeyword((osPrefix + "UL_CORNER_LON").c_str(), "0"));
    1206             :     const double dfULY =
    1207           0 :         CPLAtof(oMTL.GetKeyword((osPrefix + "UL_CORNER_LAT").c_str(), "0"));
    1208             :     const double dfLRX =
    1209           0 :         CPLAtof(oMTL.GetKeyword((osPrefix + "LR_CORNER_LON").c_str(), "0"));
    1210             :     const double dfLRY =
    1211           0 :         CPLAtof(oMTL.GetKeyword((osPrefix + "LR_CORNER_LAT").c_str(), "0"));
    1212             :     const double dfLLX =
    1213           0 :         CPLAtof(oMTL.GetKeyword((osPrefix + "LL_CORNER_LON").c_str(), "0"));
    1214             :     const double dfLLY =
    1215           0 :         CPLAtof(oMTL.GetKeyword((osPrefix + "LL_CORNER_LAT").c_str(), "0"));
    1216             :     const double dfURX =
    1217           0 :         CPLAtof(oMTL.GetKeyword((osPrefix + "UR_CORNER_LON").c_str(), "0"));
    1218             :     const double dfURY =
    1219           0 :         CPLAtof(oMTL.GetKeyword((osPrefix + "UR_CORNER_LAT").c_str(), "0"));
    1220             : 
    1221           0 :     m_oGCPSRS.importFromWkt(
    1222             :         "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\",6378137,"
    1223             :         "298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],TOWGS84[0,0,0,0,0,0,0],"
    1224             :         "AUTHORITY[\"EPSG\",\"6326\"]],PRIMEM[\"Greenwich\",0,"
    1225             :         "AUTHORITY[\"EPSG\",\"8901\"]],UNIT[\"degree\",0.0174532925199433,"
    1226             :         "AUTHORITY[\"EPSG\",\"9108\"]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST]"
    1227             :         ",AUTHORITY[\"EPSG\",\"4326\"]]");
    1228             : 
    1229           0 :     m_aoGCPs.emplace_back(nullptr, nullptr, 0.0, 0.0, dfULX, dfULY);
    1230           0 :     m_aoGCPs.emplace_back(nullptr, nullptr, GetRasterXSize(), 0.0, dfURX,
    1231           0 :                           dfURY);
    1232           0 :     m_aoGCPs.emplace_back(nullptr, nullptr, 0.0, GetRasterYSize(), dfLLX,
    1233           0 :                           dfLLY);
    1234           0 :     m_aoGCPs.emplace_back(nullptr, nullptr, GetRasterXSize(), GetRasterYSize(),
    1235           0 :                           dfLRX, dfLRY);
    1236             : }
    1237             : 
    1238             : /************************************************************************/
    1239             : /*                       CaptureNRLGeoTransform()                       */
    1240             : /*                                                                      */
    1241             : /*      Capture geotransform and coordinate system from NRL (Naval      */
    1242             : /*      Research Laboratory, Stennis Space Center) metadata.            */
    1243             : /************************************************************************/
    1244             : 
    1245             : /* Example metadata:
    1246             : Metadata:
    1247             :   createTime=Fri Oct  1 18:00:07 2004
    1248             :   createSoftware=APS v2.8.4
    1249             :   createPlatform=i686-pc-linux-gnu
    1250             :   createAgency=Naval Research Laboratory, Stennis Space Center
    1251             :   sensor=MODIS
    1252             :   sensorPlatform=TERRA-AM
    1253             :   sensorAgency=NASA
    1254             :   sensorType=whiskbroom
    1255             :   sensorSpectrum=Visible/Thermal
    1256             :   sensorNumberOfBands=36
    1257             :   sensorBandUnits=nano meters
    1258             :   sensorBands=645, 858.5, 469, 555, 1240, 1640, 2130, 412.5, 443, 488, 531, 551,
    1259             :  667, 678, 748, 869.5, 905, 936, 940, 3750, 3959, 3959, 4050, 4465.5, 4515.5, 13
    1260             : 75, 6715, 7325, 8550, 9730, 11130, 12020, 13335, 13635, 13935, 14235
    1261             :   sensorBandWidths=50, 35, 20, 20, 20, 24, 50, 15, 10, 10, 10, 10, 10, 10, 10, 1
    1262             : 5, 30, 10, 50, 90, 60, 60, 60, 65, 67, 30, 360, 300, 300, 300, 500, 500, 300, 30
    1263             : 0, 300, 300
    1264             :   sensorNominalAltitudeInKM=705
    1265             :   sensorScanWidthInKM=2330
    1266             :   sensorResolutionInKM=1
    1267             :   sensorPlatformType=Polar-orbiting Satellite
    1268             :   timeStartYear=2004
    1269             :   timeStartDay=275
    1270             :   timeStartTime=56400000
    1271             :   timeStart=Fri Oct  1 15:40:00 2004
    1272             :   timeDayNight=Day
    1273             :   timeEndYear=2004
    1274             :   timeEndDay=275
    1275             :   timeEndTime=56700000
    1276             :   timeEnd=Fri Oct  1 15:45:00 2004
    1277             :   inputMasks=HIGLINT,CLDICE,LAND,ATMFAIL
    1278             :   inputMasksInt=523
    1279             :   processedVersion=1.2
    1280             :   file=MODAM2004275.L3_Mosaic_NOAA_GMX
    1281             :   fileTitle=NRL Level-3 Mosaic
    1282             :   fileVersion=3.0
    1283             :   fileClassification=UNCLASSIFIED
    1284             :   fileStatus=EXPERIMENTAL
    1285             :   navType=mapped
    1286             :   mapProjectionSystem=NRL(USGS)
    1287             :   mapProjection=Gomex
    1288             :   mapUpperLeft=31, -99
    1289             :   mapUpperRight=31, -79
    1290             :   mapLowerLeft=14.9844128048645, -99
    1291             :   mapLowerRight=14.9844128048645, -79
    1292             :   inputFiles=MODAM2004275154000.L3_NOAA_GMX
    1293             :   ...
    1294             :  */
    1295             : 
    1296           0 : void HDF4ImageDataset::CaptureNRLGeoTransform()
    1297             : 
    1298             : {
    1299             :     /* -------------------------------------------------------------------- */
    1300             :     /*      Collect the four corners.                                       */
    1301             :     /* -------------------------------------------------------------------- */
    1302           0 :     double adfXY[8] = {};
    1303             :     static const char *const apszItems[] = {"mapUpperLeft", "mapUpperRight",
    1304             :                                             "mapLowerLeft", "mapLowerRight"};
    1305           0 :     bool bLLPossible = true;
    1306             : 
    1307           0 :     for (int iCorner = 0; iCorner < 4; iCorner++)
    1308             :     {
    1309             :         const char *pszCornerLoc =
    1310           0 :             CSLFetchNameValue(papszGlobalMetadata, apszItems[iCorner]);
    1311             : 
    1312           0 :         if (pszCornerLoc == nullptr)
    1313           0 :             return;
    1314             : 
    1315             :         char **papszTokens =
    1316           0 :             CSLTokenizeStringComplex(pszCornerLoc, ",", FALSE, FALSE);
    1317           0 :         if (CSLCount(papszTokens) != 2)
    1318             :         {
    1319           0 :             CSLDestroy(papszTokens);
    1320           0 :             return;
    1321             :         }
    1322             : 
    1323           0 :         adfXY[iCorner * 2 + 0] = CPLAtof(papszTokens[1]);
    1324           0 :         adfXY[iCorner * 2 + 1] = CPLAtof(papszTokens[0]);
    1325             : 
    1326           0 :         if (adfXY[iCorner * 2 + 0] < -360 || adfXY[iCorner * 2 + 0] > 360 ||
    1327           0 :             adfXY[iCorner * 2 + 1] < -90 || adfXY[iCorner * 2 + 1] > 90)
    1328           0 :             bLLPossible = false;
    1329             : 
    1330           0 :         CSLDestroy(papszTokens);
    1331             :     }
    1332             : 
    1333             :     /* -------------------------------------------------------------------- */
    1334             :     /*      Does this look like nice clean "northup" lat/long data?         */
    1335             :     /* -------------------------------------------------------------------- */
    1336           0 :     if (adfXY[0 * 2 + 0] == adfXY[2 * 2 + 0] &&
    1337           0 :         adfXY[0 * 2 + 1] == adfXY[1 * 2 + 1] && bLLPossible)
    1338             :     {
    1339           0 :         bHasGeoTransform = true;
    1340           0 :         adfGeoTransform[0] = adfXY[0 * 2 + 0];
    1341           0 :         adfGeoTransform[1] =
    1342           0 :             (adfXY[1 * 2 + 0] - adfXY[0 * 2 + 0]) / nRasterXSize;
    1343           0 :         adfGeoTransform[2] = 0.0;
    1344           0 :         adfGeoTransform[3] = adfXY[0 * 2 + 1];
    1345           0 :         adfGeoTransform[4] = 0.0;
    1346           0 :         adfGeoTransform[5] =
    1347           0 :             (adfXY[2 * 2 + 1] - adfXY[0 * 2 + 1]) / nRasterYSize;
    1348             : 
    1349           0 :         m_oSRS.SetWellKnownGeogCS("WGS84");
    1350             :     }
    1351             : 
    1352             :     /* -------------------------------------------------------------------- */
    1353             :     /*      Can we find the USGS Projection Parameters?                     */
    1354             :     /* -------------------------------------------------------------------- */
    1355           0 :     bool bGotGCTPProjection = false;
    1356           0 :     int l_iSDSIndex = FAIL;
    1357           0 :     int l_iSDS = FAIL;
    1358             :     const char *mapProjection =
    1359           0 :         CSLFetchNameValue(papszGlobalMetadata, "mapProjection");
    1360             : 
    1361           0 :     if (mapProjection)
    1362           0 :         l_iSDSIndex = SDnametoindex(hSD, mapProjection);
    1363             : 
    1364           0 :     if (l_iSDSIndex != FAIL)
    1365           0 :         l_iSDS = SDselect(hSD, l_iSDSIndex);
    1366             : 
    1367           0 :     if (l_iSDS != FAIL)
    1368             :     {
    1369           0 :         char l_szName[HDF4_SDS_MAXNAMELEN] = {};
    1370           0 :         int32 l_iRank = 0;
    1371           0 :         int32 l_iNumType = 0;
    1372           0 :         int32 l_nAttrs = 0;
    1373           0 :         int32 l_aiDimSizes[H4_MAX_VAR_DIMS] = {};
    1374             : 
    1375           0 :         double adfGCTP[29] = {};
    1376           0 :         int32 aiStart[H4_MAX_NC_DIMS] = {};
    1377           0 :         int32 aiEdges[H4_MAX_NC_DIMS] = {};
    1378             : 
    1379           0 :         aiStart[0] = 0;
    1380           0 :         aiEdges[0] = 29;
    1381             : 
    1382           0 :         if (SDgetinfo(l_iSDS, l_szName, &l_iRank, l_aiDimSizes, &l_iNumType,
    1383           0 :                       &l_nAttrs) == 0 &&
    1384           0 :             l_iNumType == DFNT_FLOAT64 && l_iRank == 1 &&
    1385           0 :             l_aiDimSizes[0] >= 29 &&
    1386           0 :             SDreaddata(l_iSDS, aiStart, nullptr, aiEdges, adfGCTP) == 0 &&
    1387           0 :             m_oSRS.importFromUSGS(static_cast<long>(adfGCTP[1]),
    1388           0 :                                   static_cast<long>(adfGCTP[2]), adfGCTP + 4,
    1389           0 :                                   static_cast<long>(adfGCTP[3])) == OGRERR_NONE)
    1390             :         {
    1391           0 :             CPLDebug("HDF4Image",
    1392             :                      "GCTP Params = %g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,"
    1393             :                      "%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g,%g",
    1394             :                      adfGCTP[0], adfGCTP[1], adfGCTP[2], adfGCTP[3], adfGCTP[4],
    1395             :                      adfGCTP[5], adfGCTP[6], adfGCTP[7], adfGCTP[8], adfGCTP[9],
    1396             :                      adfGCTP[10], adfGCTP[11], adfGCTP[12], adfGCTP[13],
    1397             :                      adfGCTP[14], adfGCTP[15], adfGCTP[16], adfGCTP[17],
    1398             :                      adfGCTP[18], adfGCTP[19], adfGCTP[20], adfGCTP[21],
    1399             :                      adfGCTP[22], adfGCTP[23], adfGCTP[24], adfGCTP[25],
    1400             :                      adfGCTP[26], adfGCTP[27], adfGCTP[28]);
    1401             : 
    1402           0 :             bGotGCTPProjection = true;
    1403             :         }
    1404             : 
    1405           0 :         SDendaccess(l_iSDS);
    1406             :     }
    1407             : 
    1408             :     /* -------------------------------------------------------------------- */
    1409             :     /*      If we derived a GCTP based projection, then we need to          */
    1410             :     /*      transform the lat/long corners into this projection and use     */
    1411             :     /*      them to establish the geotransform.                             */
    1412             :     /* -------------------------------------------------------------------- */
    1413           0 :     if (bLLPossible && bGotGCTPProjection)
    1414             :     {
    1415           0 :         OGRSpatialReference oWGS84;
    1416             : 
    1417           0 :         oWGS84.SetWellKnownGeogCS("WGS84");
    1418           0 :         oWGS84.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    1419             : 
    1420             :         OGRCoordinateTransformation *poCT =
    1421           0 :             OGRCreateCoordinateTransformation(&oWGS84, &m_oSRS);
    1422             : 
    1423           0 :         double dfULX = adfXY[0 * 2 + 0];
    1424           0 :         double dfULY = adfXY[0 * 2 + 1];
    1425             : 
    1426           0 :         double dfLRX = adfXY[3 * 2 + 0];
    1427           0 :         double dfLRY = adfXY[3 * 2 + 1];
    1428             : 
    1429           0 :         if (poCT->Transform(1, &dfULX, &dfULY) &&
    1430           0 :             poCT->Transform(1, &dfLRX, &dfLRY))
    1431             :         {
    1432           0 :             bHasGeoTransform = true;
    1433           0 :             adfGeoTransform[0] = dfULX;
    1434           0 :             adfGeoTransform[1] = (dfLRX - dfULX) / nRasterXSize;
    1435           0 :             adfGeoTransform[2] = 0.0;
    1436           0 :             adfGeoTransform[3] = dfULY;
    1437           0 :             adfGeoTransform[4] = 0.0;
    1438           0 :             adfGeoTransform[5] = (dfLRY - dfULY) / nRasterYSize;
    1439             :         }
    1440             : 
    1441           0 :         delete poCT;
    1442             :     }
    1443             : }
    1444             : 
    1445             : /************************************************************************/
    1446             : /*                     CaptureCoastwatchGCTPInfo()                      */
    1447             : /************************************************************************/
    1448             : 
    1449             : /* Example Metadata from:
    1450             : 
    1451             :   http://coastwatch.noaa.gov/interface/most_recent.php?sensor=MODIS&product=chlorNASA
    1452             : 
    1453             : Definitions at:
    1454             :   http://coastwatch.noaa.gov/cw_form_hdf.html
    1455             : 
    1456             : Metadata:
    1457             :   satellite=Aqua
    1458             :   sensor=MODIS
    1459             :   origin=USDOC/NOAA/NESDIS CoastWatch
    1460             :   history=PGE01:4.1.12;PGE02:4.3.1.12;SeaDAS Version ?.?, MSl12 4.0.2,
    1461             : Linux 2.4.21-27.0.1.EL cwregister GulfOfMexicoSinusoidal.hdf
    1462             : MODSCW.P2005023.1835.swath09.hdf MODSCW.P2005023.1835.GM16.mapped09.hdf
    1463             : cwgraphics MODSCW.P2005023.1835.GM16.closest.hdf
    1464             : cwmath --template chlor_a --expr
    1465             : chlor_a=select(and(l2_flags,514)!=0,nan,chlor_a)
    1466             : /data/aps/browse/lvl3/seadas/coastwatch/hdf/MODSCW_P2005023_1835_GM16_closest.hdf
    1467             : /data/aps/browse/lvl3/seadas/coastwatch/maskhdf/MODSCW_P2005023_1835_GM16_closest_chlora.hdf
    1468             : cwmath --template latitude --expr latitude=latitude
    1469             : /data/aps/browse/lvl3/seadas/coastwatch/hdf/MODSCW_P2005023_1835_GM16_closest.hdf
    1470             : /data/aps/browse/lvl3/seadas/coastwatch/maskhdf/MODSCW_P2005023_1835_GM16_closest_chlora.hdf
    1471             : cwmath --template longitude --expr longitude=longitude
    1472             : /data/aps/browse/lvl3/seadas/coastwatch/hdf/MODSCW_P2005023_1835_GM16_closest.hdf
    1473             : /data/aps/browse/lvl3/seadas/coastwatch/maskhdf/MODSCW_P2005023_1835_GM16_closest_chlora.hdf
    1474             :   cwhdf_version=3.2
    1475             :   pass_type=day
    1476             :   pass_date=12806
    1477             :   start_time=66906
    1478             :   temporal_extent=298
    1479             :   projection_type=mapped
    1480             :   projection=Sinusoidal
    1481             :   gctp_sys=16
    1482             :   gctp_zone=62
    1483             :   gctp_parm=6378137, 0, 0, 0, -89000000, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
    1484             :   gctp_datum=12
    1485             :   et_affine=0, -1008.74836097881, 1008.74836097881, 0, -953126.102425113,
    1486             : 3447041.10282512 rows=1540 cols=2000 polygon_latitude=31, 31, 31, 31,
    1487             : 31, 27.5095879249529, 24.0191758499058, 20.5287637748587, 17.0383516998116, 17.0383516998116,
    1488             : 17.0383516998116, 17.0383516998116, 17.0383516998116, 20.5287637748587, 24.0191758499058,
    1489             : 27.5095879249529, 31 polygon_longitude=-99, -93.7108573344442,
    1490             : -88.4217146688883, -83.1325720033325, -77.8434293377767, -78.217853417453,
    1491             : -78.5303805448579, -78.7884829057512, -78.9979508907244, -83.7397542896832,
    1492             : -88.481557688642, -93.2233610876007, -97.9651644865595, -98.1529175079091,
    1493             : -98.3842631146439, -98.664391423662, -99 orbit_type=ascending
    1494             :   raster_type=RasterPixelIsArea
    1495             :   swath_sync_lines=1
    1496             : 
    1497             :  */
    1498             : 
    1499           0 : void HDF4ImageDataset::CaptureCoastwatchGCTPInfo()
    1500             : 
    1501             : {
    1502           0 :     if (CSLFetchNameValue(papszGlobalMetadata, "gctp_sys") == nullptr ||
    1503           0 :         CSLFetchNameValue(papszGlobalMetadata, "gctp_zone") == nullptr ||
    1504           0 :         CSLFetchNameValue(papszGlobalMetadata, "gctp_parm") == nullptr ||
    1505           0 :         CSLFetchNameValue(papszGlobalMetadata, "gctp_datum") == nullptr ||
    1506           0 :         CSLFetchNameValue(papszGlobalMetadata, "et_affine") == nullptr)
    1507           0 :         return;
    1508             : 
    1509             :     /* -------------------------------------------------------------------- */
    1510             :     /*      Grab USGS/GCTP Parameters.                                      */
    1511             :     /* -------------------------------------------------------------------- */
    1512           0 :     const int nSys = atoi(CSLFetchNameValue(papszGlobalMetadata, "gctp_sys"));
    1513           0 :     const int nZone = atoi(CSLFetchNameValue(papszGlobalMetadata, "gctp_zone"));
    1514             :     const int nDatum =
    1515           0 :         atoi(CSLFetchNameValue(papszGlobalMetadata, "gctp_datum"));
    1516             : 
    1517           0 :     char **papszTokens = CSLTokenizeStringComplex(
    1518           0 :         CSLFetchNameValue(papszGlobalMetadata, "gctp_parm"), ",", FALSE, FALSE);
    1519           0 :     if (CSLCount(papszTokens) < 15)
    1520             :     {
    1521           0 :         CSLDestroy(papszTokens);
    1522           0 :         return;
    1523             :     }
    1524             : 
    1525             :     double adfParams[15];
    1526           0 :     for (int iParam = 0; iParam < 15; iParam++)
    1527           0 :         adfParams[iParam] = CPLAtof(papszTokens[iParam]);
    1528           0 :     CSLDestroy(papszTokens);
    1529             : 
    1530             :     /* -------------------------------------------------------------------- */
    1531             :     /*      Convert into an SRS.                                            */
    1532             :     /* -------------------------------------------------------------------- */
    1533             : 
    1534           0 :     if (m_oSRS.importFromUSGS(nSys, nZone, adfParams, nDatum) != OGRERR_NONE)
    1535           0 :         return;
    1536             : 
    1537             :     /* -------------------------------------------------------------------- */
    1538             :     /*      Capture the affine transform info.                              */
    1539             :     /* -------------------------------------------------------------------- */
    1540             : 
    1541           0 :     papszTokens = CSLTokenizeStringComplex(
    1542           0 :         CSLFetchNameValue(papszGlobalMetadata, "et_affine"), ",", FALSE, FALSE);
    1543           0 :     if (CSLCount(papszTokens) != 6)
    1544             :     {
    1545           0 :         CSLDestroy(papszTokens);
    1546           0 :         return;
    1547             :     }
    1548             : 
    1549             :     // We don't seem to have proper ef_affine docs so I don't
    1550             :     // know which of these two coefficients goes where.
    1551           0 :     if (CPLAtof(papszTokens[0]) != 0.0 || CPLAtof(papszTokens[3]) != 0.0)
    1552             :     {
    1553           0 :         CSLDestroy(papszTokens);
    1554           0 :         return;
    1555             :     }
    1556             : 
    1557           0 :     bHasGeoTransform = true;
    1558           0 :     adfGeoTransform[0] = CPLAtof(papszTokens[4]);
    1559           0 :     adfGeoTransform[1] = CPLAtof(papszTokens[2]);
    1560           0 :     adfGeoTransform[2] = 0.0;
    1561           0 :     adfGeoTransform[3] = CPLAtof(papszTokens[5]);
    1562           0 :     adfGeoTransform[4] = 0.0;
    1563           0 :     adfGeoTransform[5] = CPLAtof(papszTokens[1]);
    1564             : 
    1565             :     // Middle of pixel adjustment.
    1566           0 :     adfGeoTransform[0] -= adfGeoTransform[1] * 0.5;
    1567           0 :     adfGeoTransform[3] -= adfGeoTransform[5] * 0.5;
    1568             : 
    1569           0 :     CSLDestroy(papszTokens);
    1570             : }
    1571             : 
    1572             : /************************************************************************/
    1573             : /*                          GetImageDimensions()                        */
    1574             : /************************************************************************/
    1575             : 
    1576           0 : void HDF4ImageDataset::GetImageDimensions(char *pszDimList)
    1577             : {
    1578             :     char **papszDimList =
    1579           0 :         CSLTokenizeString2(pszDimList, ",", CSLT_HONOURSTRINGS);
    1580           0 :     const int nDimCount = CSLCount(papszDimList);
    1581             : 
    1582             :     // TODO: check whether nDimCount is > 1 and do something if it isn't.
    1583             : 
    1584             :     // Search for the "Band" word in the name of dimension
    1585             :     // or take the first one as a number of bands
    1586           0 :     if (iRank == 2)
    1587             :     {
    1588           0 :         nBandCount = 1;
    1589             :     }
    1590             :     else
    1591             :     {
    1592           0 :         for (int i = 0; i < nDimCount; i++)
    1593             :         {
    1594           0 :             if (strstr(papszDimList[i], "band"))
    1595             :             {
    1596           0 :                 iBandDim = i;
    1597           0 :                 nBandCount = aiDimSizes[i];
    1598             :                 // Handle 4D datasets
    1599           0 :                 if (iRank > 3 && i < nDimCount - 1)
    1600             :                 {
    1601             :                     // FIXME: is there a better way to search for
    1602             :                     // the 4th dimension?
    1603           0 :                     i4Dim = i + 1;
    1604           0 :                     nBandCount *= aiDimSizes[i4Dim];
    1605             :                 }
    1606           0 :                 break;
    1607             :             }
    1608             :         }
    1609             :     }
    1610             : 
    1611             :     // Search for the starting "X" and "Y" in the names or take
    1612             :     // the last two dimensions as X and Y sizes.
    1613           0 :     iXDim = nDimCount - 1;
    1614           0 :     iYDim = nDimCount - 2;
    1615             : 
    1616           0 :     for (int i = 0; i < nDimCount; i++)
    1617             :     {
    1618           0 :         if (STARTS_WITH_CI(papszDimList[i], "X") && iBandDim != i)
    1619           0 :             iXDim = i;
    1620           0 :         else if (STARTS_WITH_CI(papszDimList[i], "Y") && iBandDim != i)
    1621           0 :             iYDim = i;
    1622             :     }
    1623             : 
    1624             :     // If didn't get a band dimension yet, but have an extra
    1625             :     // dimension, use it as the band dimension.
    1626           0 :     if (iRank > 2 && iBandDim == -1)
    1627             :     {
    1628           0 :         if (iXDim != 0 && iYDim != 0)
    1629           0 :             iBandDim = 0;
    1630           0 :         else if (iXDim != 1 && iYDim != 1)
    1631           0 :             iBandDim = 1;
    1632           0 :         else if (iXDim != 2 && iYDim != 2)
    1633           0 :             iBandDim = 2;
    1634             : 
    1635           0 :         nBandCount = aiDimSizes[iBandDim];
    1636             :     }
    1637             : 
    1638           0 :     CSLDestroy(papszDimList);
    1639           0 : }
    1640             : 
    1641             : /************************************************************************/
    1642             : /*                            GetSwatAttrs()                            */
    1643             : /************************************************************************/
    1644             : 
    1645           0 : void HDF4ImageDataset::GetSwatAttrs(int32 hSW)
    1646             : {
    1647             :     /* -------------------------------------------------------------------- */
    1648             :     /*      At the start we will fetch the global HDF attributes.           */
    1649             :     /* -------------------------------------------------------------------- */
    1650           0 :     int32 hDummy = 0;
    1651             : 
    1652           0 :     EHidinfo(hHDF4, &hDummy, &hSD);
    1653           0 :     ReadGlobalAttributes(hSD);
    1654           0 :     papszLocalMetadata = CSLDuplicate(papszGlobalMetadata);
    1655             : 
    1656             :     /* -------------------------------------------------------------------- */
    1657             :     /*      Fetch the esoteric HDF-EOS attributes then.                     */
    1658             :     /* -------------------------------------------------------------------- */
    1659           0 :     int32 nStrBufSize = 0;
    1660             : 
    1661           0 :     if (SWinqattrs(hSW, nullptr, &nStrBufSize) > 0 && nStrBufSize > 0)
    1662             :     {
    1663             :         char *pszAttrList =
    1664           0 :             reinterpret_cast<char *>(CPLMalloc(nStrBufSize + 1));
    1665           0 :         SWinqattrs(hSW, pszAttrList, &nStrBufSize);
    1666             : 
    1667             : #ifdef DEBUG
    1668           0 :         CPLDebug("HDF4Image", "List of attributes in swath \"%s\": %s",
    1669             :                  pszFieldName, pszAttrList);
    1670             : #endif
    1671             : 
    1672             :         char **papszAttributes =
    1673           0 :             CSLTokenizeString2(pszAttrList, ",", CSLT_HONOURSTRINGS);
    1674           0 :         const int l_nAttrs = CSLCount(papszAttributes);
    1675           0 :         for (int i = 0; i < l_nAttrs; i++)
    1676             :         {
    1677           0 :             int32 l_iNumType = 0;
    1678           0 :             int32 nValues = 0;
    1679             : 
    1680           0 :             if (SWattrinfo(hSW, papszAttributes[i], &l_iNumType, &nValues) < 0)
    1681           0 :                 continue;
    1682           0 :             const int nDataTypeSize = GetDataTypeSize(l_iNumType);
    1683           0 :             if (nDataTypeSize == 0)
    1684           0 :                 continue;
    1685           0 :             CPLAssert((nValues % nDataTypeSize) == 0);
    1686             : 
    1687           0 :             void *pData = CPLMalloc(nValues + 1);
    1688           0 :             SWreadattr(hSW, papszAttributes[i], pData);
    1689             : 
    1690           0 :             if (l_iNumType == DFNT_CHAR8 || l_iNumType == DFNT_UCHAR8)
    1691             :             {
    1692           0 :                 reinterpret_cast<char *>(pData)[nValues] = '\0';
    1693           0 :                 papszLocalMetadata = CSLAddNameValue(
    1694           0 :                     papszLocalMetadata, papszAttributes[i],
    1695             :                     const_cast<const char *>(reinterpret_cast<char *>(pData)));
    1696             :             }
    1697             :             else
    1698             :             {
    1699           0 :                 char *pszTemp = SPrintArray(GetDataType(l_iNumType), pData,
    1700             :                                             nValues / nDataTypeSize, ", ");
    1701           0 :                 papszLocalMetadata = CSLAddNameValue(
    1702           0 :                     papszLocalMetadata, papszAttributes[i], pszTemp);
    1703           0 :                 CPLFree(pszTemp);
    1704             :             }
    1705             : 
    1706           0 :             CPLFree(pData);
    1707             :         }
    1708             : 
    1709           0 :         CSLDestroy(papszAttributes);
    1710           0 :         CPLFree(pszAttrList);
    1711             :     }
    1712             : 
    1713             :     /* -------------------------------------------------------------------- */
    1714             :     /*      After fetching HDF-EOS specific stuff we will read the generic  */
    1715             :     /*      HDF attributes and append them to the list of metadata.         */
    1716             :     /* -------------------------------------------------------------------- */
    1717           0 :     int32 l_iSDS = 0;
    1718           0 :     if (SWsdid(hSW, pszFieldName, &l_iSDS) != -1)
    1719             :     {
    1720           0 :         int32 l_iRank = 0;
    1721           0 :         int32 l_iNumType = 0;
    1722           0 :         int32 l_nAttrs = 0;
    1723           0 :         char l_szName[HDF4_SDS_MAXNAMELEN] = {};
    1724           0 :         int32 l_aiDimSizes[H4_MAX_VAR_DIMS] = {};
    1725             : 
    1726           0 :         if (SDgetinfo(l_iSDS, l_szName, &l_iRank, l_aiDimSizes, &l_iNumType,
    1727           0 :                       &l_nAttrs) == 0)
    1728             :         {
    1729           0 :             for (int32 iAttribute = 0; iAttribute < l_nAttrs; iAttribute++)
    1730             :             {
    1731           0 :                 char szAttrName[H4_MAX_NC_NAME] = {};
    1732           0 :                 int32 nValues = 0;
    1733           0 :                 SDattrinfo(l_iSDS, iAttribute, szAttrName, &l_iNumType,
    1734             :                            &nValues);
    1735           0 :                 papszLocalMetadata = TranslateHDF4Attributes(
    1736             :                     l_iSDS, iAttribute, szAttrName, l_iNumType, nValues,
    1737             :                     papszLocalMetadata);
    1738             :             }
    1739             :         }
    1740             :     }
    1741             : 
    1742             :     /* -------------------------------------------------------------------- */
    1743             :     /*      Finally make the whole list visible.                            */
    1744             :     /* -------------------------------------------------------------------- */
    1745           0 :     SetMetadata(papszLocalMetadata);
    1746           0 : }
    1747             : 
    1748             : /************************************************************************/
    1749             : /*                            GetGridAttrs()                            */
    1750             : /************************************************************************/
    1751             : 
    1752           0 : void HDF4ImageDataset::GetGridAttrs(int32 hGD)
    1753             : {
    1754             :     /* -------------------------------------------------------------------- */
    1755             :     /*      At the start we will fetch the global HDF attributes.           */
    1756             :     /* -------------------------------------------------------------------- */
    1757           0 :     int32 hDummy = 0;
    1758             : 
    1759           0 :     EHidinfo(hHDF4, &hDummy, &hSD);
    1760           0 :     ReadGlobalAttributes(hSD);
    1761           0 :     papszLocalMetadata = CSLDuplicate(papszGlobalMetadata);
    1762             : 
    1763             :     /* -------------------------------------------------------------------- */
    1764             :     /*      Fetch the esoteric HDF-EOS attributes then.                     */
    1765             :     /* -------------------------------------------------------------------- */
    1766           0 :     int32 nStrBufSize = 0;
    1767             : 
    1768           0 :     if (GDinqattrs(hGD, nullptr, &nStrBufSize) > 0 && nStrBufSize > 0)
    1769             :     {
    1770             :         char *pszAttrList =
    1771           0 :             reinterpret_cast<char *>(CPLMalloc(nStrBufSize + 1));
    1772           0 :         GDinqattrs(hGD, pszAttrList, &nStrBufSize);
    1773             : 
    1774             : #ifdef DEBUG
    1775           0 :         CPLDebug("HDF4Image", "List of attributes in grid %s: %s", pszFieldName,
    1776             :                  pszAttrList);
    1777             : #endif
    1778             : 
    1779             :         char **papszAttributes =
    1780           0 :             CSLTokenizeString2(pszAttrList, ",", CSLT_HONOURSTRINGS);
    1781           0 :         const int l_nAttrs = CSLCount(papszAttributes);
    1782           0 :         for (int i = 0; i < l_nAttrs; i++)
    1783             :         {
    1784           0 :             int32 l_iNumType = 0;
    1785           0 :             int32 nValues = 0;
    1786             : 
    1787           0 :             GDattrinfo(hGD, papszAttributes[i], &l_iNumType, &nValues);
    1788           0 :             const int nDataTypeSize = GetDataTypeSize(l_iNumType);
    1789           0 :             if (nDataTypeSize == 0)
    1790           0 :                 continue;
    1791           0 :             CPLAssert((nValues % nDataTypeSize) == 0);
    1792             : 
    1793           0 :             void *pData = CPLMalloc(nValues + 1);
    1794           0 :             GDreadattr(hGD, papszAttributes[i], pData);
    1795             : 
    1796           0 :             if (l_iNumType == DFNT_CHAR8 || l_iNumType == DFNT_UCHAR8)
    1797             :             {
    1798           0 :                 reinterpret_cast<char *>(pData)[nValues] = '\0';
    1799           0 :                 papszLocalMetadata =
    1800           0 :                     CSLAddNameValue(papszLocalMetadata, papszAttributes[i],
    1801             :                                     (const char *)pData);
    1802             :             }
    1803             :             else
    1804             :             {
    1805           0 :                 char *pszTemp = SPrintArray(GetDataType(l_iNumType), pData,
    1806             :                                             nValues / nDataTypeSize, ", ");
    1807           0 :                 papszLocalMetadata = CSLAddNameValue(
    1808           0 :                     papszLocalMetadata, papszAttributes[i], pszTemp);
    1809           0 :                 CPLFree(pszTemp);
    1810             :             }
    1811             : 
    1812           0 :             CPLFree(pData);
    1813             :         }
    1814             : 
    1815           0 :         CSLDestroy(papszAttributes);
    1816           0 :         CPLFree(pszAttrList);
    1817             :     }
    1818             : 
    1819             :     /* -------------------------------------------------------------------- */
    1820             :     /*      After fetching HDF-EOS specific stuff we will read the generic  */
    1821             :     /*      HDF attributes and append them to the list of metadata.         */
    1822             :     /* -------------------------------------------------------------------- */
    1823           0 :     int32 l_iSDS = 0;
    1824           0 :     if (GDsdid(hGD, pszFieldName, &l_iSDS) != -1)
    1825             :     {
    1826           0 :         int32 l_iRank = 0;
    1827           0 :         int32 l_iNumType = 0;
    1828           0 :         int32 l_nAttrs = 0;
    1829           0 :         int32 nValues = 0;
    1830           0 :         char l_szName[HDF4_SDS_MAXNAMELEN] = {};
    1831           0 :         int32 l_aiDimSizes[H4_MAX_VAR_DIMS] = {};
    1832             : 
    1833           0 :         if (SDgetinfo(l_iSDS, l_szName, &l_iRank, l_aiDimSizes, &l_iNumType,
    1834           0 :                       &l_nAttrs) == 0)
    1835             :         {
    1836           0 :             for (int32 iAttribute = 0; iAttribute < l_nAttrs; iAttribute++)
    1837             :             {
    1838           0 :                 char szAttrName[H4_MAX_NC_NAME] = {};
    1839           0 :                 SDattrinfo(l_iSDS, iAttribute, szAttrName, &l_iNumType,
    1840             :                            &nValues);
    1841           0 :                 papszLocalMetadata = TranslateHDF4Attributes(
    1842             :                     l_iSDS, iAttribute, szAttrName, l_iNumType, nValues,
    1843             :                     papszLocalMetadata);
    1844             :             }
    1845             :         }
    1846             :     }
    1847             : 
    1848             :     /* -------------------------------------------------------------------- */
    1849             :     /*      Finally make the whole list visible.                            */
    1850             :     /* -------------------------------------------------------------------- */
    1851           0 :     SetMetadata(papszLocalMetadata);
    1852           0 : }
    1853             : 
    1854             : /************************************************************************/
    1855             : /*                     ProcessModisSDSGeolocation()                     */
    1856             : /*                                                                      */
    1857             : /*      Recognise latitude and longitude geolocation arrays in          */
    1858             : /*      simple SDS datasets like:                                       */
    1859             : /*                                                                      */
    1860             : /*      download.osgeo.org/gdal/data/hdf4/A2006005182000.L2_LAC_SST.x.hdf */
    1861             : /*                                                                      */
    1862             : /*      As reported in ticket #1895.                                    */
    1863             : /************************************************************************/
    1864             : 
    1865         299 : void HDF4ImageDataset::ProcessModisSDSGeolocation(void)
    1866             : 
    1867             : {
    1868             :     // No point in assigning geolocation to the geolocation SDSes themselves.
    1869         299 :     if (EQUAL(szName, "longitude") || EQUAL(szName, "latitude"))
    1870         299 :         return;
    1871             : 
    1872         299 :     if (nRasterYSize == 1)
    1873           0 :         return;
    1874             : 
    1875             :     /* -------------------------------------------------------------------- */
    1876             :     /*      Scan for latitude and longitude sections.                       */
    1877             :     /* -------------------------------------------------------------------- */
    1878         299 :     int32 nDatasets = 0;
    1879         299 :     int32 nAttributes = 0;
    1880             : 
    1881         299 :     if (SDfileinfo(hSD, &nDatasets, &nAttributes) != 0)
    1882           0 :         return;
    1883             : 
    1884         299 :     int nLongitudeWidth = 0;
    1885         299 :     int nLongitudeHeight = 0;
    1886         299 :     int nLatitudeWidth = 0;
    1887         299 :     int nLatitudeHeight = 0;
    1888         299 :     int iXIndex = -1;
    1889         299 :     int iYIndex = -1;
    1890         598 :     for (int iDSIndex = 0; iDSIndex < nDatasets; iDSIndex++)
    1891             :     {
    1892         299 :         int32 l_iRank = 0;
    1893         299 :         int32 l_iNumType = 0;
    1894         299 :         int32 l_nAttrs = 0;
    1895         299 :         char l_szName[HDF4_SDS_MAXNAMELEN] = {};
    1896         299 :         int32 l_aiDimSizes[H4_MAX_VAR_DIMS] = {};
    1897             : 
    1898         299 :         const int32 l_iSDS = SDselect(hSD, iDSIndex);
    1899             : 
    1900         299 :         if (SDgetinfo(l_iSDS, l_szName, &l_iRank, l_aiDimSizes, &l_iNumType,
    1901         299 :                       &l_nAttrs) == 0)
    1902             :         {
    1903         299 :             if (EQUAL(l_szName, "latitude"))
    1904             :             {
    1905           0 :                 iYIndex = iDSIndex;
    1906           0 :                 if (l_iRank == 2)
    1907             :                 {
    1908           0 :                     nLatitudeWidth = l_aiDimSizes[1];
    1909           0 :                     nLatitudeHeight = l_aiDimSizes[0];
    1910             :                 }
    1911             :             }
    1912             : 
    1913         299 :             if (EQUAL(l_szName, "longitude"))
    1914             :             {
    1915           0 :                 iXIndex = iDSIndex;
    1916           0 :                 if (l_iRank == 2)
    1917             :                 {
    1918           0 :                     nLongitudeWidth = l_aiDimSizes[1];
    1919           0 :                     nLongitudeHeight = l_aiDimSizes[0];
    1920             :                 }
    1921             :             }
    1922             :         }
    1923             : 
    1924         299 :         SDendaccess(l_iSDS);
    1925             :     }
    1926             : 
    1927         299 :     if (iXIndex == -1 || iYIndex == -1)
    1928         299 :         return;
    1929             : 
    1930           0 :     int nPixelOffset = 0;
    1931           0 :     int nLineOffset = 0;
    1932           0 :     int nPixelStep = 1;
    1933           0 :     int nLineStep = 1;
    1934           0 :     if (nLongitudeWidth != nLatitudeWidth ||
    1935             :         nLongitudeHeight != nLatitudeHeight)
    1936             :     {
    1937           0 :         CPLDebug("HDF4", "Longitude and latitude subdatasets don't have same "
    1938             :                          "dimensions...");
    1939             :     }
    1940           0 :     else if (nLongitudeWidth > 0 && nLongitudeHeight > 0)
    1941             :     {
    1942           0 :         nPixelStep =
    1943           0 :             static_cast<int>(0.5 + 1.0 * nRasterXSize / nLongitudeWidth);
    1944           0 :         nLineStep =
    1945           0 :             static_cast<int>(0.5 + 1.0 * nRasterYSize / nLongitudeHeight);
    1946           0 :         nPixelOffset = (nPixelStep - 1) / 2;
    1947           0 :         nLineOffset = (nLineStep - 1) / 2;
    1948             :     }
    1949             : 
    1950             :     /* -------------------------------------------------------------------- */
    1951             :     /*      We found geolocation information.  Record it as metadata.       */
    1952             :     /* -------------------------------------------------------------------- */
    1953             : 
    1954           0 :     SetMetadataItem("SRS", SRS_WKT_WGS84_LAT_LONG, "GEOLOCATION");
    1955             : 
    1956           0 :     CPLString osWrk;
    1957           0 :     osWrk.Printf("HDF4_SDS:UNKNOWN:\"%s\":%d", pszFilename, iXIndex);
    1958           0 :     SetMetadataItem("X_DATASET", osWrk, "GEOLOCATION");
    1959           0 :     SetMetadataItem("X_BAND", "1", "GEOLOCATION");
    1960             : 
    1961           0 :     osWrk.Printf("HDF4_SDS:UNKNOWN:\"%s\":%d", pszFilename, iYIndex);
    1962           0 :     SetMetadataItem("Y_DATASET", osWrk, "GEOLOCATION");
    1963           0 :     SetMetadataItem("Y_BAND", "1", "GEOLOCATION");
    1964             : 
    1965           0 :     SetMetadataItem("PIXEL_OFFSET", CPLSPrintf("%d", nPixelOffset),
    1966           0 :                     "GEOLOCATION");
    1967           0 :     SetMetadataItem("PIXEL_STEP", CPLSPrintf("%d", nPixelStep), "GEOLOCATION");
    1968             : 
    1969           0 :     SetMetadataItem("LINE_OFFSET", CPLSPrintf("%d", nLineOffset),
    1970           0 :                     "GEOLOCATION");
    1971           0 :     SetMetadataItem("LINE_STEP", CPLSPrintf("%d", nLineStep), "GEOLOCATION");
    1972             : }
    1973             : 
    1974             : /************************************************************************/
    1975             : /*                      ProcessSwathGeolocation()                       */
    1976             : /*                                                                      */
    1977             : /*      Handle the swath geolocation data for a swath.  Attach          */
    1978             : /*      geolocation metadata corresponding to it (if there is no        */
    1979             : /*      lattice), and also attach it as GCPs.  This is only invoked     */
    1980             : /*      for EOS_SWATH, not EOS_SWATH_GEOL datasets.                     */
    1981             : /************************************************************************/
    1982             : 
    1983           0 : int HDF4ImageDataset::ProcessSwathGeolocation(int32 hSW, char **papszDimList)
    1984             : {
    1985             : 
    1986             :     /* -------------------------------------------------------------------- */
    1987             :     /*  Determine a product name.                                           */
    1988             :     /* -------------------------------------------------------------------- */
    1989           0 :     const char *pszProduct = CSLFetchNameValue(papszLocalMetadata, "SHORTNAME");
    1990             : 
    1991           0 :     HDF4EOSProduct eProduct = PROD_UNKNOWN;
    1992           0 :     if (pszProduct)
    1993             :     {
    1994           0 :         if (STARTS_WITH_CI(pszProduct, "ASTL1A"))
    1995           0 :             eProduct = PROD_ASTER_L1A;
    1996           0 :         else if (STARTS_WITH_CI(pszProduct, "ASTL1B"))
    1997           0 :             eProduct = PROD_ASTER_L1B;
    1998           0 :         else if (STARTS_WITH_CI(pszProduct, "AST_04") ||
    1999           0 :                  STARTS_WITH_CI(pszProduct, "AST_05") ||
    2000           0 :                  STARTS_WITH_CI(pszProduct, "AST_06") ||
    2001           0 :                  STARTS_WITH_CI(pszProduct, "AST_07") ||
    2002           0 :                  STARTS_WITH_CI(pszProduct, "AST_08") ||
    2003           0 :                  STARTS_WITH_CI(pszProduct, "AST_09") ||
    2004           0 :                  STARTS_WITH_CI(pszProduct, "AST13") ||
    2005           0 :                  STARTS_WITH_CI(pszProduct, "AST3"))
    2006           0 :             eProduct = PROD_ASTER_L2;
    2007           0 :         else if (STARTS_WITH_CI(pszProduct, "AST14"))
    2008           0 :             eProduct = PROD_ASTER_L3;
    2009           0 :         else if (STARTS_WITH_CI(pszProduct, "MOD02") ||
    2010           0 :                  STARTS_WITH_CI(pszProduct, "MYD02"))
    2011           0 :             eProduct = PROD_MODIS_L1B;
    2012           0 :         else if (STARTS_WITH_CI(pszProduct, "MOD07_L2"))
    2013           0 :             eProduct = PROD_MODIS_L2;
    2014             :     }
    2015             : 
    2016             :     /* -------------------------------------------------------------------- */
    2017             :     /*      Read names of geolocation fields and corresponding              */
    2018             :     /*      geolocation maps.                                               */
    2019             :     /* -------------------------------------------------------------------- */
    2020           0 :     int32 nStrBufSize = 0;
    2021           0 :     const int32 nDataFields = SWnentries(hSW, HDFE_NENTGFLD, &nStrBufSize);
    2022           0 :     if (nDataFields < 0 || nDataFields > 1024 * 1024)
    2023           0 :         return FALSE;
    2024           0 :     char *pszGeoList = reinterpret_cast<char *>(CPLMalloc(nStrBufSize + 1));
    2025             :     int32 *paiRank =
    2026           0 :         reinterpret_cast<int32 *>(CPLMalloc(nDataFields * sizeof(int32)));
    2027             :     int32 *paiNumType =
    2028           0 :         reinterpret_cast<int32 *>(CPLMalloc(nDataFields * sizeof(int32)));
    2029             : 
    2030           0 :     if (nDataFields != SWinqgeofields(hSW, pszGeoList, paiRank, paiNumType))
    2031             :     {
    2032           0 :         CPLDebug("HDF4Image",
    2033             :                  "Can't get the list of geolocation fields in swath \"%s\"",
    2034             :                  pszSubdatasetName);
    2035             :     }
    2036             : 
    2037             : #ifdef DEBUG
    2038             :     else
    2039             :     {
    2040           0 :         CPLDebug("HDF4Image",
    2041             :                  "Number of geolocation fields in swath \"%s\": %ld",
    2042             :                  pszSubdatasetName, static_cast<long>(nDataFields));
    2043           0 :         CPLDebug("HDF4Image", "List of geolocation fields in swath \"%s\": %s",
    2044             :                  pszSubdatasetName, pszGeoList);
    2045           0 :         char *pszTmp = SPrintArray(GDT_UInt32, paiRank, nDataFields, ",");
    2046           0 :         CPLDebug("HDF4Image", "Geolocation fields ranks: %s", pszTmp);
    2047           0 :         CPLFree(pszTmp);
    2048             :     }
    2049             : #endif
    2050             : 
    2051           0 :     CPLFree(paiRank);
    2052           0 :     CPLFree(paiNumType);
    2053             : 
    2054             :     /* -------------------------------------------------------------------- */
    2055             :     /*      Read geolocation data.                                          */
    2056             :     /* -------------------------------------------------------------------- */
    2057           0 :     char szXGeo[N_BUF_SIZE] = "";
    2058           0 :     char szYGeo[N_BUF_SIZE] = "";
    2059           0 :     char szPixel[N_BUF_SIZE] = "";
    2060           0 :     char szLine[N_BUF_SIZE] = "";
    2061           0 :     int32 *paiOffset = nullptr;
    2062           0 :     int32 *paiIncrement = nullptr;
    2063             : 
    2064           0 :     int32 nDimMaps = SWnentries(hSW, HDFE_NENTMAP, &nStrBufSize);
    2065           0 :     if (nDimMaps <= 0)
    2066             :     {
    2067             : 
    2068             : #ifdef DEBUG
    2069           0 :         CPLDebug("HDF4Image", "No geolocation maps in swath \"%s\"",
    2070             :                  pszSubdatasetName);
    2071           0 :         CPLDebug("HDF4Image",
    2072             :                  "Suppose one-to-one mapping. X field is \"%s\", "
    2073             :                  "Y field is \"%s\"",
    2074           0 :                  papszDimList[iXDim], papszDimList[iYDim]);
    2075             : #endif
    2076             : 
    2077           0 :         snprintf(szPixel, sizeof(szPixel), "%s", papszDimList[iXDim]);
    2078             : 
    2079           0 :         snprintf(szLine, sizeof(szLine), "%s", papszDimList[iYDim]);
    2080             : 
    2081           0 :         snprintf(szXGeo, sizeof(szXGeo), "%s", papszDimList[iXDim]);
    2082             : 
    2083           0 :         snprintf(szYGeo, sizeof(szYGeo), "%s", papszDimList[iYDim]);
    2084             : 
    2085           0 :         paiOffset = reinterpret_cast<int32 *>(CPLCalloc(2, sizeof(int32)));
    2086           0 :         paiIncrement = reinterpret_cast<int32 *>(CPLCalloc(2, sizeof(int32)));
    2087           0 :         paiOffset[0] = 0;
    2088           0 :         paiOffset[1] = 0;
    2089           0 :         paiIncrement[0] = 1;
    2090           0 :         paiIncrement[1] = 1;
    2091             :     }
    2092             :     else
    2093             :     {
    2094           0 :         char *pszDimMaps = reinterpret_cast<char *>(CPLMalloc(nStrBufSize + 1));
    2095             :         paiOffset =
    2096           0 :             reinterpret_cast<int32 *>(CPLCalloc(nDimMaps, sizeof(int32)));
    2097             :         paiIncrement =
    2098           0 :             reinterpret_cast<int32 *>(CPLCalloc(nDimMaps, sizeof(int32)));
    2099             : 
    2100           0 :         *pszDimMaps = '\0';
    2101           0 :         if (nDimMaps != SWinqmaps(hSW, pszDimMaps, paiOffset, paiIncrement))
    2102             :         {
    2103           0 :             CPLDebug("HDF4Image",
    2104             :                      "Can't get the list of geolocation maps in swath \"%s\"",
    2105             :                      pszSubdatasetName);
    2106             :         }
    2107             : 
    2108             : #ifdef DEBUG
    2109             :         else
    2110             :         {
    2111             : 
    2112           0 :             CPLDebug("HDF4Image",
    2113             :                      "List of geolocation maps in swath \"%s\": %s",
    2114             :                      pszSubdatasetName, pszDimMaps);
    2115           0 :             char *pszTmp = SPrintArray(GDT_Int32, paiOffset, nDimMaps, ",");
    2116           0 :             CPLDebug("HDF4Image", "Geolocation map offsets: %s", pszTmp);
    2117           0 :             CPLFree(pszTmp);
    2118           0 :             pszTmp = SPrintArray(GDT_Int32, paiIncrement, nDimMaps, ",");
    2119           0 :             CPLDebug("HDF4Image", "Geolocation map increments: %s", pszTmp);
    2120           0 :             CPLFree(pszTmp);
    2121             :         }
    2122             : #endif
    2123             : 
    2124             :         char **papszDimMap =
    2125           0 :             CSLTokenizeString2(pszDimMaps, ",", CSLT_HONOURSTRINGS);
    2126           0 :         const int nDimMapCount = CSLCount(papszDimMap);
    2127             : 
    2128           0 :         for (int i = 0; i < nDimMapCount; i++)
    2129             :         {
    2130           0 :             if (strstr(papszDimMap[i], papszDimList[iXDim]))
    2131             :             {
    2132           0 :                 snprintf(szPixel, sizeof(szPixel), "%s", papszDimList[iXDim]);
    2133             : 
    2134           0 :                 snprintf(szXGeo, sizeof(szXGeo), "%s", papszDimMap[i]);
    2135             : 
    2136           0 :                 char *pszTemp = strchr(szXGeo, '/');
    2137           0 :                 if (pszTemp)
    2138           0 :                     *pszTemp = '\0';
    2139             :             }
    2140           0 :             else if (strstr(papszDimMap[i], papszDimList[iYDim]))
    2141             :             {
    2142           0 :                 snprintf(szLine, sizeof(szLine), "%s", papszDimList[iYDim]);
    2143             : 
    2144           0 :                 snprintf(szYGeo, sizeof(szYGeo), "%s", papszDimMap[i]);
    2145             : 
    2146           0 :                 char *pszTemp = strchr(szYGeo, '/');
    2147           0 :                 if (pszTemp)
    2148           0 :                     *pszTemp = '\0';
    2149             :             }
    2150             :         }
    2151             : 
    2152           0 :         CSLDestroy(papszDimMap);
    2153           0 :         CPLFree(pszDimMaps);
    2154             :     }
    2155             : 
    2156           0 :     if (*szXGeo == 0 || *szYGeo == 0)
    2157             :     {
    2158           0 :         CPLFree(paiOffset);
    2159           0 :         CPLFree(paiIncrement);
    2160           0 :         CPLFree(pszGeoList);
    2161           0 :         return FALSE;
    2162             :     }
    2163             : 
    2164             :     /* -------------------------------------------------------------------- */
    2165             :     /*      Read geolocation fields.                                        */
    2166             :     /* -------------------------------------------------------------------- */
    2167           0 :     char szGeoDimList[N_BUF_SIZE] = "";
    2168             :     char **papszGeolocations =
    2169           0 :         CSLTokenizeString2(pszGeoList, ",", CSLT_HONOURSTRINGS);
    2170           0 :     const int nGeolocationsCount = CSLCount(papszGeolocations);
    2171           0 :     int32 l_aiDimSizes[H4_MAX_VAR_DIMS] = {};
    2172             : 
    2173           0 :     int32 iWrkNumType = 0;
    2174           0 :     void *pLat = nullptr;
    2175           0 :     void *pLong = nullptr;
    2176             : 
    2177           0 :     int32 l_iRank = 0;
    2178           0 :     int32 nLatCount = 0;
    2179           0 :     int32 nLongCount = 0;
    2180           0 :     int32 nXPoints = 0;
    2181           0 :     int32 nYPoints = 0;
    2182           0 :     int iDataSize = 0;
    2183             : 
    2184           0 :     int iPixelDim = -1;
    2185           0 :     int iLineDim = -1;
    2186           0 :     int iLongDim = -1;
    2187           0 :     int iLatDim = -1;
    2188             : 
    2189           0 :     for (int i = 0; i < nGeolocationsCount; i++)
    2190             :     {
    2191             :         // Skip "SceneLineNumber" table if present in the list of geolocation
    2192             :         // fields. It is not needed to fetch geocoding data.
    2193           0 :         if (EQUAL(papszGeolocations[i], "SceneLineNumber"))
    2194           0 :             continue;
    2195             : 
    2196           0 :         if (SWfieldinfo(hSW, papszGeolocations[i], &l_iRank, l_aiDimSizes,
    2197           0 :                         &iWrkNumType, szGeoDimList) < 0)
    2198             :         {
    2199             : 
    2200           0 :             CPLDebug("HDF4Image",
    2201             :                      "Can't read attributes of geolocation field \"%s\"",
    2202           0 :                      papszGeolocations[i]);
    2203           0 :             CSLDestroy(papszGeolocations);
    2204           0 :             CPLFree(paiOffset);
    2205           0 :             CPLFree(paiIncrement);
    2206           0 :             CPLFree(pszGeoList);
    2207           0 :             return FALSE;
    2208             :         }
    2209             : 
    2210           0 :         CPLDebug("HDF4Image",
    2211             :                  "List of dimensions in geolocation field \"%s\": %s",
    2212           0 :                  papszGeolocations[i], szGeoDimList);
    2213             : 
    2214             :         char **papszGeoDimList =
    2215           0 :             CSLTokenizeString2(szGeoDimList, ",", CSLT_HONOURSTRINGS);
    2216             : 
    2217           0 :         const int iXGeo = CSLFindString(papszGeoDimList, szXGeo);
    2218           0 :         const int iYGeo = CSLFindString(papszGeoDimList, szYGeo);
    2219           0 :         if (CSLCount(papszGeoDimList) > H4_MAX_VAR_DIMS || iXGeo < 0 ||
    2220             :             iYGeo < 0)
    2221             :         {
    2222           0 :             CSLDestroy(papszGeoDimList);
    2223           0 :             CSLDestroy(papszGeolocations);
    2224           0 :             CPLFree(paiOffset);
    2225           0 :             CPLFree(paiIncrement);
    2226           0 :             CPLFree(pszGeoList);
    2227           0 :             return FALSE;
    2228             :         }
    2229             : 
    2230           0 :         nXPoints = l_aiDimSizes[iXGeo];
    2231           0 :         nYPoints = l_aiDimSizes[iYGeo];
    2232             : 
    2233           0 :         if (EQUAL(szPixel, papszDimList[iXDim]))
    2234             :         {
    2235           0 :             iPixelDim = 1;
    2236           0 :             iLineDim = 0;
    2237             :         }
    2238             :         else
    2239             :         {
    2240           0 :             iPixelDim = 0;
    2241           0 :             iLineDim = 1;
    2242             :         }
    2243             : 
    2244           0 :         iDataSize = GetDataTypeSize(iWrkNumType);
    2245           0 :         if (strstr(papszGeolocations[i], "Latitude"))
    2246             :         {
    2247           0 :             iLatDim = i;
    2248           0 :             nLatCount = nXPoints * nYPoints;
    2249           0 :             pLat = CPLMalloc(nLatCount * iDataSize);
    2250           0 :             if (SWreadfield(hSW, papszGeolocations[i], nullptr, nullptr,
    2251           0 :                             nullptr, (VOIDP)pLat) < 0)
    2252             :             {
    2253           0 :                 CPLDebug("HDF4Image", "Can't read geolocation field %s",
    2254           0 :                          papszGeolocations[i]);
    2255           0 :                 CPLFree(pLat);
    2256           0 :                 pLat = nullptr;
    2257             :             }
    2258             :         }
    2259           0 :         else if (strstr(papszGeolocations[i], "Longitude"))
    2260             :         {
    2261           0 :             iLongDim = i;
    2262           0 :             nLongCount = nXPoints * nYPoints;
    2263           0 :             pLong = CPLMalloc(nLongCount * iDataSize);
    2264           0 :             if (SWreadfield(hSW, papszGeolocations[i], nullptr, nullptr,
    2265           0 :                             nullptr, (VOIDP)pLong) < 0)
    2266             :             {
    2267           0 :                 CPLDebug("HDF4Image", "Can't read geolocation field %s",
    2268           0 :                          papszGeolocations[i]);
    2269           0 :                 CPLFree(pLong);
    2270           0 :                 pLong = nullptr;
    2271             :             }
    2272             :         }
    2273             : 
    2274           0 :         CSLDestroy(papszGeoDimList);
    2275             :     }
    2276             : 
    2277             :     /* -------------------------------------------------------------------- */
    2278             :     /*      Do we have a lattice table?                                     */
    2279             :     /* -------------------------------------------------------------------- */
    2280           0 :     void *pLatticeX = nullptr;
    2281           0 :     void *pLatticeY = nullptr;
    2282           0 :     int32 iLatticeType = 0;
    2283           0 :     int32 iLatticeDataSize = 0;
    2284           0 :     char pszLatticePoint[] = "LatticePoint";
    2285           0 :     if (SWfieldinfo(hSW, pszLatticePoint, &l_iRank, l_aiDimSizes, &iLatticeType,
    2286           0 :                     szGeoDimList) == 0 &&
    2287           0 :         l_iRank == 3 && nXPoints == l_aiDimSizes[1] &&
    2288           0 :         nYPoints == l_aiDimSizes[0] && l_aiDimSizes[2] == 2)
    2289             :     {
    2290           0 :         iLatticeDataSize = GetDataTypeSize(iLatticeType);
    2291             : 
    2292           0 :         int32 iStart[H4_MAX_NC_DIMS] = {};
    2293           0 :         int32 iEdges[H4_MAX_NC_DIMS] = {};
    2294           0 :         iStart[1] = 0;
    2295           0 :         iEdges[1] = nXPoints;
    2296             : 
    2297           0 :         iStart[0] = 0;
    2298           0 :         iEdges[0] = nYPoints;
    2299             : 
    2300           0 :         iStart[2] = 0;
    2301           0 :         iEdges[2] = 1;
    2302             : 
    2303           0 :         pLatticeX = CPLMalloc(nLatCount * iLatticeDataSize);
    2304           0 :         if (SWreadfield(hSW, pszLatticePoint, iStart, nullptr, iEdges,
    2305           0 :                         (VOIDP)pLatticeX) < 0)
    2306             :         {
    2307           0 :             CPLDebug("HDF4Image", "Can't read lattice field");
    2308           0 :             CPLFree(pLatticeX);
    2309           0 :             pLatticeX = nullptr;
    2310             :         }
    2311             : 
    2312           0 :         iStart[2] = 1;
    2313           0 :         iEdges[2] = 1;
    2314             : 
    2315           0 :         pLatticeY = CPLMalloc(nLatCount * iLatticeDataSize);
    2316           0 :         if (SWreadfield(hSW, pszLatticePoint, iStart, nullptr, iEdges,
    2317           0 :                         (VOIDP)pLatticeY) < 0)
    2318             :         {
    2319           0 :             CPLDebug("HDF4Image", "Can't read lattice field");
    2320           0 :             CPLFree(pLatticeY);
    2321           0 :             pLatticeY = nullptr;
    2322             :         }
    2323             :     }
    2324             : 
    2325             :     /* -------------------------------------------------------------------- */
    2326             :     /*      Determine whether to use no, partial or full GCPs.              */
    2327             :     /* -------------------------------------------------------------------- */
    2328           0 :     const char *pszGEOL_AS_GCPS = CPLGetConfigOption("GEOL_AS_GCPS", "PARTIAL");
    2329           0 :     int iGCPStepX = 0;
    2330           0 :     int iGCPStepY = 0;
    2331             : 
    2332           0 :     if (EQUAL(pszGEOL_AS_GCPS, "NONE"))
    2333             :     {
    2334             :         // Leave as is: iGCPStepX = iGCPStepY = 0;
    2335             :     }
    2336           0 :     else if (EQUAL(pszGEOL_AS_GCPS, "FULL"))
    2337             :     {
    2338           0 :         iGCPStepX = 1;
    2339           0 :         iGCPStepY = 1;
    2340             :     }
    2341             :     else
    2342             :     {
    2343             :         // Aim for 10x10 grid or so.
    2344           0 :         iGCPStepX = std::max(static_cast<int32>(1), ((nXPoints - 1) / 11));
    2345           0 :         iGCPStepY = std::max(static_cast<int32>(1), ((nYPoints - 1) / 11));
    2346             :     }
    2347             : 
    2348             :     /* -------------------------------------------------------------------- */
    2349             :     /*  Fetch projection information for various datasets.                  */
    2350             :     /* -------------------------------------------------------------------- */
    2351           0 :     if (nLatCount && nLongCount && nLatCount == nLongCount && pLat && pLong)
    2352             :     {
    2353             :         // ASTER Level 1A
    2354           0 :         if (eProduct == PROD_ASTER_L1A)
    2355             :         {
    2356           0 :             m_oGCPSRS.importFromWkt(
    2357             :                 "GEOGCS[\"WGS 84\",DATUM[\"WGS_1984\",SPHEROID[\"WGS 84\","
    2358             :                 "6378137,298.257223563,AUTHORITY[\"EPSG\",\"7030\"]],"
    2359             :                 "TOWGS84[0,0,0,0,0,0,0],AUTHORITY[\"EPSG\",\"6326\"]],"
    2360             :                 "PRIMEM[\"Greenwich\",0,AUTHORITY[\"EPSG\",\"8901\"]],"
    2361             :                 "UNIT[\"degree\",0.0174532925199433,AUTHORITY[\"EPSG\","
    2362             :                 "\"9108\"]],AXIS[\"Lat\",NORTH],AXIS[\"Long\",EAST],"
    2363             :                 "AUTHORITY[\"EPSG\",\"4326\"]]");
    2364             :         }
    2365             : 
    2366             :         // ASTER Level 1B, Level 2
    2367           0 :         else if (eProduct == PROD_ASTER_L1B || eProduct == PROD_ASTER_L2)
    2368             :         {
    2369             :             // Construct the metadata keys.
    2370             :             // A band number is taken from the field name.
    2371           0 :             const char *pszBand = strpbrk(pszFieldName, "0123456789");
    2372             : 
    2373           0 :             if (!pszBand)
    2374           0 :                 pszBand = "";
    2375             : 
    2376           0 :             char *pszProjLine = CPLStrdup(CPLSPrintf("MPMETHOD%s", pszBand));
    2377             :             char *pszParamsLine =
    2378           0 :                 CPLStrdup(CPLSPrintf("PROJECTIONPARAMETERS%s", pszBand));
    2379           0 :             char *pszZoneLine = CPLStrdup(CPLSPrintf("UTMZONECODE%s", pszBand));
    2380             :             char *pszEllipsoidLine =
    2381           0 :                 CPLStrdup(CPLSPrintf("ELLIPSOIDANDDATUM%s", pszBand));
    2382             : 
    2383             :             // Fetch projection related values from the
    2384             :             // metadata.
    2385             :             const char *pszProj =
    2386           0 :                 CSLFetchNameValue(papszLocalMetadata, pszProjLine);
    2387             :             const char *pszParams =
    2388           0 :                 CSLFetchNameValue(papszLocalMetadata, pszParamsLine);
    2389             :             const char *pszZone =
    2390           0 :                 CSLFetchNameValue(papszLocalMetadata, pszZoneLine);
    2391             : 
    2392             : #ifdef DEBUG
    2393             :             const char *pszEllipsoid =
    2394           0 :                 CSLFetchNameValue(papszLocalMetadata, pszEllipsoidLine);
    2395             : 
    2396           0 :             CPLDebug("HDF4Image",
    2397             :                      "Projection %s=%s, parameters %s=%s, "
    2398             :                      "zone %s=%s",
    2399             :                      pszProjLine, pszProj, pszParamsLine, pszParams,
    2400             :                      pszZoneLine, pszZone);
    2401           0 :             CPLDebug("HDF4Image", "Ellipsoid %s=%s", pszEllipsoidLine,
    2402             :                      pszEllipsoid);
    2403             : #endif
    2404             : 
    2405             :             // Transform all mnemonic codes in the values.
    2406             :             // Projection is UTM by default
    2407           0 :             const long iProjSys = pszProj ? USGSMnemonicToCode(pszProj) : 1L;
    2408           0 :             const long iZone = (pszZone && iProjSys == 1L) ? atoi(pszZone) : 0L;
    2409             : #if 0  // Not needed without the WGS84 check.
    2410             :             char **papszEllipsoid = (pszEllipsoid) ?
    2411             :                 CSLTokenizeString2( pszEllipsoid, ",",
    2412             :                                     CSLT_HONOURSTRINGS ) : NULL;
    2413             : #endif
    2414             : 
    2415           0 :             const long iEllipsoid = 8L;  // WGS84 by default
    2416             : #if 0                                    // This block is redundant.
    2417             :             if( papszEllipsoid && CSLCount(papszEllipsoid) > 0 )
    2418             :             {
    2419             :                 if (EQUAL( papszEllipsoid[0], "WGS84"))
    2420             :                     iEllipsoid = 8L;
    2421             :             }
    2422             : #endif
    2423             :             char **papszParams =
    2424             :                 pszParams
    2425           0 :                     ? CSLTokenizeString2(pszParams, ",", CSLT_HONOURSTRINGS)
    2426           0 :                     : nullptr;
    2427           0 :             std::vector<double> adfProjParams(15);
    2428           0 :             if (papszParams)
    2429             :             {
    2430           0 :                 for (int i = 0; i < 15 && papszParams[i] != nullptr; i++)
    2431           0 :                     adfProjParams[i] = CPLAtof(papszParams[i]);
    2432             :             }
    2433             : 
    2434             :             // Create projection definition
    2435           0 :             m_oSRS.importFromUSGS(iProjSys, iZone, adfProjParams.data(),
    2436             :                                   iEllipsoid);
    2437           0 :             m_oSRS.SetLinearUnits(SRS_UL_METER, 1.0);
    2438             : 
    2439           0 :             CSLDestroy(papszParams);
    2440           0 :             CPLFree(pszEllipsoidLine);
    2441           0 :             CPLFree(pszZoneLine);
    2442           0 :             CPLFree(pszParamsLine);
    2443           0 :             CPLFree(pszProjLine);
    2444             :         }
    2445             : 
    2446             :         // ASTER Level 3 (DEM)
    2447           0 :         else if (eProduct == PROD_ASTER_L3)
    2448             :         {
    2449           0 :             double dfCenterX = 0.0;
    2450           0 :             double dfCenterY = 0.0;
    2451             : 
    2452           0 :             ReadCoordinates(
    2453           0 :                 CSLFetchNameValue(papszGlobalMetadata, "SCENECENTER"),
    2454             :                 &dfCenterY, &dfCenterX);
    2455             : 
    2456             :             // Calculate UTM zone from scene center coordinates
    2457           0 :             const int iZone = 30 + static_cast<int>((dfCenterX + 6.0) / 6.0);
    2458             : 
    2459             :             // Create projection definition
    2460           0 :             if (dfCenterY > 0)
    2461           0 :                 m_oSRS.SetUTM(iZone, TRUE);
    2462             :             else
    2463           0 :                 m_oSRS.SetUTM(-iZone, FALSE);
    2464           0 :             m_oSRS.SetWellKnownGeogCS("WGS84");
    2465           0 :             m_oSRS.SetLinearUnits(SRS_UL_METER, 1.0);
    2466             :         }
    2467             : 
    2468             :         // MODIS L1B
    2469           0 :         else if (eProduct == PROD_MODIS_L1B || eProduct == PROD_MODIS_L2)
    2470             :         {
    2471           0 :             m_oGCPSRS.importFromWkt(SRS_WKT_WGS84_LAT_LONG);
    2472             :         }
    2473             : 
    2474             :         /* --------------------------------------------------------------------
    2475             :          */
    2476             :         /*  Fill the GCPs list. */
    2477             :         /* --------------------------------------------------------------------
    2478             :          */
    2479           0 :         if (iGCPStepX > 0)
    2480             :         {
    2481           0 :             for (int i = 0; i < nYPoints; i += iGCPStepY)
    2482             :             {
    2483           0 :                 for (int j = 0; j < nXPoints; j += iGCPStepX)
    2484             :                 {
    2485           0 :                     const int iGeoOff = i * nXPoints + j;
    2486             : 
    2487           0 :                     double dfGCPX = AnyTypeToDouble(
    2488             :                         iWrkNumType, reinterpret_cast<void *>(
    2489             :                                          reinterpret_cast<char *>(pLong) +
    2490           0 :                                          iGeoOff * iDataSize));
    2491           0 :                     double dfGCPY = AnyTypeToDouble(
    2492             :                         iWrkNumType, reinterpret_cast<void *>(
    2493             :                                          reinterpret_cast<char *>(pLat) +
    2494           0 :                                          iGeoOff * iDataSize));
    2495             : 
    2496             :                     // GCPs in Level 1A/1B dataset are in geocentric
    2497             :                     // coordinates. Convert them in geodetic (we
    2498             :                     // will convert latitudes only, longitudes
    2499             :                     // do not need to be converted, because
    2500             :                     // they are the same).
    2501             :                     // This calculation valid for WGS84 datum only.
    2502           0 :                     if (eProduct == PROD_ASTER_L1A ||
    2503             :                         eProduct == PROD_ASTER_L1B)
    2504             :                     {
    2505           0 :                         dfGCPY = atan(tan(dfGCPY * PI / 180) / 0.99330562) *
    2506           0 :                                  180 / PI;
    2507             :                     }
    2508             : 
    2509           0 :                     ToGeoref(&dfGCPX, &dfGCPY);
    2510             : 
    2511           0 :                     double dfGCPPixel = 0.0;
    2512           0 :                     double dfGCPLine = 0.0;
    2513           0 :                     if (pLatticeX && pLatticeY)
    2514             :                     {
    2515           0 :                         dfGCPPixel =
    2516           0 :                             AnyTypeToDouble(
    2517             :                                 iLatticeType,
    2518             :                                 reinterpret_cast<void *>(
    2519             :                                     reinterpret_cast<char *>(pLatticeX) +
    2520           0 :                                     iGeoOff * iLatticeDataSize)) +
    2521             :                             0.5;
    2522           0 :                         dfGCPLine =
    2523           0 :                             AnyTypeToDouble(
    2524             :                                 iLatticeType,
    2525             :                                 reinterpret_cast<void *>(
    2526             :                                     reinterpret_cast<char *>(pLatticeY) +
    2527           0 :                                     iGeoOff * iLatticeDataSize)) +
    2528             :                             0.5;
    2529             :                     }
    2530           0 :                     else if (paiOffset && paiIncrement)
    2531             :                     {
    2532           0 :                         dfGCPPixel = paiOffset[iPixelDim] +
    2533           0 :                                      j * paiIncrement[iPixelDim] + 0.5;
    2534           0 :                         dfGCPLine = paiOffset[iLineDim] +
    2535           0 :                                     i * paiIncrement[iLineDim] + 0.5;
    2536             :                     }
    2537             : 
    2538             :                     m_aoGCPs.emplace_back("", "", dfGCPPixel, dfGCPLine, dfGCPX,
    2539           0 :                                           dfGCPY);
    2540             :                 }
    2541             :             }
    2542             :         }
    2543             : 
    2544             :         /* --------------------------------------------------------------------
    2545             :          */
    2546             :         /*      Establish geolocation metadata, but only if there is no */
    2547             :         /*      lattice.  The lattice destroys the regularity of the grid. */
    2548             :         /* --------------------------------------------------------------------
    2549             :          */
    2550           0 :         if (pLatticeX == nullptr && iLatDim != -1 && iLongDim != -1 &&
    2551           0 :             iPixelDim != -1 && iLineDim != -1)
    2552             :         {
    2553           0 :             char *pszWKT = nullptr;
    2554           0 :             m_oGCPSRS.exportToWkt(&pszWKT);
    2555           0 :             if (pszWKT)
    2556           0 :                 SetMetadataItem("SRS", pszWKT, "GEOLOCATION");
    2557           0 :             CPLFree(pszWKT);
    2558             : 
    2559           0 :             CPLString osWrk;
    2560             :             osWrk.Printf("HDF4_EOS:EOS_SWATH_GEOL:\"%s\":%s:%s", pszFilename,
    2561           0 :                          pszSubdatasetName, papszGeolocations[iLongDim]);
    2562           0 :             SetMetadataItem("X_DATASET", osWrk, "GEOLOCATION");
    2563           0 :             SetMetadataItem("X_BAND", "1", "GEOLOCATION");
    2564             : 
    2565             :             osWrk.Printf("HDF4_EOS:EOS_SWATH_GEOL:\"%s\":%s:%s", pszFilename,
    2566           0 :                          pszSubdatasetName, papszGeolocations[iLatDim]);
    2567           0 :             SetMetadataItem("Y_DATASET", osWrk, "GEOLOCATION");
    2568           0 :             SetMetadataItem("Y_BAND", "1", "GEOLOCATION");
    2569             : 
    2570           0 :             if (paiOffset && paiIncrement)
    2571             :             {
    2572           0 :                 osWrk.Printf("%ld", static_cast<long>(paiOffset[iPixelDim]));
    2573           0 :                 SetMetadataItem("PIXEL_OFFSET", osWrk, "GEOLOCATION");
    2574           0 :                 osWrk.Printf("%ld", static_cast<long>(paiIncrement[iPixelDim]));
    2575           0 :                 SetMetadataItem("PIXEL_STEP", osWrk, "GEOLOCATION");
    2576             : 
    2577           0 :                 osWrk.Printf("%ld", static_cast<long>(paiOffset[iLineDim]));
    2578           0 :                 SetMetadataItem("LINE_OFFSET", osWrk, "GEOLOCATION");
    2579           0 :                 osWrk.Printf("%ld", static_cast<long>(paiIncrement[iLineDim]));
    2580           0 :                 SetMetadataItem("LINE_STEP", osWrk, "GEOLOCATION");
    2581             :             }
    2582             :         }
    2583             : 
    2584             :         /* --------------------------------------------------------------------
    2585             :          */
    2586             :         /*      Cleanup */
    2587             :         /* --------------------------------------------------------------------
    2588             :          */
    2589           0 :         CPLFree(pLatticeX);
    2590           0 :         CPLFree(pLatticeY);
    2591           0 :         CPLFree(pLat);
    2592           0 :         CPLFree(pLong);
    2593             : 
    2594           0 :         if (iGCPStepX == 0)
    2595             :         {
    2596           0 :             m_oGCPSRS.Clear();
    2597             :         }
    2598             :     }
    2599             : 
    2600             :     /* -------------------------------------------------------------------- */
    2601             :     /*      Cleanup                                                         */
    2602             :     /* -------------------------------------------------------------------- */
    2603           0 :     CPLFree(paiOffset);
    2604           0 :     CPLFree(paiIncrement);
    2605           0 :     CPLFree(pszGeoList);
    2606           0 :     CSLDestroy(papszGeolocations);
    2607             : 
    2608           0 :     return TRUE;
    2609             : }
    2610             : 
    2611             : /************************************************************************/
    2612             : /*                                Open()                                */
    2613             : /************************************************************************/
    2614             : 
    2615         301 : GDALDataset *HDF4ImageDataset::Open(GDALOpenInfo *poOpenInfo)
    2616             : {
    2617         301 :     if (!HDF4ImageDatasetIdentify(poOpenInfo))
    2618           0 :         return nullptr;
    2619             : 
    2620             :     /* -------------------------------------------------------------------- */
    2621             :     /*      Create a corresponding GDALDataset.                             */
    2622             :     /* -------------------------------------------------------------------- */
    2623         301 :     if (poOpenInfo->fpL != nullptr)
    2624             :     {
    2625           0 :         VSIFCloseL(poOpenInfo->fpL);
    2626           0 :         poOpenInfo->fpL = nullptr;
    2627             :     }
    2628             : 
    2629         301 :     HDF4ImageDataset *poDS = new HDF4ImageDataset();
    2630         602 :     CPLMutexHolderD(&hHDF4Mutex);
    2631             : 
    2632         602 :     char **papszSubdatasetName = CSLTokenizeString2(
    2633         301 :         poOpenInfo->pszFilename, ":",
    2634             :         CSLT_HONOURSTRINGS | CSLT_PRESERVEQUOTES | CSLT_PRESERVEESCAPES);
    2635         301 :     if (CSLCount(papszSubdatasetName) != 4 &&
    2636         301 :         CSLCount(papszSubdatasetName) != 5 &&
    2637           0 :         CSLCount(papszSubdatasetName) != 6)
    2638             :     {
    2639           0 :         CSLDestroy(papszSubdatasetName);
    2640             :         // Release mutex otherwise we deadlock with GDALDataset own mutex.
    2641           0 :         CPLReleaseMutex(hHDF4Mutex);
    2642           0 :         delete poDS;
    2643           0 :         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    2644           0 :         return nullptr;
    2645             :     }
    2646             : 
    2647             :     {
    2648             :         // Un-quote filename
    2649         301 :         size_t nLenPart2 = strlen(papszSubdatasetName[2]);
    2650         301 :         if (papszSubdatasetName[2][0] == '"' &&
    2651         301 :             papszSubdatasetName[2][nLenPart2 - 1] == '"')
    2652             :         {
    2653         301 :             papszSubdatasetName[2][nLenPart2 - 1] = 0;
    2654         301 :             memmove(papszSubdatasetName[2], papszSubdatasetName[2] + 1,
    2655             :                     nLenPart2 - 1);
    2656             :         }
    2657             :     }
    2658             : 
    2659             :     /* -------------------------------------------------------------------- */
    2660             :     /*    Check for drive name in windows HDF4_xx:TYPE:D:\...               */
    2661             :     /* -------------------------------------------------------------------- */
    2662         301 :     if (strlen(papszSubdatasetName[2]) == 1)
    2663             :     {
    2664           0 :         const size_t nLen = 2 + strlen(papszSubdatasetName[3]) + 1;
    2665           0 :         char *pszFilename = reinterpret_cast<char *>(CPLMalloc(nLen));
    2666           0 :         snprintf(pszFilename, nLen, "%s:%s", papszSubdatasetName[2],
    2667           0 :                  papszSubdatasetName[3]);
    2668           0 :         CPLFree(papszSubdatasetName[2]);
    2669           0 :         CPLFree(papszSubdatasetName[3]);
    2670           0 :         papszSubdatasetName[2] = pszFilename;
    2671             : 
    2672             :         /* Move following arguments one rank upper */
    2673           0 :         papszSubdatasetName[3] = papszSubdatasetName[4];
    2674           0 :         if (papszSubdatasetName[4] != nullptr)
    2675             :         {
    2676           0 :             papszSubdatasetName[4] = papszSubdatasetName[5];
    2677           0 :             papszSubdatasetName[5] = nullptr;
    2678             :         }
    2679             :     }
    2680             : 
    2681         602 :     for (int i = 3; papszSubdatasetName[i] != nullptr; i++)
    2682             :     {
    2683             :         // Un-quote and unescape components after filename
    2684         301 :         size_t nLenPart = strlen(papszSubdatasetName[i]);
    2685         301 :         if (papszSubdatasetName[i][0] == '"' &&
    2686           0 :             papszSubdatasetName[i][nLenPart - 1] == '"')
    2687             :         {
    2688           0 :             CPLString osStr(papszSubdatasetName[i]);
    2689           0 :             osStr.replaceAll("\\\\", '\\');
    2690           0 :             osStr.replaceAll("\\\"", '"');
    2691           0 :             if (osStr[0] == '"' && osStr.back() == '"')
    2692             :             {
    2693           0 :                 osStr = osStr.substr(1, osStr.size() - 2);
    2694           0 :                 CPLFree(papszSubdatasetName[i]);
    2695           0 :                 papszSubdatasetName[i] = CPLStrdup(osStr);
    2696             :             }
    2697             :         }
    2698             :     }
    2699             : 
    2700         301 :     poDS->pszFilename = CPLStrdup(papszSubdatasetName[2]);
    2701             : 
    2702         301 :     if (EQUAL(papszSubdatasetName[0], "HDF4_SDS"))
    2703         299 :         poDS->iDatasetType = HDF4_SDS;
    2704           2 :     else if (EQUAL(papszSubdatasetName[0], "HDF4_GR"))
    2705           2 :         poDS->iDatasetType = HDF4_GR;
    2706           0 :     else if (EQUAL(papszSubdatasetName[0], "HDF4_EOS"))
    2707           0 :         poDS->iDatasetType = HDF4_EOS;
    2708             :     else
    2709           0 :         poDS->iDatasetType = HDF4_UNKNOWN;
    2710             : 
    2711         301 :     if (EQUAL(papszSubdatasetName[1], "GDAL_HDF4"))
    2712         299 :         poDS->iSubdatasetType = H4ST_GDAL;
    2713           2 :     else if (EQUAL(papszSubdatasetName[1], "EOS_GRID"))
    2714           0 :         poDS->iSubdatasetType = H4ST_EOS_GRID;
    2715           2 :     else if (EQUAL(papszSubdatasetName[1], "EOS_SWATH"))
    2716           0 :         poDS->iSubdatasetType = H4ST_EOS_SWATH;
    2717           2 :     else if (EQUAL(papszSubdatasetName[1], "EOS_SWATH_GEOL"))
    2718           0 :         poDS->iSubdatasetType = H4ST_EOS_SWATH_GEOL;
    2719           2 :     else if (EQUAL(papszSubdatasetName[1], "SEAWIFS_L3"))
    2720           0 :         poDS->iSubdatasetType = H4ST_SEAWIFS_L3;
    2721           2 :     else if (EQUAL(papszSubdatasetName[1], "HYPERION_L1"))
    2722           0 :         poDS->iSubdatasetType = H4ST_HYPERION_L1;
    2723             :     else
    2724           2 :         poDS->iSubdatasetType = H4ST_UNKNOWN;
    2725             : 
    2726             :     /* -------------------------------------------------------------------- */
    2727             :     /*      Is our file still here?                                         */
    2728             :     /* -------------------------------------------------------------------- */
    2729         301 :     if (!Hishdf(poDS->pszFilename))
    2730             :     {
    2731           0 :         CSLDestroy(papszSubdatasetName);
    2732             :         // Release mutex otherwise we deadlock with GDALDataset own mutex.
    2733           0 :         CPLReleaseMutex(hHDF4Mutex);
    2734           0 :         delete poDS;
    2735           0 :         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    2736           0 :         return nullptr;
    2737             :     }
    2738             : 
    2739             :     /* -------------------------------------------------------------------- */
    2740             :     /*      Collect the remain (post filename) components to treat as       */
    2741             :     /*      the subdataset name.                                            */
    2742             :     /* -------------------------------------------------------------------- */
    2743         602 :     CPLString osSubdatasetName = papszSubdatasetName[3];
    2744         301 :     if (papszSubdatasetName[4] != nullptr)
    2745             :     {
    2746           0 :         osSubdatasetName += ":";
    2747           0 :         osSubdatasetName += papszSubdatasetName[4];
    2748             :     }
    2749             : 
    2750             :     /* -------------------------------------------------------------------- */
    2751             :     /*      Try opening the dataset.                                        */
    2752             :     /* -------------------------------------------------------------------- */
    2753         301 :     double dfNoData = 0.0;
    2754         301 :     double dfScale = 1.0;
    2755         301 :     double dfOffset = 0.0;
    2756         301 :     bool bNoDataSet = false;
    2757         301 :     bool bHaveScale = false;
    2758         301 :     bool bHaveOffset = false;
    2759         301 :     const char *pszUnits = nullptr;
    2760         301 :     const char *pszDescription = nullptr;
    2761             : 
    2762             :     /* -------------------------------------------------------------------- */
    2763             :     /*      Select SDS or GR to read from.                                  */
    2764             :     /* -------------------------------------------------------------------- */
    2765         301 :     if (poDS->iDatasetType == HDF4_EOS)
    2766             :     {
    2767           0 :         if (papszSubdatasetName[4] == nullptr)
    2768             :         {
    2769             :             // Release mutex.  Otherwise it will deadlock with GDALDataset's own
    2770             :             // mutex.
    2771           0 :             CPLReleaseMutex(hHDF4Mutex);
    2772           0 :             delete poDS;
    2773           0 :             CPLAcquireMutex(hHDF4Mutex, 1000.0);
    2774           0 :             return nullptr;
    2775             :         }
    2776           0 :         poDS->pszSubdatasetName = CPLStrdup(papszSubdatasetName[3]);
    2777           0 :         poDS->pszFieldName = CPLStrdup(papszSubdatasetName[4]);
    2778             :     }
    2779             :     else
    2780             :     {
    2781         301 :         CPLAssert(papszSubdatasetName[3]);
    2782         301 :         poDS->iDataset = atoi(papszSubdatasetName[3]);
    2783             :     }
    2784         301 :     CSLDestroy(papszSubdatasetName);
    2785             : 
    2786         301 :     switch (poDS->iDatasetType)
    2787             :     {
    2788           0 :         case HDF4_EOS:
    2789             :         {
    2790           0 :             switch (poDS->iSubdatasetType)
    2791             :             {
    2792             : 
    2793             :                     /* --------------------------------------------------------------------
    2794             :                      */
    2795             :                     /*  HDF-EOS Swath. */
    2796             :                     /* --------------------------------------------------------------------
    2797             :                      */
    2798           0 :                 case H4ST_EOS_SWATH:
    2799             :                 case H4ST_EOS_SWATH_GEOL:
    2800             :                 {
    2801           0 :                     if (poOpenInfo->eAccess == GA_ReadOnly)
    2802           0 :                         poDS->hHDF4 = SWopen(poDS->pszFilename, DFACC_READ);
    2803             :                     else
    2804           0 :                         poDS->hHDF4 = SWopen(poDS->pszFilename, DFACC_WRITE);
    2805             : 
    2806           0 :                     if (poDS->hHDF4 <= 0)
    2807             :                     {
    2808           0 :                         CPLDebug("HDF4Image",
    2809             :                                  "Can't open file \"%s\" for swath reading",
    2810             :                                  poDS->pszFilename);
    2811             :                         // Release mutex otherwise we deadlock with GDALDataset
    2812             :                         // own mutex.
    2813           0 :                         CPLReleaseMutex(hHDF4Mutex);
    2814           0 :                         delete poDS;
    2815           0 :                         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    2816           0 :                         return nullptr;
    2817             :                     }
    2818             : 
    2819             :                     const int32 hSW =
    2820           0 :                         SWattach(poDS->hHDF4, poDS->pszSubdatasetName);
    2821           0 :                     if (hSW < 0)
    2822             :                     {
    2823           0 :                         CPLDebug("HDF4Image", "Can't attach to subdataset %s",
    2824             :                                  poDS->pszSubdatasetName);
    2825             :                         // Release mutex otherwise we deadlock with GDALDataset
    2826             :                         // own mutex.
    2827           0 :                         CPLReleaseMutex(hHDF4Mutex);
    2828           0 :                         delete poDS;
    2829           0 :                         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    2830           0 :                         return nullptr;
    2831             :                     }
    2832             : 
    2833             :                     /* --------------------------------------------------------------------
    2834             :                      */
    2835             :                     /*      Decode the dimension map. */
    2836             :                     /* --------------------------------------------------------------------
    2837             :                      */
    2838           0 :                     int32 nStrBufSize = 0;
    2839             : 
    2840           0 :                     if (SWnentries(hSW, HDFE_NENTDIM, &nStrBufSize) < 0 ||
    2841           0 :                         nStrBufSize <= 0)
    2842             :                     {
    2843           0 :                         CPLDebug("HDF4Image",
    2844             :                                  "Can't read a number of dimension maps.");
    2845             :                         // Release mutex otherwise we deadlock with GDALDataset
    2846             :                         // own mutex.
    2847           0 :                         CPLReleaseMutex(hHDF4Mutex);
    2848           0 :                         delete poDS;
    2849           0 :                         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    2850           0 :                         return nullptr;
    2851             :                     }
    2852             : 
    2853             :                     char *pszDimList =
    2854           0 :                         reinterpret_cast<char *>(CPLMalloc(nStrBufSize + 1));
    2855           0 :                     if (SWfieldinfo(hSW, poDS->pszFieldName, &poDS->iRank,
    2856           0 :                                     poDS->aiDimSizes, &poDS->iNumType,
    2857           0 :                                     pszDimList) < 0)
    2858             :                     {
    2859           0 :                         CPLDebug("HDF4Image", "Can't read dimension maps.");
    2860           0 :                         CPLFree(pszDimList);
    2861             :                         // Release mutex otherwise we deadlock with GDALDataset
    2862             :                         // own mutex.
    2863           0 :                         CPLReleaseMutex(hHDF4Mutex);
    2864           0 :                         delete poDS;
    2865           0 :                         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    2866           0 :                         return nullptr;
    2867             :                     }
    2868           0 :                     pszDimList[nStrBufSize] = '\0';
    2869             : 
    2870             : #ifdef DEBUG
    2871           0 :                     CPLDebug("HDF4Image",
    2872             :                              "List of dimensions in swath \"%s\": %s",
    2873             :                              poDS->pszFieldName, pszDimList);
    2874             : #endif
    2875             : 
    2876           0 :                     poDS->GetImageDimensions(pszDimList);
    2877             : 
    2878             : #ifdef DEBUG
    2879           0 :                     CPLDebug("HDF4Image",
    2880             :                              "X dimension is %d, Y dimension is %d",
    2881             :                              poDS->iXDim, poDS->iYDim);
    2882             : #endif
    2883             : 
    2884             :                     /* --------------------------------------------------------------------
    2885             :                      */
    2886             :                     /*  Fetch metadata. */
    2887             :                     /* --------------------------------------------------------------------
    2888             :                      */
    2889             :                     // Not H4ST_EOS_SWATH_GEOL.
    2890           0 :                     if (poDS->iSubdatasetType == H4ST_EOS_SWATH)
    2891           0 :                         poDS->GetSwatAttrs(hSW);
    2892             : 
    2893             :                     /* --------------------------------------------------------------------
    2894             :                      */
    2895             :                     /*  Fetch NODATA value. */
    2896             :                     /* --------------------------------------------------------------------
    2897             :                      */
    2898             :                     void *pNoDataValue =
    2899           0 :                         CPLMalloc(poDS->GetDataTypeSize(poDS->iNumType));
    2900           0 :                     if (SWgetfillvalue(hSW, poDS->pszFieldName, pNoDataValue) !=
    2901             :                         -1)
    2902             :                     {
    2903             :                         dfNoData =
    2904           0 :                             poDS->AnyTypeToDouble(poDS->iNumType, pNoDataValue);
    2905           0 :                         bNoDataSet = true;
    2906             :                     }
    2907             :                     else
    2908             :                     {
    2909           0 :                         const char *pszNoData = CSLFetchNameValue(
    2910           0 :                             poDS->papszLocalMetadata, "_FillValue");
    2911           0 :                         if (pszNoData)
    2912             :                         {
    2913           0 :                             dfNoData = CPLAtof(pszNoData);
    2914           0 :                             bNoDataSet = true;
    2915             :                         }
    2916             :                     }
    2917           0 :                     CPLFree(pNoDataValue);
    2918             : 
    2919             :                     /* --------------------------------------------------------------------
    2920             :                      */
    2921             :                     /*      Handle Geolocation processing. */
    2922             :                     /* --------------------------------------------------------------------
    2923             :                      */
    2924             :                     // Not H4ST_SWATH_GEOL.
    2925           0 :                     if (poDS->iSubdatasetType == H4ST_EOS_SWATH)
    2926             :                     {
    2927           0 :                         char **papszDimList = CSLTokenizeString2(
    2928             :                             pszDimList, ",", CSLT_HONOURSTRINGS);
    2929           0 :                         if (!poDS->ProcessSwathGeolocation(hSW, papszDimList))
    2930             :                         {
    2931           0 :                             CPLDebug(
    2932             :                                 "HDF4Image",
    2933             :                                 "No geolocation available for this swath.");
    2934             :                         }
    2935           0 :                         CSLDestroy(papszDimList);
    2936             :                     }
    2937             : 
    2938             :                     /* --------------------------------------------------------------------
    2939             :                      */
    2940             :                     /*      Cleanup. */
    2941             :                     /* --------------------------------------------------------------------
    2942             :                      */
    2943           0 :                     CPLFree(pszDimList);
    2944           0 :                     SWdetach(hSW);
    2945             :                 }
    2946           0 :                 break;
    2947             : 
    2948             :                     /* --------------------------------------------------------------------
    2949             :                      */
    2950             :                     /*      HDF-EOS Grid. */
    2951             :                     /* --------------------------------------------------------------------
    2952             :                      */
    2953           0 :                 case H4ST_EOS_GRID:
    2954             :                 {
    2955           0 :                     if (poOpenInfo->eAccess == GA_ReadOnly)
    2956           0 :                         poDS->hHDF4 = GDopen(poDS->pszFilename, DFACC_READ);
    2957             :                     else
    2958           0 :                         poDS->hHDF4 = GDopen(poDS->pszFilename, DFACC_WRITE);
    2959             : 
    2960           0 :                     if (poDS->hHDF4 <= 0)
    2961             :                     {
    2962           0 :                         CPLDebug("HDF4Image",
    2963             :                                  "Can't open file \"%s\" for grid reading",
    2964             :                                  poDS->pszFilename);
    2965             :                         // Release mutex otherwise we deadlock with GDALDataset
    2966             :                         // own mutex.
    2967           0 :                         CPLReleaseMutex(hHDF4Mutex);
    2968           0 :                         delete poDS;
    2969           0 :                         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    2970           0 :                         return nullptr;
    2971             :                     }
    2972             : 
    2973             :                     const int32 hGD =
    2974           0 :                         GDattach(poDS->hHDF4, poDS->pszSubdatasetName);
    2975             : 
    2976             :                     /* --------------------------------------------------------------------
    2977             :                      */
    2978             :                     /*      Decode the dimension map. */
    2979             :                     /* --------------------------------------------------------------------
    2980             :                      */
    2981           0 :                     char szDimList[N_BUF_SIZE] = {};
    2982           0 :                     GDfieldinfo(hGD, poDS->pszFieldName, &poDS->iRank,
    2983           0 :                                 poDS->aiDimSizes, &poDS->iNumType, szDimList);
    2984             : #ifdef DEBUG
    2985           0 :                     CPLDebug("HDF4Image", "List of dimensions in grid %s: %s",
    2986             :                              poDS->pszFieldName, szDimList);
    2987             : #endif
    2988           0 :                     poDS->GetImageDimensions(szDimList);
    2989             : 
    2990           0 :                     int32 tilecode = 0;
    2991           0 :                     int32 tilerank = 0;
    2992           0 :                     if (GDtileinfo(hGD, poDS->pszFieldName, &tilecode,
    2993           0 :                                    &tilerank, nullptr) == 0)
    2994             :                     {
    2995           0 :                         if (tilecode == HDFE_TILE)
    2996             :                         {
    2997             :                             int32 *tiledims = reinterpret_cast<int32 *>(
    2998           0 :                                 CPLCalloc(tilerank, sizeof(int32)));
    2999           0 :                             GDtileinfo(hGD, poDS->pszFieldName, &tilecode,
    3000             :                                        &tilerank, tiledims);
    3001           0 :                             if ((tilerank == 2) && (poDS->iRank == tilerank))
    3002             :                             {
    3003           0 :                                 poDS->nBlockPreferredXSize = tiledims[1];
    3004           0 :                                 poDS->nBlockPreferredYSize = tiledims[0];
    3005           0 :                                 poDS->bReadTile = true;
    3006             : #ifdef DEBUG
    3007           0 :                                 CPLDebug("HDF4_EOS:EOS_GRID:",
    3008             :                                          "tilerank in grid %s: %d",
    3009             :                                          poDS->pszFieldName,
    3010             :                                          static_cast<int>(tilerank));
    3011           0 :                                 CPLDebug("HDF4_EOS:EOS_GRID:",
    3012             :                                          "tiledimens in grid %s: %d,%d",
    3013             :                                          poDS->pszFieldName,
    3014             :                                          static_cast<int>(tiledims[0]),
    3015           0 :                                          static_cast<int>(tiledims[1]));
    3016             : #endif
    3017             :                             }
    3018             : #ifdef DEBUG
    3019             :                             else
    3020             :                             {
    3021           0 :                                 CPLDebug(
    3022             :                                     "HDF4_EOS:EOS_GRID:",
    3023             :                                     "tilerank in grid %s: %d not supported",
    3024             :                                     poDS->pszFieldName,
    3025             :                                     static_cast<int>(tilerank));
    3026             :                             }
    3027             : #endif
    3028           0 :                             CPLFree(tiledims);
    3029             :                         }
    3030             :                         else
    3031             :                         {
    3032             : #ifdef DEBUG
    3033           0 :                             CPLDebug("HDF4_EOS:EOS_GRID:",
    3034             :                                      "tilecode == HDFE_NOTILE in grid %s: %d",
    3035             :                                      poDS->pszFieldName,
    3036           0 :                                      static_cast<int>(poDS->iRank));
    3037             : #endif
    3038             :                         }
    3039             :                     }
    3040             : 
    3041             : #ifdef DEBUG
    3042             :                     else
    3043             :                     {
    3044           0 :                         CPLDebug("HDF4_EOS:EOS_GRID:", "ERROR GDtileinfo %s",
    3045             :                                  poDS->pszFieldName);
    3046             :                     }
    3047             : #endif
    3048             : 
    3049             :                     /* --------------------------------------------------------------------
    3050             :                      */
    3051             :                     /*      Fetch projection information */
    3052             :                     /* --------------------------------------------------------------------
    3053             :                      */
    3054           0 :                     int32 iProjCode = 0;
    3055           0 :                     int32 iZoneCode = 0;
    3056           0 :                     int32 iSphereCode = 0;
    3057             :                     double adfProjParams[15];
    3058             : 
    3059           0 :                     if (GDprojinfo(hGD, &iProjCode, &iZoneCode, &iSphereCode,
    3060           0 :                                    adfProjParams) >= 0)
    3061             :                     {
    3062             : #ifdef DEBUG
    3063           0 :                         CPLDebug("HDF4Image",
    3064             :                                  "Grid projection: "
    3065             :                                  "projection code: %ld, zone code %ld, "
    3066             :                                  "sphere code %ld",
    3067             :                                  static_cast<long>(iProjCode),
    3068             :                                  static_cast<long>(iZoneCode),
    3069             :                                  static_cast<long>(iSphereCode));
    3070             : #endif
    3071           0 :                         poDS->m_oSRS.importFromUSGS(iProjCode, iZoneCode,
    3072             :                                                     adfProjParams, iSphereCode,
    3073             :                                                     USGS_ANGLE_RADIANS);
    3074             :                     }
    3075             : 
    3076             :                     /* --------------------------------------------------------------------
    3077             :                      */
    3078             :                     /*      Fetch geotransformation matrix */
    3079             :                     /* --------------------------------------------------------------------
    3080             :                      */
    3081           0 :                     int32 nXSize = 0;
    3082           0 :                     int32 nYSize = 0;
    3083             :                     double adfUpLeft[2];
    3084             :                     double adfLowRight[2];
    3085           0 :                     if (GDgridinfo(hGD, &nXSize, &nYSize, adfUpLeft,
    3086           0 :                                    adfLowRight) >= 0)
    3087             :                     {
    3088             : #ifdef DEBUG
    3089           0 :                         CPLDebug("HDF4Image",
    3090             :                                  "Grid geolocation: "
    3091             :                                  "top left X %f, top left Y %f, "
    3092             :                                  "low right X %f, low right Y %f, "
    3093             :                                  "cols %ld, rows %ld",
    3094             :                                  adfUpLeft[0], adfUpLeft[1], adfLowRight[0],
    3095             :                                  adfLowRight[1], static_cast<long>(nXSize),
    3096             :                                  static_cast<long>(nYSize));
    3097             : #endif
    3098           0 :                         if (iProjCode)
    3099             :                         {
    3100             :                             // For projected systems coordinates are in meters.
    3101           0 :                             poDS->adfGeoTransform[1] =
    3102           0 :                                 (adfLowRight[0] - adfUpLeft[0]) / nXSize;
    3103           0 :                             poDS->adfGeoTransform[5] =
    3104           0 :                                 (adfLowRight[1] - adfUpLeft[1]) / nYSize;
    3105           0 :                             poDS->adfGeoTransform[0] = adfUpLeft[0];
    3106           0 :                             poDS->adfGeoTransform[3] = adfUpLeft[1];
    3107             :                         }
    3108             :                         else
    3109             :                         {
    3110             :                             // Handle angular geographic coordinates here.
    3111           0 :                             poDS->adfGeoTransform[1] =
    3112           0 :                                 (CPLPackedDMSToDec(adfLowRight[0]) -
    3113           0 :                                  CPLPackedDMSToDec(adfUpLeft[0])) /
    3114             :                                 nXSize;
    3115           0 :                             poDS->adfGeoTransform[5] =
    3116           0 :                                 (CPLPackedDMSToDec(adfLowRight[1]) -
    3117           0 :                                  CPLPackedDMSToDec(adfUpLeft[1])) /
    3118             :                                 nYSize;
    3119           0 :                             poDS->adfGeoTransform[0] =
    3120           0 :                                 CPLPackedDMSToDec(adfUpLeft[0]);
    3121           0 :                             poDS->adfGeoTransform[3] =
    3122           0 :                                 CPLPackedDMSToDec(adfUpLeft[1]);
    3123             :                         }
    3124           0 :                         poDS->adfGeoTransform[2] = 0.0;
    3125           0 :                         poDS->adfGeoTransform[4] = 0.0;
    3126           0 :                         poDS->bHasGeoTransform = true;
    3127             :                     }
    3128             : 
    3129             :                     /* --------------------------------------------------------------------
    3130             :                      */
    3131             :                     /*      Fetch metadata. */
    3132             :                     /* --------------------------------------------------------------------
    3133             :                      */
    3134           0 :                     poDS->GetGridAttrs(hGD);
    3135             : 
    3136           0 :                     GDdetach(hGD);
    3137             : 
    3138             :                     /* --------------------------------------------------------------------
    3139             :                      */
    3140             :                     /*      Fetch NODATA value. */
    3141             :                     /* --------------------------------------------------------------------
    3142             :                      */
    3143             :                     void *pNoDataValue =
    3144           0 :                         CPLMalloc(poDS->GetDataTypeSize(poDS->iNumType));
    3145           0 :                     if (GDgetfillvalue(hGD, poDS->pszFieldName, pNoDataValue) !=
    3146             :                         -1)
    3147             :                     {
    3148             :                         dfNoData =
    3149           0 :                             poDS->AnyTypeToDouble(poDS->iNumType, pNoDataValue);
    3150           0 :                         bNoDataSet = true;
    3151             :                     }
    3152             :                     else
    3153             :                     {
    3154           0 :                         const char *pszNoData = CSLFetchNameValue(
    3155           0 :                             poDS->papszLocalMetadata, "_FillValue");
    3156           0 :                         if (pszNoData)
    3157             :                         {
    3158           0 :                             dfNoData = CPLAtof(pszNoData);
    3159           0 :                             bNoDataSet = true;
    3160             :                         }
    3161             :                     }
    3162           0 :                     CPLFree(pNoDataValue);
    3163             :                 }
    3164           0 :                 break;
    3165             : 
    3166           0 :                 default:
    3167           0 :                     break;
    3168             :             }
    3169             : 
    3170             :             /* --------------------------------------------------------------------
    3171             :              */
    3172             :             /*      Fetch unit type, scale, offset and description */
    3173             :             /*      Should be similar among various HDF-EOS kinds. */
    3174             :             /* --------------------------------------------------------------------
    3175             :              */
    3176             :             {
    3177             :                 const char *pszTmp =
    3178           0 :                     CSLFetchNameValue(poDS->papszLocalMetadata, "scale_factor");
    3179           0 :                 if (pszTmp)
    3180             :                 {
    3181           0 :                     dfScale = CPLAtof(pszTmp);
    3182             :                     // Some producers (i.e. lndcsm from LEDAPS) emit
    3183             :                     // files with scale_factor=0 which is crazy to carry
    3184             :                     // through.
    3185           0 :                     if (dfScale == 0.0)
    3186           0 :                         dfScale = 1.0;
    3187             : 
    3188           0 :                     bHaveScale = (dfScale != 0.0);
    3189             :                 }
    3190             : 
    3191             :                 pszTmp =
    3192           0 :                     CSLFetchNameValue(poDS->papszLocalMetadata, "add_offset");
    3193           0 :                 if (pszTmp)
    3194             :                 {
    3195           0 :                     dfOffset = CPLAtof(pszTmp);
    3196           0 :                     bHaveOffset = true;
    3197             :                 }
    3198             : 
    3199           0 :                 pszUnits = CSLFetchNameValue(poDS->papszLocalMetadata, "units");
    3200             :                 pszDescription =
    3201           0 :                     CSLFetchNameValue(poDS->papszLocalMetadata, "long_name");
    3202             :             }
    3203             :         }
    3204           0 :         break;
    3205             : 
    3206             :             /* --------------------------------------------------------------------
    3207             :              */
    3208             :             /*  'Plain' HDF scientific datasets. */
    3209             :             /* --------------------------------------------------------------------
    3210             :              */
    3211         299 :         case HDF4_SDS:
    3212             :         {
    3213             : #ifdef HDF4_HAS_MAXOPENFILES
    3214             :             // Attempt to increase maximum number of opened HDF files
    3215         299 :             intn nCurrMax = 0;
    3216         299 :             intn nSysLimit = 0;
    3217             : 
    3218         598 :             if (SDget_maxopenfiles(&nCurrMax, &nSysLimit) >= 0 &&
    3219         299 :                 nCurrMax < nSysLimit)
    3220             :             {
    3221           0 :                 SDreset_maxopenfiles(nSysLimit);
    3222             :             }
    3223             : #endif  // HDF4_HAS_MAXOPENFILES
    3224             : 
    3225         299 :             if (poOpenInfo->eAccess == GA_ReadOnly)
    3226         299 :                 poDS->hHDF4 = Hopen(poDS->pszFilename, DFACC_READ, 0);
    3227             :             else
    3228           0 :                 poDS->hHDF4 = Hopen(poDS->pszFilename, DFACC_WRITE, 0);
    3229             : 
    3230         299 :             if (poDS->hHDF4 <= 0)
    3231             :             {
    3232             :                 // Release mutex otherwise we deadlock with GDALDataset own
    3233             :                 // mutex.
    3234           0 :                 CPLReleaseMutex(hHDF4Mutex);
    3235           0 :                 delete poDS;
    3236           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3237           0 :                 return nullptr;
    3238             :             }
    3239             : 
    3240         299 :             poDS->hSD = SDstart(poDS->pszFilename, DFACC_READ);
    3241         299 :             if (poDS->hSD == -1)
    3242             :             {
    3243             :                 // Release mutex otherwise we deadlock with GDALDataset own
    3244             :                 // mutex.
    3245           0 :                 CPLReleaseMutex(hHDF4Mutex);
    3246           0 :                 delete poDS;
    3247           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3248           0 :                 return nullptr;
    3249             :             }
    3250             : 
    3251         299 :             if (poDS->ReadGlobalAttributes(poDS->hSD) != CE_None)
    3252             :             {
    3253             :                 // Release mutex otherwise we deadlock with GDALDataset own
    3254             :                 // mutex.
    3255           0 :                 CPLReleaseMutex(hHDF4Mutex);
    3256           0 :                 delete poDS;
    3257           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3258           0 :                 return nullptr;
    3259             :             }
    3260             : 
    3261         299 :             int32 nDatasets = 0;
    3262         299 :             int32 l_nAttrs = 0;
    3263         299 :             if (SDfileinfo(poDS->hSD, &nDatasets, &l_nAttrs) != 0)
    3264             :             {
    3265             :                 // Release mutex otherwise we deadlock with GDALDataset own
    3266             :                 // mutex.
    3267           0 :                 CPLReleaseMutex(hHDF4Mutex);
    3268           0 :                 delete poDS;
    3269           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3270           0 :                 return nullptr;
    3271             :             }
    3272             : 
    3273         299 :             if (poDS->iDataset < 0 || poDS->iDataset >= nDatasets)
    3274             :             {
    3275           0 :                 CPLError(CE_Failure, CPLE_AppDefined,
    3276             :                          "Subdataset index should be between 0 and %ld",
    3277           0 :                          static_cast<long int>(nDatasets) - 1);
    3278             :                 // Release mutex otherwise we deadlock with GDALDataset own
    3279             :                 // mutex.
    3280           0 :                 CPLReleaseMutex(hHDF4Mutex);
    3281           0 :                 delete poDS;
    3282           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3283           0 :                 return nullptr;
    3284             :             }
    3285             : 
    3286         299 :             memset(poDS->aiDimSizes, 0, sizeof(int32) * H4_MAX_VAR_DIMS);
    3287         299 :             const int32 iSDS = SDselect(poDS->hSD, poDS->iDataset);
    3288         299 :             SDgetinfo(iSDS, poDS->szName, &poDS->iRank, poDS->aiDimSizes,
    3289             :                       &poDS->iNumType, &poDS->nAttrs);
    3290             : 
    3291             :             // We will duplicate global metadata for every subdataset.
    3292         299 :             poDS->papszLocalMetadata = CSLDuplicate(poDS->papszGlobalMetadata);
    3293             : 
    3294         299 :             for (int32 iAttribute = 0; iAttribute < poDS->nAttrs; iAttribute++)
    3295             :             {
    3296           0 :                 char szAttrName[H4_MAX_NC_NAME] = {};
    3297           0 :                 int32 iAttrNumType = 0;
    3298           0 :                 int32 nValues = 0;
    3299           0 :                 SDattrinfo(iSDS, iAttribute, szAttrName, &iAttrNumType,
    3300             :                            &nValues);
    3301           0 :                 poDS->papszLocalMetadata = poDS->TranslateHDF4Attributes(
    3302             :                     iSDS, iAttribute, szAttrName, iAttrNumType, nValues,
    3303             :                     poDS->papszLocalMetadata);
    3304             :             }
    3305         299 :             poDS->SetMetadata(poDS->papszLocalMetadata, "");
    3306         299 :             SDendaccess(iSDS);
    3307             : 
    3308             : #ifdef DEBUG
    3309         299 :             CPLDebug("HDF4Image",
    3310             :                      "aiDimSizes[0]=%ld, aiDimSizes[1]=%ld, "
    3311             :                      "aiDimSizes[2]=%ld, aiDimSizes[3]=%ld",
    3312         299 :                      static_cast<long>(poDS->aiDimSizes[0]),
    3313         299 :                      static_cast<long>(poDS->aiDimSizes[1]),
    3314         299 :                      static_cast<long>(poDS->aiDimSizes[2]),
    3315         299 :                      static_cast<long>(poDS->aiDimSizes[3]));
    3316             : #endif
    3317         299 :             switch (poDS->iRank)
    3318             :             {
    3319           0 :                 case 1:
    3320           0 :                     poDS->nBandCount = 1;
    3321           0 :                     poDS->iXDim = 0;
    3322           0 :                     poDS->iYDim = -1;
    3323           0 :                     break;
    3324         135 :                 case 2:
    3325         135 :                     poDS->nBandCount = 1;
    3326         135 :                     poDS->iXDim = 1;
    3327         135 :                     poDS->iYDim = 0;
    3328         135 :                     break;
    3329         164 :                 case 3:
    3330             :                     // TODO: We should probably remove the following test as
    3331             :                     // there are valid datasets where the height is lower than
    3332             :                     // the band number. For example:
    3333             :                     //   http://www.iapmw.unibe.ch/research/projects/FriOWL/data/otd/LISOTD_HRAC_V2.2.hdf
    3334             :                     // which is a 720x360 x 365 bands.
    3335             :                     // Use a HACK for now.
    3336         164 :                     if (poDS->aiDimSizes[0] < poDS->aiDimSizes[2] &&
    3337           0 :                         !(poDS->aiDimSizes[0] == 360 &&
    3338           0 :                           poDS->aiDimSizes[1] == 720 &&
    3339           0 :                           poDS->aiDimSizes[2] == 365))
    3340             :                     {
    3341           0 :                         poDS->iBandDim = 0;
    3342           0 :                         poDS->iXDim = 2;
    3343           0 :                         poDS->iYDim = 1;
    3344             :                     }
    3345             :                     else
    3346             :                     {
    3347         164 :                         if (poDS->aiDimSizes[1] <= poDS->aiDimSizes[0] &&
    3348         164 :                             poDS->aiDimSizes[1] <= poDS->aiDimSizes[2])
    3349             :                         {
    3350           0 :                             poDS->iBandDim = 1;
    3351           0 :                             poDS->iXDim = 2;
    3352           0 :                             poDS->iYDim = 0;
    3353             :                         }
    3354             :                         else
    3355             :                         {
    3356         164 :                             poDS->iBandDim = 2;
    3357         164 :                             poDS->iXDim = 1;
    3358         164 :                             poDS->iYDim = 0;
    3359             :                         }
    3360             :                     }
    3361         164 :                     poDS->nBandCount = poDS->aiDimSizes[poDS->iBandDim];
    3362         164 :                     break;
    3363           0 :                 case 4:  // FIXME
    3364           0 :                     poDS->nBandCount =
    3365           0 :                         poDS->aiDimSizes[2] * poDS->aiDimSizes[3];
    3366           0 :                     break;
    3367           0 :                 default:
    3368           0 :                     break;
    3369             :             }
    3370             : 
    3371             :             // We preset this because CaptureNRLGeoTransform needs it.
    3372         299 :             poDS->nRasterXSize = poDS->aiDimSizes[poDS->iXDim];
    3373         299 :             if (poDS->iYDim >= 0)
    3374         299 :                 poDS->nRasterYSize = poDS->aiDimSizes[poDS->iYDim];
    3375             :             else
    3376           0 :                 poDS->nRasterYSize = 1;
    3377             : 
    3378             :             // Special case projection info for NRL generated files.
    3379         598 :             const char *pszMapProjectionSystem = CSLFetchNameValue(
    3380         299 :                 poDS->papszGlobalMetadata, "mapProjectionSystem");
    3381         299 :             if (pszMapProjectionSystem != nullptr &&
    3382           0 :                 EQUAL(pszMapProjectionSystem, "NRL(USGS)"))
    3383             :             {
    3384           0 :                 poDS->CaptureNRLGeoTransform();
    3385             :             }
    3386             : 
    3387             :             // Special case for coastwatch hdf files.
    3388         299 :             if (CSLFetchNameValue(poDS->papszGlobalMetadata, "gctp_sys") !=
    3389             :                 nullptr)
    3390           0 :                 poDS->CaptureCoastwatchGCTPInfo();
    3391             : 
    3392             :             // Special case for MODIS geolocation
    3393         299 :             poDS->ProcessModisSDSGeolocation();
    3394             : 
    3395             :             // Special case for NASA/CCRS Landsat in HDF
    3396         299 :             poDS->CaptureL1GMTLInfo();
    3397             :         }
    3398         299 :         break;
    3399             : 
    3400             :             /* --------------------------------------------------------------------
    3401             :              */
    3402             :             /*  'Plain' HDF rasters. */
    3403             :             /* --------------------------------------------------------------------
    3404             :              */
    3405           2 :         case HDF4_GR:
    3406             :         {
    3407             :             // Attempt to increase maximum number of opened HDF files.
    3408             : #ifdef HDF4_HAS_MAXOPENFILES
    3409           2 :             intn nCurrMax = 0;
    3410           2 :             intn nSysLimit = 0;
    3411             : 
    3412           4 :             if (SDget_maxopenfiles(&nCurrMax, &nSysLimit) >= 0 &&
    3413           2 :                 nCurrMax < nSysLimit)
    3414             :             {
    3415           0 :                 SDreset_maxopenfiles(nSysLimit);
    3416             :             }
    3417             : #endif  // HDF4_HAS_MAXOPENFILES
    3418             : 
    3419           2 :             if (poOpenInfo->eAccess == GA_ReadOnly)
    3420           2 :                 poDS->hHDF4 = Hopen(poDS->pszFilename, DFACC_READ, 0);
    3421             :             else
    3422           0 :                 poDS->hHDF4 = Hopen(poDS->pszFilename, DFACC_WRITE, 0);
    3423             : 
    3424           2 :             if (poDS->hHDF4 <= 0)
    3425             :             {
    3426             :                 // Release mutex otherwise we deadlock with GDALDataset own
    3427             :                 // mutex.
    3428           0 :                 CPLReleaseMutex(hHDF4Mutex);
    3429           0 :                 delete poDS;
    3430           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3431           0 :                 return nullptr;
    3432             :             }
    3433             : 
    3434           2 :             poDS->hGR = GRstart(poDS->hHDF4);
    3435           2 :             if (poDS->hGR == -1)
    3436             :             {
    3437             :                 // Release mutex otherwise we deadlock with GDALDataset own
    3438             :                 // mutex.
    3439           0 :                 CPLReleaseMutex(hHDF4Mutex);
    3440           0 :                 delete poDS;
    3441           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3442           0 :                 return nullptr;
    3443             :             }
    3444             : 
    3445           2 :             poDS->iGR = GRselect(poDS->hGR, poDS->iDataset);
    3446           4 :             if (GRgetiminfo(poDS->iGR, poDS->szName, &poDS->iRank,
    3447             :                             &poDS->iNumType, &poDS->iInterlaceMode,
    3448           2 :                             poDS->aiDimSizes, &poDS->nAttrs) != 0)
    3449             :             {
    3450             :                 // Release mutex otherwise we deadlock with GDALDataset own
    3451             :                 // mutex.
    3452           0 :                 CPLReleaseMutex(hHDF4Mutex);
    3453           0 :                 delete poDS;
    3454           0 :                 CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3455           0 :                 return nullptr;
    3456             :             }
    3457             : 
    3458             :             // We will duplicate global metadata for every subdataset.
    3459           2 :             poDS->papszLocalMetadata = CSLDuplicate(poDS->papszGlobalMetadata);
    3460             : 
    3461           2 :             poDS->SetMetadata(poDS->papszLocalMetadata, "");
    3462             :             // Read colour table
    3463             : 
    3464           2 :             poDS->iPal = GRgetlutid(poDS->iGR, 0);
    3465           2 :             if (poDS->iPal != -1)
    3466             :             {
    3467           2 :                 GRgetlutinfo(poDS->iPal, &poDS->nComps, &poDS->iPalDataType,
    3468             :                              &poDS->iPalInterlaceMode, &poDS->nPalEntries);
    3469           1 :                 if (poDS->nPalEntries && poDS->nComps == 3 &&
    3470           1 :                     GDALGetDataTypeSizeBytes(GetDataType(poDS->iPalDataType)) ==
    3471           3 :                         1 &&
    3472           1 :                     poDS->nPalEntries <= 256)
    3473             :                 {
    3474           1 :                     GRreadlut(poDS->iPal, poDS->aiPaletteData);
    3475           1 :                     poDS->poColorTable = new GDALColorTable();
    3476             :                     GDALColorEntry oEntry;
    3477         257 :                     for (int i = 0;
    3478         257 :                          i < std::min(static_cast<int>(poDS->nPalEntries),
    3479         257 :                                       N_COLOR_ENTRIES);
    3480             :                          i++)
    3481             :                     {
    3482         256 :                         oEntry.c1 = poDS->aiPaletteData[i][0];
    3483         256 :                         oEntry.c2 = poDS->aiPaletteData[i][1];
    3484         256 :                         oEntry.c3 = poDS->aiPaletteData[i][2];
    3485         256 :                         oEntry.c4 = 255;
    3486             : 
    3487         256 :                         poDS->poColorTable->SetColorEntry(i, &oEntry);
    3488             :                     }
    3489             :                 }
    3490             :             }
    3491             : 
    3492           2 :             poDS->iXDim = 0;
    3493           2 :             poDS->iYDim = 1;
    3494           2 :             poDS->nBandCount = poDS->iRank;
    3495           2 :             break;
    3496             :         }
    3497           0 :         default:
    3498             :             // Release mutex otherwise we'll deadlock with GDALDataset own mutex
    3499           0 :             CPLReleaseMutex(hHDF4Mutex);
    3500           0 :             delete poDS;
    3501           0 :             CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3502           0 :             return nullptr;
    3503             :     }
    3504             : 
    3505         301 :     poDS->nRasterXSize = poDS->aiDimSizes[poDS->iXDim];
    3506         301 :     if (poDS->iYDim >= 0)
    3507         301 :         poDS->nRasterYSize = poDS->aiDimSizes[poDS->iYDim];
    3508             :     else
    3509           0 :         poDS->nRasterYSize = 1;
    3510             : 
    3511         301 :     if (poDS->iSubdatasetType == H4ST_HYPERION_L1)
    3512             :     {
    3513             :         // XXX: Hyperion SDSs has Height x Bands x Width dimensions scheme
    3514           0 :         if (poDS->iRank > 2)
    3515             :         {
    3516           0 :             poDS->nBandCount = poDS->aiDimSizes[1];
    3517           0 :             poDS->nRasterXSize = poDS->aiDimSizes[2];
    3518           0 :             poDS->nRasterYSize = poDS->aiDimSizes[0];
    3519             :         }
    3520             :         else
    3521             :         {
    3522           0 :             poDS->nBandCount = poDS->aiDimSizes[0];
    3523           0 :             poDS->nRasterXSize = poDS->aiDimSizes[1];
    3524           0 :             poDS->nRasterYSize = 1;
    3525             :         }
    3526             :     }
    3527             : 
    3528             :     /* -------------------------------------------------------------------- */
    3529             :     /*      Create band information objects.                                */
    3530             :     /* -------------------------------------------------------------------- */
    3531         635 :     for (int i = 1; i <= poDS->nBandCount; i++)
    3532             :     {
    3533             :         HDF4ImageRasterBand *poBand =
    3534         334 :             new HDF4ImageRasterBand(poDS, i, poDS->GetDataType(poDS->iNumType));
    3535         334 :         poDS->SetBand(i, poBand);
    3536             : 
    3537         334 :         if (bNoDataSet)
    3538           0 :             poBand->SetNoDataValue(dfNoData);
    3539         334 :         if (bHaveScale)
    3540             :         {
    3541           0 :             poBand->bHaveScale = true;
    3542           0 :             poBand->dfScale = dfScale;
    3543             :         }
    3544         334 :         if (bHaveOffset)
    3545             :         {
    3546           0 :             poBand->bHaveOffset = true;
    3547           0 :             poBand->dfOffset = dfOffset;
    3548             :         }
    3549         334 :         if (pszUnits)
    3550           0 :             poBand->osUnitType = pszUnits;
    3551         334 :         if (pszDescription)
    3552           0 :             poBand->SetDescription(pszDescription);
    3553             :     }
    3554             : 
    3555             :     /* -------------------------------------------------------------------- */
    3556             :     /*      Now we will handle particular types of HDF products. Every      */
    3557             :     /*      HDF product has its own structure.                              */
    3558             :     /* -------------------------------------------------------------------- */
    3559             : 
    3560         301 :     switch (poDS->iSubdatasetType)
    3561             :     {
    3562             :             /* --------------------------------------------------------------------
    3563             :              */
    3564             :             /*  HDF, created by GDAL. */
    3565             :             /* --------------------------------------------------------------------
    3566             :              */
    3567         299 :         case H4ST_GDAL:
    3568             :         {
    3569         299 :             CPLDebug("HDF4Image", "Input dataset interpreted as GDAL_HDF4");
    3570             : 
    3571             :             const char *pszValue =
    3572         299 :                 CSLFetchNameValue(poDS->papszGlobalMetadata, "Projection");
    3573         299 :             if (pszValue != nullptr)
    3574             :             {
    3575         119 :                 poDS->m_oSRS.importFromWkt(pszValue);
    3576             :             }
    3577         299 :             if ((pszValue = CSLFetchNameValue(poDS->papszGlobalMetadata,
    3578         299 :                                               "TransformationMatrix")) !=
    3579             :                 nullptr)
    3580             :             {
    3581         299 :                 int i = 0;
    3582         299 :                 char *pszString = const_cast<char *>(pszValue);
    3583        2093 :                 while (*pszValue && i < 6)
    3584             :                 {
    3585        1794 :                     poDS->adfGeoTransform[i++] =
    3586        1794 :                         CPLStrtod(pszString, &pszString);
    3587        1794 :                     pszString++;
    3588             :                 }
    3589         299 :                 poDS->bHasGeoTransform = true;
    3590             :             }
    3591         630 :             for (int i = 1; i <= poDS->nBands; i++)
    3592             :             {
    3593         331 :                 if ((pszValue = CSLFetchNameValue(
    3594         331 :                          poDS->papszGlobalMetadata,
    3595         331 :                          CPLSPrintf("BandDesc%d", i))) != nullptr)
    3596          36 :                     poDS->GetRasterBand(i)->SetDescription(pszValue);
    3597             :             }
    3598         630 :             for (int i = 1; i <= poDS->nBands; i++)
    3599             :             {
    3600         331 :                 if ((pszValue = CSLFetchNameValue(
    3601         331 :                          poDS->papszGlobalMetadata,
    3602         331 :                          CPLSPrintf("NoDataValue%d", i))) != nullptr)
    3603          36 :                     poDS->GetRasterBand(i)->SetNoDataValue(CPLAtof(pszValue));
    3604             :             }
    3605             :         }
    3606         299 :         break;
    3607             : 
    3608             :             /* --------------------------------------------------------------------
    3609             :              */
    3610             :             /*      SeaWiFS Level 3 Standard Mapped Image Products. */
    3611             :             /*      Organized similar to MODIS Level 3 products. */
    3612             :             /* --------------------------------------------------------------------
    3613             :              */
    3614           0 :         case H4ST_SEAWIFS_L3:
    3615             :         {
    3616           0 :             CPLDebug("HDF4Image", "Input dataset interpreted as SEAWIFS_L3");
    3617             : 
    3618             :             // Read band description
    3619           0 :             for (int i = 1; i <= poDS->nBands; i++)
    3620             :             {
    3621           0 :                 poDS->GetRasterBand(i)->SetDescription(
    3622           0 :                     CSLFetchNameValue(poDS->papszGlobalMetadata, "Parameter"));
    3623             :             }
    3624             : 
    3625             :             // Read coordinate system and geotransform matrix.
    3626           0 :             poDS->m_oSRS.SetWellKnownGeogCS("WGS84");
    3627             : 
    3628           0 :             if (EQUAL(CSLFetchNameValue(poDS->papszGlobalMetadata,
    3629             :                                         "Map Projection"),
    3630             :                       "Equidistant Cylindrical"))
    3631             :             {
    3632           0 :                 poDS->m_oSRS.SetEquirectangular(0.0, 0.0, 0.0, 0.0);
    3633           0 :                 poDS->m_oSRS.SetLinearUnits(SRS_UL_METER, 1);
    3634             :             }
    3635             : 
    3636           0 :             double dfULX = CPLAtof(CSLFetchNameValue(poDS->papszGlobalMetadata,
    3637           0 :                                                      "Westernmost Longitude"));
    3638           0 :             double dfULY = CPLAtof(CSLFetchNameValue(poDS->papszGlobalMetadata,
    3639           0 :                                                      "Northernmost Latitude"));
    3640           0 :             double dfLRX = CPLAtof(CSLFetchNameValue(poDS->papszGlobalMetadata,
    3641           0 :                                                      "Easternmost Longitude"));
    3642           0 :             double dfLRY = CPLAtof(CSLFetchNameValue(poDS->papszGlobalMetadata,
    3643           0 :                                                      "Southernmost Latitude"));
    3644           0 :             poDS->ToGeoref(&dfULX, &dfULY);
    3645           0 :             poDS->ToGeoref(&dfLRX, &dfLRY);
    3646           0 :             poDS->adfGeoTransform[0] = dfULX;
    3647           0 :             poDS->adfGeoTransform[3] = dfULY;
    3648           0 :             poDS->adfGeoTransform[1] = (dfLRX - dfULX) / poDS->nRasterXSize;
    3649           0 :             poDS->adfGeoTransform[5] = (dfULY - dfLRY) / poDS->nRasterYSize;
    3650           0 :             if (dfULY > 0)  // Northern hemisphere.
    3651           0 :                 poDS->adfGeoTransform[5] = -poDS->adfGeoTransform[5];
    3652           0 :             poDS->adfGeoTransform[2] = 0.0;
    3653           0 :             poDS->adfGeoTransform[4] = 0.0;
    3654           0 :             poDS->bHasGeoTransform = true;
    3655             :         }
    3656           0 :         break;
    3657             : 
    3658             :             /* --------------------------------------------------------------------
    3659             :              */
    3660             :             /*      Generic SDS */
    3661             :             /* --------------------------------------------------------------------
    3662             :              */
    3663           2 :         case H4ST_UNKNOWN:
    3664             :         {
    3665             :             // This is a coastwatch convention.
    3666           2 :             if (CSLFetchNameValue(poDS->papszLocalMetadata, "missing_value"))
    3667             :             {
    3668           0 :                 for (int i = 1; i <= poDS->nBands; i++)
    3669             :                 {
    3670           0 :                     poDS->GetRasterBand(i)->SetNoDataValue(
    3671           0 :                         CPLAtof(CSLFetchNameValue(poDS->papszLocalMetadata,
    3672           0 :                                                   "missing_value")));
    3673             :                 }
    3674             :             }
    3675             : 
    3676             :             // Coastwatch offset and scale.
    3677           2 :             if (CSLFetchNameValue(poDS->papszLocalMetadata, "scale_factor") &&
    3678           0 :                 CSLFetchNameValue(poDS->papszLocalMetadata, "add_offset"))
    3679             :             {
    3680           0 :                 for (int i = 1; i <= poDS->nBands; i++)
    3681             :                 {
    3682             :                     HDF4ImageRasterBand *poBand =
    3683             :                         reinterpret_cast<HDF4ImageRasterBand *>(
    3684           0 :                             poDS->GetRasterBand(i));
    3685             : 
    3686           0 :                     poBand->bHaveScale = true;
    3687           0 :                     poBand->bHaveOffset = true;
    3688           0 :                     poBand->dfScale = CPLAtof(CSLFetchNameValue(
    3689           0 :                         poDS->papszLocalMetadata, "scale_factor"));
    3690             :                     // See #4891 regarding offset interpretation.
    3691             :                     //  poBand->dfOffset = -1 * poBand->dfScale *
    3692             :                     //      CPLAtof( CSLFetchNameValue(
    3693             :                     //      poDS->papszLocalMetadata,
    3694             :                     //                                  "add_offset" ) );
    3695           0 :                     poBand->dfOffset = CPLAtof(CSLFetchNameValue(
    3696           0 :                         poDS->papszLocalMetadata, "add_offset"));
    3697             :                 }
    3698             :             }
    3699             : 
    3700             :             // This is a modis level3 convention (data from ACT)
    3701             :             // e.g. data/hdf/act/modis/MODAM2004280160000.L3_NOAA_GMX
    3702             : 
    3703           2 :             if (CSLFetchNameValue(poDS->papszLocalMetadata, "scalingSlope") &&
    3704           0 :                 CSLFetchNameValue(poDS->papszLocalMetadata, "scalingIntercept"))
    3705             :             {
    3706           0 :                 CPLString osUnits;
    3707             : 
    3708           0 :                 if (CSLFetchNameValue(poDS->papszLocalMetadata, "productUnits"))
    3709             :                 {
    3710           0 :                     osUnits = CSLFetchNameValue(poDS->papszLocalMetadata,
    3711           0 :                                                 "productUnits");
    3712             :                 }
    3713             : 
    3714           0 :                 for (int i = 1; i <= poDS->nBands; i++)
    3715             :                 {
    3716             :                     HDF4ImageRasterBand *poBand =
    3717             :                         reinterpret_cast<HDF4ImageRasterBand *>(
    3718           0 :                             poDS->GetRasterBand(i));
    3719             : 
    3720           0 :                     poBand->bHaveScale = true;
    3721           0 :                     poBand->bHaveOffset = true;
    3722           0 :                     poBand->dfScale = CPLAtof(CSLFetchNameValue(
    3723           0 :                         poDS->papszLocalMetadata, "scalingSlope"));
    3724           0 :                     poBand->dfOffset = CPLAtof(CSLFetchNameValue(
    3725           0 :                         poDS->papszLocalMetadata, "scalingIntercept"));
    3726             : 
    3727           0 :                     poBand->osUnitType = osUnits;
    3728             :                 }
    3729             :             }
    3730             :         }
    3731           2 :         break;
    3732             : 
    3733             :             /* --------------------------------------------------------------------
    3734             :              */
    3735             :             /*      Hyperion Level 1. */
    3736             :             /* --------------------------------------------------------------------
    3737             :              */
    3738           0 :         case H4ST_HYPERION_L1:
    3739             :         {
    3740           0 :             CPLDebug("HDF4Image", "Input dataset interpreted as HYPERION_L1");
    3741             :         }
    3742           0 :         break;
    3743             : 
    3744           0 :         default:
    3745             : 
    3746             : #ifdef DEBUG_VERBOSE
    3747             :             CPLError(CE_Debug, CPLE_AppDefined, "Unknown subdata type %d",
    3748             :                      poDS->iSubdatasetType);
    3749             : #endif
    3750           0 :             break;
    3751             :     }
    3752             : 
    3753             :     /* -------------------------------------------------------------------- */
    3754             :     /*      Setup PAM info for this subdataset                              */
    3755             :     /* -------------------------------------------------------------------- */
    3756         301 :     poDS->SetPhysicalFilename(poDS->pszFilename);
    3757         301 :     poDS->SetSubdatasetName(osSubdatasetName);
    3758             : 
    3759             :     // Release mutex otherwise we'll deadlock with GDALDataset own mutex.
    3760         301 :     CPLReleaseMutex(hHDF4Mutex);
    3761         301 :     poDS->TryLoadXML();
    3762             : 
    3763         301 :     poDS->oOvManager.Initialize(poDS, ":::VIRTUAL:::");
    3764         301 :     CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3765             : 
    3766         301 :     return poDS;
    3767             : }
    3768             : 
    3769             : /************************************************************************/
    3770             : /*                               Create()                               */
    3771             : /************************************************************************/
    3772             : 
    3773         176 : GDALDataset *HDF4ImageDataset::Create(const char *pszFilename, int nXSize,
    3774             :                                       int nYSize, int nBandsIn,
    3775             :                                       GDALDataType eType, char **papszOptions)
    3776             : 
    3777             : {
    3778             :     /* -------------------------------------------------------------------- */
    3779             :     /*      Create the dataset.                                             */
    3780             :     /* -------------------------------------------------------------------- */
    3781         176 :     if (nBandsIn == 0)
    3782             :     {
    3783           1 :         CPLError(CE_Failure, CPLE_NotSupported,
    3784             :                  "Unable to export files with zero bands.");
    3785           1 :         return nullptr;
    3786             :     }
    3787             : 
    3788             :     // Try now to create the file to avoid memory leaks if it is
    3789             :     // the SDK that fails to do it.
    3790         175 :     VSILFILE *fpVSIL = VSIFOpenL(pszFilename, "wb");
    3791         175 :     if (fpVSIL == nullptr)
    3792             :     {
    3793           3 :         CPLError(CE_Failure, CPLE_OpenFailed, "Failed to create %s.",
    3794             :                  pszFilename);
    3795           3 :         return nullptr;
    3796             :     }
    3797         172 :     VSIFCloseL(fpVSIL);
    3798         172 :     VSIUnlink(pszFilename);
    3799             : 
    3800         172 :     HDF4ImageDataset *poDS = new HDF4ImageDataset();
    3801             : 
    3802         344 :     CPLMutexHolderD(&hHDF4Mutex);
    3803             : 
    3804             :     /* -------------------------------------------------------------------- */
    3805             :     /*      Choose rank for the created dataset.                            */
    3806             :     /* -------------------------------------------------------------------- */
    3807         172 :     poDS->iRank = 3;
    3808         298 :     if (CSLFetchNameValue(papszOptions, "RANK") != nullptr &&
    3809         126 :         EQUAL(CSLFetchNameValue(papszOptions, "RANK"), "2"))
    3810          63 :         poDS->iRank = 2;
    3811             : 
    3812         172 :     poDS->hSD = SDstart(pszFilename, DFACC_CREATE);
    3813         172 :     if (poDS->hSD == -1)
    3814             :     {
    3815           0 :         CPLError(CE_Failure, CPLE_OpenFailed, "Can't create HDF4 file %s",
    3816             :                  pszFilename);
    3817             :         // Release mutex otherwise we'll deadlock with GDALDataset own mutex.
    3818           0 :         CPLReleaseMutex(hHDF4Mutex);
    3819           0 :         delete poDS;
    3820           0 :         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3821           0 :         return nullptr;
    3822             :     }
    3823         172 :     poDS->iXDim = 1;
    3824         172 :     poDS->iYDim = 0;
    3825         172 :     poDS->iBandDim = 2;
    3826             : 
    3827         172 :     int32 aiDimSizes[H4_MAX_VAR_DIMS] = {};
    3828         172 :     aiDimSizes[poDS->iXDim] = nXSize;
    3829         172 :     aiDimSizes[poDS->iYDim] = nYSize;
    3830         172 :     aiDimSizes[poDS->iBandDim] = nBandsIn;
    3831             : 
    3832         172 :     const auto GetHDFType = [](GDALDataType eTypeIn)
    3833             :     {
    3834         172 :         switch (eTypeIn)
    3835             :         {
    3836          17 :             case GDT_Float64:
    3837          17 :                 return DFNT_FLOAT64;
    3838          17 :             case GDT_Float32:
    3839          17 :                 return DFNT_FLOAT32;
    3840           2 :             case GDT_UInt64:
    3841             :                 // SDCreate() doesn't like it
    3842           2 :                 return DFNT_UINT64;
    3843          17 :             case GDT_UInt32:
    3844          17 :                 return DFNT_UINT32;
    3845          17 :             case GDT_UInt16:
    3846          17 :                 return DFNT_UINT16;
    3847           2 :             case GDT_Int64:
    3848             :                 // SDCreate() doesn't like it
    3849           2 :                 return DFNT_INT64;
    3850          17 :             case GDT_Int32:
    3851          17 :                 return DFNT_INT32;
    3852          17 :             case GDT_Int16:
    3853          17 :                 return DFNT_INT16;
    3854          38 :             case GDT_Byte:
    3855          38 :                 return DFNT_UINT8;
    3856          16 :             case GDT_Int8:
    3857          16 :                 return DFNT_INT8;
    3858          12 :             default:
    3859          12 :                 CPLError(CE_Warning, CPLE_NotSupported,
    3860             :                          "Datatype %s not supported. Defauting to Byte",
    3861             :                          GDALGetDataTypeName(eTypeIn));
    3862          12 :                 return DFNT_UINT8;
    3863             :         }
    3864             :     };
    3865             : 
    3866         172 :     const char *pszSDSName = nullptr;
    3867         172 :     int32 iSDS = -1;
    3868             : 
    3869         172 :     if (poDS->iRank == 2)
    3870             :     {
    3871         126 :         for (int iBand = 0; iBand < nBandsIn; iBand++)
    3872             :         {
    3873          63 :             pszSDSName = CPLSPrintf("Band%d", iBand);
    3874          63 :             iSDS = SDcreate(poDS->hSD, pszSDSName, GetHDFType(eType),
    3875             :                             poDS->iRank, aiDimSizes);
    3876          63 :             if (iSDS < 0)
    3877           0 :                 break;
    3878          63 :             SDendaccess(iSDS);
    3879             :         }
    3880             :     }
    3881         109 :     else if (poDS->iRank == 3)
    3882             :     {
    3883         109 :         pszSDSName = "3-dimensional Scientific Dataset";
    3884         109 :         poDS->iDataset = 0;
    3885         109 :         iSDS = SDcreate(poDS->hSD, pszSDSName, GetHDFType(eType), poDS->iRank,
    3886             :                         aiDimSizes);
    3887             :     }
    3888             :     else
    3889             :     {
    3890             :         // Should never happen.
    3891             :         // Release mutex otherwise we'll deadlock with GDALDataset own mutex.
    3892           0 :         CPLReleaseMutex(hHDF4Mutex);
    3893           0 :         delete poDS;
    3894           0 :         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3895           0 :         return nullptr;
    3896             :     }
    3897             : 
    3898         172 :     if (iSDS < 0)
    3899             :     {
    3900           4 :         CPLError(CE_Failure, CPLE_AppDefined,
    3901             :                  "Can't create SDS with rank %ld for file %s",
    3902           4 :                  static_cast<long>(poDS->iRank), pszFilename);
    3903             :         // Release mutex otherwise we'll deadlock with GDALDataset own mutex.
    3904           4 :         CPLReleaseMutex(hHDF4Mutex);
    3905           4 :         delete poDS;
    3906           4 :         CPLAcquireMutex(hHDF4Mutex, 1000.0);
    3907           4 :         return nullptr;
    3908             :     }
    3909             : 
    3910         168 :     poDS->nRasterXSize = nXSize;
    3911         168 :     poDS->nRasterYSize = nYSize;
    3912         168 :     poDS->eAccess = GA_Update;
    3913         168 :     poDS->iDatasetType = HDF4_SDS;
    3914         168 :     poDS->iSubdatasetType = H4ST_GDAL;
    3915         168 :     poDS->nBands = nBandsIn;
    3916             : 
    3917             :     /* -------------------------------------------------------------------- */
    3918             :     /*      Create band information objects.                                */
    3919             :     /* -------------------------------------------------------------------- */
    3920         378 :     for (int iBand = 1; iBand <= nBandsIn; iBand++)
    3921         210 :         poDS->SetBand(iBand, new HDF4ImageRasterBand(poDS, iBand, eType));
    3922             : 
    3923         168 :     SDsetattr(poDS->hSD, "Signature", DFNT_CHAR8,
    3924             :               static_cast<int>(strlen(pszGDALSignature)) + 1, pszGDALSignature);
    3925             : 
    3926         168 :     return GDALDataset::FromHandle(poDS);
    3927             : }
    3928             : 
    3929             : /************************************************************************/
    3930             : /*                        GDALRegister_HDF4Image()                      */
    3931             : /************************************************************************/
    3932             : 
    3933           9 : void GDALRegister_HDF4Image()
    3934             : 
    3935             : {
    3936           9 :     if (GDALGetDriverByName(HDF4_IMAGE_DRIVER_NAME) != nullptr)
    3937           0 :         return;
    3938             : 
    3939           9 :     GDALDriver *poDriver = new GDALDriver();
    3940           9 :     HDF4ImageDriverSetCommonMetadata(poDriver);
    3941             : 
    3942             : #ifdef HDF4_HAS_MAXOPENFILES
    3943           9 :     poDriver->SetMetadataItem("HDF4_HAS_MAXOPENFILES", "YES");
    3944             : #endif
    3945           9 :     poDriver->pfnOpen = HDF4ImageDataset::Open;
    3946           9 :     poDriver->pfnCreate = HDF4ImageDataset::Create;
    3947             : 
    3948           9 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    3949             : }

Generated by: LCOV version 1.14