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

Generated by: LCOV version 1.14