LCOV - code coverage report
Current view: top level - frmts/grib - gribdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1167 1355 86.1 %
Date: 2024-04-29 17:29:47 Functions: 58 60 96.7 %

          Line data    Source code
       1             : /******************************************************************************
       2             :  *
       3             :  * Project:  GRIB Driver
       4             :  * Purpose:  GDALDataset driver for GRIB translator for read support
       5             :  * Author:   Bas Retsios, retsios@itc.nl
       6             :  *
       7             :  ******************************************************************************
       8             :  * Copyright (c) 2007, ITC
       9             :  * Copyright (c) 2008-2017, Even Rouault <even dot rouault at spatialys dot com>
      10             :  *
      11             :  * Permission is hereby granted, free of charge, to any person obtaining a
      12             :  * copy of this software and associated documentation files (the "Software"),
      13             :  * to deal in the Software without restriction, including without limitation
      14             :  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
      15             :  * and/or sell copies of the Software, and to permit persons to whom the
      16             :  * Software is furnished to do so, subject to the following conditions:
      17             :  *
      18             :  * The above copyright notice and this permission notice shall be included
      19             :  * in all copies or substantial portions of the Software.
      20             :  *
      21             :  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
      22             :  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
      23             :  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
      24             :  * THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
      25             :  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
      26             :  * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
      27             :  * DEALINGS IN THE SOFTWARE.
      28             :  ******************************************************************************
      29             :  *
      30             :  */
      31             : 
      32             : #include "cpl_port.h"
      33             : #include "gribdataset.h"
      34             : #include "gribdrivercore.h"
      35             : 
      36             : #include <cerrno>
      37             : #include <cmath>
      38             : #include <cstddef>
      39             : #include <cstdio>
      40             : #include <cstdlib>
      41             : #include <cstring>
      42             : #if HAVE_FCNTL_H
      43             : #include <fcntl.h>
      44             : #endif
      45             : 
      46             : #include <algorithm>
      47             : #include <set>
      48             : #include <string>
      49             : #include <vector>
      50             : 
      51             : #include "cpl_conv.h"
      52             : #include "cpl_error.h"
      53             : #include "cpl_multiproc.h"
      54             : #include "cpl_string.h"
      55             : #include "cpl_vsi.h"
      56             : #include "cpl_time.h"
      57             : #include "degrib/degrib/degrib2.h"
      58             : #include "degrib/degrib/inventory.h"
      59             : #include "degrib/degrib/meta.h"
      60             : #include "degrib/degrib/metaname.h"
      61             : #include "degrib/degrib/myerror.h"
      62             : #include "degrib/degrib/type.h"
      63             : CPL_C_START
      64             : #include "degrib/g2clib/grib2.h"
      65             : #include "degrib/g2clib/pdstemplates.h"
      66             : CPL_C_END
      67             : #include "gdal.h"
      68             : #include "gdal_frmts.h"
      69             : #include "gdal_pam.h"
      70             : #include "gdal_priv.h"
      71             : #include "ogr_spatialref.h"
      72             : #include "memdataset.h"
      73             : 
      74             : static CPLMutex *hGRIBMutex = nullptr;
      75             : 
      76             : /************************************************************************/
      77             : /*                         ConvertUnitInText()                          */
      78             : /************************************************************************/
      79             : 
      80         908 : static CPLString ConvertUnitInText(bool bMetricUnits, const char *pszTxt)
      81             : {
      82         908 :     if (pszTxt == nullptr)
      83           0 :         return CPLString();
      84         908 :     if (!bMetricUnits)
      85           4 :         return pszTxt;
      86             : 
      87        1808 :     CPLString osRes(pszTxt);
      88         904 :     size_t iPos = osRes.find("[K]");
      89         904 :     if (iPos != std::string::npos)
      90          96 :         osRes = osRes.substr(0, iPos) + "[C]" + osRes.substr(iPos + 3);
      91         904 :     return osRes;
      92             : }
      93             : 
      94             : /************************************************************************/
      95             : /*                         Lon360to180()                               */
      96             : /************************************************************************/
      97             : 
      98         355 : static inline double Lon360to180(double lon)
      99             : {
     100         355 :     if (lon == 180)
     101           0 :         return 180;
     102         355 :     return fmod(lon + 180, 360) - 180;
     103             : }
     104             : 
     105             : /************************************************************************/
     106             : /*                           GRIBRasterBand()                            */
     107             : /************************************************************************/
     108             : 
     109         431 : GRIBRasterBand::GRIBRasterBand(GRIBDataset *poDSIn, int nBandIn,
     110         431 :                                inventoryType *psInv)
     111         431 :     : start(psInv->start), subgNum(psInv->subgNum),
     112         862 :       longFstLevel(CPLStrdup(psInv->longFstLevel)), m_Grib_Data(nullptr),
     113         431 :       m_Grib_MetaData(nullptr), nGribDataXSize(poDSIn->nRasterXSize),
     114         431 :       nGribDataYSize(poDSIn->nRasterYSize), m_nGribVersion(psInv->GribVersion),
     115         431 :       m_bHasLookedForNoData(false), m_dfNoData(0.0), m_bHasNoData(false)
     116             : 
     117             : {
     118         431 :     poDS = poDSIn;
     119         431 :     nBand = nBandIn;
     120             : 
     121             :     // Let user do -ot Float32 if needed for saving space, GRIB contains
     122             :     // Float64 (though not fully utilized most of the time).
     123         431 :     eDataType = GDT_Float64;
     124             : 
     125         431 :     nBlockXSize = poDSIn->nRasterXSize;
     126         431 :     nBlockYSize = 1;
     127             : 
     128         431 :     if (psInv->unitName != nullptr && psInv->comment != nullptr &&
     129         405 :         psInv->element != nullptr)
     130             :     {
     131         405 :         bLoadedMetadata = true;
     132             :         const char *pszGribNormalizeUnits =
     133         405 :             CPLGetConfigOption("GRIB_NORMALIZE_UNITS", "YES");
     134         405 :         bool bMetricUnits = CPLTestBool(pszGribNormalizeUnits);
     135             : 
     136         405 :         SetMetadataItem("GRIB_UNIT",
     137         810 :                         ConvertUnitInText(bMetricUnits, psInv->unitName));
     138         405 :         SetMetadataItem("GRIB_COMMENT",
     139         810 :                         ConvertUnitInText(bMetricUnits, psInv->comment));
     140         405 :         SetMetadataItem("GRIB_ELEMENT", psInv->element);
     141         405 :         SetMetadataItem("GRIB_SHORT_NAME", psInv->shortFstLevel);
     142         405 :         SetMetadataItem("GRIB_REF_TIME",
     143         810 :                         CPLString().Printf("%.0f", psInv->refTime));
     144         405 :         SetMetadataItem("GRIB_VALID_TIME",
     145         810 :                         CPLString().Printf("%.0f", psInv->validTime));
     146         405 :         SetMetadataItem("GRIB_FORECAST_SECONDS",
     147         810 :                         CPLString().Printf("%.0f", psInv->foreSec));
     148             :     }
     149         431 : }
     150             : 
     151             : /************************************************************************/
     152             : /*                           FindMetaData()                             */
     153             : /************************************************************************/
     154             : 
     155         779 : void GRIBRasterBand::FindMetaData()
     156             : {
     157         779 :     if (bLoadedMetadata)
     158         753 :         return;
     159          26 :     if (m_Grib_MetaData == nullptr)
     160             :     {
     161             :         grib_MetaData *metaData;
     162           5 :         GRIBDataset *poGDS = static_cast<GRIBDataset *>(poDS);
     163           5 :         GRIBRasterBand::ReadGribData(poGDS->fp, start, subgNum, nullptr,
     164             :                                      &metaData);
     165           5 :         if (metaData == nullptr)
     166           0 :             return;
     167           5 :         m_Grib_MetaData = metaData;
     168             :     }
     169          26 :     bLoadedMetadata = true;
     170          26 :     m_nGribVersion = m_Grib_MetaData->GribVersion;
     171             : 
     172             :     const char *pszGribNormalizeUnits =
     173          26 :         CPLGetConfigOption("GRIB_NORMALIZE_UNITS", "YES");
     174          26 :     bool bMetricUnits = CPLTestBool(pszGribNormalizeUnits);
     175             : 
     176          26 :     GDALRasterBand::SetMetadataItem(
     177             :         "GRIB_UNIT",
     178          52 :         ConvertUnitInText(bMetricUnits, m_Grib_MetaData->unitName));
     179          26 :     GDALRasterBand::SetMetadataItem(
     180             :         "GRIB_COMMENT",
     181          52 :         ConvertUnitInText(bMetricUnits, m_Grib_MetaData->comment));
     182             : 
     183          26 :     GDALRasterBand::SetMetadataItem("GRIB_ELEMENT", m_Grib_MetaData->element);
     184          26 :     GDALRasterBand::SetMetadataItem("GRIB_SHORT_NAME",
     185          26 :                                     m_Grib_MetaData->shortFstLevel);
     186             : 
     187          26 :     if (m_nGribVersion == 2)
     188             :     {
     189          14 :         GDALRasterBand::SetMetadataItem(
     190             :             "GRIB_REF_TIME",
     191          28 :             CPLString().Printf("%.0f", m_Grib_MetaData->pds2.refTime));
     192          14 :         GDALRasterBand::SetMetadataItem(
     193             :             "GRIB_VALID_TIME",
     194          28 :             CPLString().Printf("%.0f", m_Grib_MetaData->pds2.sect4.validTime));
     195             :     }
     196          12 :     else if (m_nGribVersion == 1)
     197             :     {
     198          12 :         GDALRasterBand::SetMetadataItem(
     199             :             "GRIB_REF_TIME",
     200          24 :             CPLString().Printf("%.0f", m_Grib_MetaData->pds1.refTime));
     201          12 :         GDALRasterBand::SetMetadataItem(
     202             :             "GRIB_VALID_TIME",
     203          24 :             CPLString().Printf("%.0f", m_Grib_MetaData->pds1.validTime));
     204             :     }
     205             : 
     206          26 :     GDALRasterBand::SetMetadataItem(
     207             :         "GRIB_FORECAST_SECONDS",
     208          52 :         CPLString().Printf("%d", m_Grib_MetaData->deltTime));
     209             : }
     210             : 
     211             : /************************************************************************/
     212             : /*                          FindTrueStart()                             */
     213             : /*                                                                      */
     214             : /*      Scan after the official start of the message to find its        */
     215             : /*      true starting offset.                                           */
     216             : /************************************************************************/
     217         842 : vsi_l_offset GRIBRasterBand::FindTrueStart(VSILFILE *fp, vsi_l_offset start)
     218             : {
     219             :     // GRIB messages can be preceded by "garbage". GRIB2Inventory()
     220             :     // does not return the offset to the real start of the message
     221             :     char szHeader[1024 + 1];
     222         842 :     VSIFSeekL(fp, start, SEEK_SET);
     223             :     const int nRead =
     224         842 :         static_cast<int>(VSIFReadL(szHeader, 1, sizeof(szHeader) - 1, fp));
     225         842 :     szHeader[nRead] = 0;
     226             :     // Find the real offset of the fist message
     227         842 :     int nOffsetFirstMessage = 0;
     228        2524 :     for (int j = 0; j + 3 < nRead; j++)
     229             :     {
     230        2524 :         if (STARTS_WITH_CI(szHeader + j, "GRIB")
     231             : #ifdef ENABLE_TDLP
     232             :             || STARTS_WITH_CI(szHeader + j, "TDLP")
     233             : #endif
     234             :         )
     235             :         {
     236         842 :             nOffsetFirstMessage = j;
     237         842 :             break;
     238             :         }
     239             :     }
     240         842 :     return start + nOffsetFirstMessage;
     241             : }
     242             : 
     243             : /************************************************************************/
     244             : /*                      FindPDSTemplateGRIB2()                          */
     245             : /*                                                                      */
     246             : /*      Scan the file for the PDS template info and represent it as     */
     247             : /*      metadata.                                                       */
     248             : /************************************************************************/
     249             : 
     250         883 : void GRIBRasterBand::FindPDSTemplateGRIB2()
     251             : 
     252             : {
     253         883 :     CPLAssert(m_nGribVersion == 2);
     254             : 
     255         883 :     if (bLoadedPDS)
     256         564 :         return;
     257         319 :     bLoadedPDS = true;
     258             : 
     259         319 :     GRIBDataset *poGDS = static_cast<GRIBDataset *>(poDS);
     260         319 :     start = FindTrueStart(poGDS->fp, start);
     261             : 
     262             :     // Read section 0
     263             :     GByte abySection0[16];
     264         638 :     if (VSIFSeekL(poGDS->fp, start, SEEK_SET) != 0 ||
     265         319 :         VSIFReadL(abySection0, 16, 1, poGDS->fp) != 1)
     266             :     {
     267           0 :         CPLDebug("GRIB", "Cannot read leading bytes of section 0");
     268           0 :         return;
     269             :     }
     270         319 :     GByte nDiscipline = abySection0[7 - 1];
     271         319 :     CPLString osDiscipline;
     272         319 :     osDiscipline = CPLString().Printf("%d", nDiscipline);
     273             :     static const char *const table00[] = {"Meteorological",
     274             :                                           "Hydrological",
     275             :                                           "Land Surface",
     276             :                                           "Space products",
     277             :                                           "Space products",
     278             :                                           "Reserved",
     279             :                                           "Reserved",
     280             :                                           "Reserved",
     281             :                                           "Reserved",
     282             :                                           "Reserved",
     283             :                                           "Oceanographic Products"};
     284         319 :     m_nDisciplineCode = nDiscipline;
     285         319 :     if (nDiscipline < CPL_ARRAYSIZE(table00))
     286             :     {
     287         318 :         m_osDisciplineName = table00[nDiscipline];
     288         636 :         osDiscipline += CPLString("(") +
     289        1272 :                         CPLString(table00[nDiscipline]).replaceAll(' ', '_') +
     290         318 :                         ")";
     291             :     }
     292             : 
     293         319 :     GDALRasterBand::SetMetadataItem("GRIB_DISCIPLINE", osDiscipline.c_str());
     294             : 
     295         319 :     GByte abyHead[5] = {0};
     296             : 
     297         319 :     if (VSIFReadL(abyHead, 5, 1, poGDS->fp) != 1)
     298             :     {
     299           0 :         CPLDebug("GRIB", "Cannot read 5 leading bytes past section 0");
     300           0 :         return;
     301             :     }
     302             : 
     303         319 :     GUInt32 nSectSize = 0;
     304         319 :     if (abyHead[4] == 1)
     305             :     {
     306         319 :         memcpy(&nSectSize, abyHead, 4);
     307         319 :         CPL_MSBPTR32(&nSectSize);
     308         319 :         if (nSectSize >= 21 && nSectSize <= 100000 /* arbitrary upper limit */)
     309             :         {
     310         319 :             GByte *pabyBody = static_cast<GByte *>(CPLMalloc(nSectSize));
     311         319 :             memcpy(pabyBody, abyHead, 5);
     312         319 :             VSIFReadL(pabyBody + 5, 1, nSectSize - 5, poGDS->fp);
     313             : 
     314         638 :             CPLString osIDS;
     315         319 :             unsigned short nCenter = static_cast<unsigned short>(
     316         319 :                 pabyBody[6 - 1] * 256 + pabyBody[7 - 1]);
     317         319 :             if (nCenter != GRIB2MISSING_u1 && nCenter != GRIB2MISSING_u2)
     318             :             {
     319          86 :                 osIDS += "CENTER=";
     320          86 :                 m_nCenter = nCenter;
     321          86 :                 osIDS += CPLSPrintf("%d", nCenter);
     322          86 :                 const char *pszCenter = centerLookup(nCenter);
     323          86 :                 if (pszCenter)
     324             :                 {
     325          86 :                     m_osCenterName = pszCenter;
     326          86 :                     osIDS += CPLString("(") + pszCenter + ")";
     327             :                 }
     328             :             }
     329             : 
     330         319 :             unsigned short nSubCenter = static_cast<unsigned short>(
     331         319 :                 pabyBody[8 - 1] * 256 + pabyBody[9 - 1]);
     332         319 :             if (nSubCenter != GRIB2MISSING_u2)
     333             :             {
     334         112 :                 if (!osIDS.empty())
     335          71 :                     osIDS += " ";
     336         112 :                 osIDS += "SUBCENTER=";
     337         112 :                 osIDS += CPLSPrintf("%d", nSubCenter);
     338         112 :                 m_nSubCenter = nSubCenter;
     339         112 :                 const char *pszSubCenter = subCenterLookup(nCenter, nSubCenter);
     340         112 :                 if (pszSubCenter)
     341             :                 {
     342           3 :                     m_osSubCenterName = pszSubCenter;
     343           3 :                     osIDS += CPLString("(") + pszSubCenter + ")";
     344             :                 }
     345             :             }
     346             : 
     347         319 :             if (!osIDS.empty())
     348         127 :                 osIDS += " ";
     349         319 :             osIDS += "MASTER_TABLE=";
     350         319 :             osIDS += CPLSPrintf("%d", pabyBody[10 - 1]);
     351         319 :             osIDS += " ";
     352         319 :             osIDS += "LOCAL_TABLE=";
     353         319 :             osIDS += CPLSPrintf("%d", pabyBody[11 - 1]);
     354         319 :             osIDS += " ";
     355         319 :             osIDS += "SIGNF_REF_TIME=";
     356         319 :             unsigned nSignRefTime = pabyBody[12 - 1];
     357         319 :             osIDS += CPLSPrintf("%d", nSignRefTime);
     358             :             static const char *const table12[] = {
     359             :                 "Analysis", "Start of Forecast", "Verifying time of forecast",
     360             :                 "Observation time"};
     361         319 :             if (nSignRefTime < CPL_ARRAYSIZE(table12))
     362             :             {
     363         319 :                 m_osSignRefTimeName = table12[nSignRefTime];
     364         638 :                 osIDS += CPLString("(") +
     365        1276 :                          CPLString(table12[nSignRefTime]).replaceAll(' ', '_') +
     366         319 :                          ")";
     367             :             }
     368         319 :             osIDS += " ";
     369         319 :             osIDS += "REF_TIME=";
     370             :             m_osRefTime =
     371             :                 CPLSPrintf("%04d-%02d-%02dT%02d:%02d:%02dZ",
     372         319 :                            pabyBody[13 - 1] * 256 + pabyBody[14 - 1],
     373         319 :                            pabyBody[15 - 1], pabyBody[16 - 1], pabyBody[17 - 1],
     374         319 :                            pabyBody[18 - 1], pabyBody[19 - 1]);
     375         319 :             osIDS += m_osRefTime;
     376         319 :             osIDS += " ";
     377         319 :             osIDS += "PROD_STATUS=";
     378         319 :             unsigned nProdStatus = pabyBody[20 - 1];
     379         319 :             osIDS += CPLSPrintf("%d", nProdStatus);
     380             :             static const char *const table13[] = {
     381             :                 "Operational",     "Operational test",
     382             :                 "Research",        "Re-analysis",
     383             :                 "TIGGE",           "TIGGE test",
     384             :                 "S2S operational", "S2S test",
     385             :                 "UERRA",           "UERRA test"};
     386         319 :             if (nProdStatus < CPL_ARRAYSIZE(table13))
     387             :             {
     388         126 :                 m_osProductionStatus = table13[nProdStatus];
     389         252 :                 osIDS += CPLString("(") +
     390         504 :                          CPLString(table13[nProdStatus]).replaceAll(' ', '_') +
     391         126 :                          ")";
     392             :             }
     393         319 :             osIDS += " ";
     394         319 :             osIDS += "TYPE=";
     395         319 :             unsigned nType = pabyBody[21 - 1];
     396         319 :             osIDS += CPLSPrintf("%d", nType);
     397             :             static const char *const table14[] = {
     398             :                 "Analysis",
     399             :                 "Forecast",
     400             :                 "Analysis and forecast",
     401             :                 "Control forecast",
     402             :                 "Perturbed forecast",
     403             :                 "Control and perturbed forecast",
     404             :                 "Processed satellite observations",
     405             :                 "Processed radar observations",
     406             :                 "Event Probability"};
     407         319 :             if (nType < CPL_ARRAYSIZE(table14))
     408             :             {
     409         126 :                 m_osType = table14[nType];
     410         252 :                 osIDS += CPLString("(") +
     411         378 :                          CPLString(table14[nType]).replaceAll(' ', '_') + ")";
     412             :             }
     413             : 
     414         319 :             GDALRasterBand::SetMetadataItem("GRIB_IDS", osIDS);
     415             : 
     416         319 :             CPLFree(pabyBody);
     417             :         }
     418             : 
     419         319 :         if (VSIFReadL(abyHead, 5, 1, poGDS->fp) != 1)
     420             :         {
     421           0 :             CPLDebug("GRIB", "Cannot read 5 leading bytes past section 1");
     422           0 :             return;
     423             :         }
     424             :     }
     425             : 
     426         319 :     if (subgNum > 0)
     427             :     {
     428             :         // If we are a subgrid, then iterate over all preceding subgrids
     429          14 :         for (int iSubMessage = 0; iSubMessage < subgNum;)
     430             :         {
     431          12 :             memcpy(&nSectSize, abyHead, 4);
     432          12 :             CPL_MSBPTR32(&nSectSize);
     433          12 :             if (nSectSize < 5)
     434             :             {
     435           0 :                 CPLDebug("GRIB", "Invalid section size for iSubMessage = %d",
     436             :                          iSubMessage);
     437           0 :                 return;
     438             :             }
     439          12 :             if (VSIFSeekL(poGDS->fp, nSectSize - 5, SEEK_CUR) != 0)
     440             :             {
     441           0 :                 CPLDebug("GRIB",
     442             :                          "Cannot read past section for iSubMessage = %d",
     443             :                          iSubMessage);
     444           0 :                 return;
     445             :             }
     446          12 :             if (abyHead[4] < 2 || abyHead[4] > 7)
     447             :             {
     448           0 :                 CPLDebug("GRIB", "Invalid section number for iSubMessage = %d",
     449             :                          iSubMessage);
     450           0 :                 return;
     451             :             }
     452          12 :             if (abyHead[4] == 7)
     453             :             {
     454           2 :                 ++iSubMessage;
     455             :             }
     456          12 :             if (VSIFReadL(abyHead, 5, 1, poGDS->fp) != 1)
     457             :             {
     458           0 :                 CPLDebug("GRIB",
     459             :                          "Cannot read 5 leading bytes for iSubMessage = %d",
     460             :                          iSubMessage);
     461           0 :                 return;
     462             :             }
     463             :         }
     464             :     }
     465             : 
     466             :     // Skip to section 4
     467         930 :     while (abyHead[4] != 4)
     468             :     {
     469         611 :         memcpy(&nSectSize, abyHead, 4);
     470         611 :         CPL_MSBPTR32(&nSectSize);
     471             : 
     472         611 :         const int nCurSection = abyHead[4];
     473         611 :         if (nSectSize < 5)
     474             :         {
     475           0 :             CPLDebug("GRIB", "Invalid section size for section %d",
     476             :                      nCurSection);
     477           0 :             return;
     478             :         }
     479        1222 :         if (VSIFSeekL(poGDS->fp, nSectSize - 5, SEEK_CUR) != 0 ||
     480         611 :             VSIFReadL(abyHead, 5, 1, poGDS->fp) != 1)
     481             :         {
     482           0 :             CPLDebug("GRIB", "Cannot read section %d", nCurSection);
     483           0 :             return;
     484             :         }
     485             :     }
     486             : 
     487             :     // Collect section 4 octet information.  We read the file
     488             :     // ourselves since the GRIB API does not appear to preserve all
     489             :     // this for us.
     490             :     // if( abyHead[4] == 4 )
     491             :     {
     492         319 :         memcpy(&nSectSize, abyHead, 4);
     493         319 :         CPL_MSBPTR32(&nSectSize);
     494         319 :         if (nSectSize >= 9 && nSectSize <= 100000 /* arbitrary upper limit */)
     495             :         {
     496         319 :             GByte *pabyBody = static_cast<GByte *>(CPLMalloc(nSectSize));
     497         319 :             memcpy(pabyBody, abyHead, 5);
     498         319 :             if (VSIFReadL(pabyBody + 5, 1, nSectSize - 5, poGDS->fp) !=
     499         319 :                 nSectSize - 5)
     500             :             {
     501           0 :                 CPLDebug("GRIB", "Cannot read section 4");
     502           0 :                 CPLFree(pabyBody);
     503           0 :                 return;
     504             :             }
     505             : 
     506         319 :             GUInt16 nCoordCount = 0;
     507         319 :             memcpy(&nCoordCount, pabyBody + 6 - 1, 2);
     508         319 :             CPL_MSBPTR16(&nCoordCount);
     509             : 
     510         319 :             GUInt16 nPDTN = 0;
     511         319 :             memcpy(&nPDTN, pabyBody + 8 - 1, 2);
     512         319 :             CPL_MSBPTR16(&nPDTN);
     513             : 
     514         319 :             GDALRasterBand::SetMetadataItem("GRIB_PDS_PDTN",
     515         638 :                                             CPLString().Printf("%d", nPDTN));
     516         319 :             m_nPDTN = nPDTN;
     517             : 
     518         638 :             CPLString osOctet;
     519         319 :             const int nTemplateFoundByteCount =
     520         319 :                 nSectSize - 9U >= nCoordCount * 4U
     521         319 :                     ? static_cast<int>(nSectSize - 9 - nCoordCount * 4)
     522             :                     : 0;
     523        9040 :             for (int i = 0; i < nTemplateFoundByteCount; i++)
     524             :             {
     525        8721 :                 char szByte[10] = {'\0'};
     526             : 
     527        8721 :                 if (i == 0)
     528         319 :                     snprintf(szByte, sizeof(szByte), "%d", pabyBody[i + 9]);
     529             :                 else
     530        8402 :                     snprintf(szByte, sizeof(szByte), " %d", pabyBody[i + 9]);
     531        8721 :                 osOctet += szByte;
     532             :             }
     533             : 
     534         319 :             GDALRasterBand::SetMetadataItem("GRIB_PDS_TEMPLATE_NUMBERS",
     535             :                                             osOctet);
     536             : 
     537         319 :             g2int iofst = 0;
     538         319 :             g2int pdsnum = 0;
     539         319 :             g2int *pdstempl = nullptr;
     540         319 :             g2int mappdslen = 0;
     541         319 :             g2float *coordlist = nullptr;
     542         319 :             g2int numcoord = 0;
     543         319 :             if (getpdsindex(nPDTN) < 0)
     544             :             {
     545           3 :                 CPLError(CE_Warning, CPLE_NotSupported,
     546             :                          "Template 4.%d is not recognized currently", nPDTN);
     547             :             }
     548         316 :             else if (g2_unpack4(pabyBody, nSectSize, &iofst, &pdsnum, &pdstempl,
     549         316 :                                 &mappdslen, &coordlist, &numcoord) == 0)
     550             :             {
     551         316 :                 gtemplate *mappds = extpdstemplate(pdsnum, pdstempl);
     552         316 :                 if (mappds)
     553             :                 {
     554         316 :                     int nTemplateByteCount = 0;
     555        5448 :                     for (int i = 0; i < mappds->maplen; i++)
     556        5132 :                         nTemplateByteCount += abs(mappds->map[i]);
     557         381 :                     for (int i = 0; i < mappds->extlen; i++)
     558          65 :                         nTemplateByteCount += abs(mappds->ext[i]);
     559         316 :                     if (nTemplateByteCount == nTemplateFoundByteCount)
     560             :                     {
     561         628 :                         CPLString osValues;
     562        5479 :                         for (g2int i = 0; i < mappds->maplen + mappds->extlen;
     563             :                              i++)
     564             :                         {
     565        5165 :                             if (i > 0)
     566        4851 :                                 osValues += " ";
     567        5165 :                             const int nEltSize =
     568        5165 :                                 (i < mappds->maplen)
     569        5165 :                                     ? mappds->map[i]
     570          65 :                                     : mappds->ext[i - mappds->maplen];
     571        5165 :                             if (nEltSize == 4)
     572             :                             {
     573          88 :                                 m_anPDSTemplateAssembledValues.push_back(
     574          88 :                                     static_cast<GUInt32>(pdstempl[i]));
     575             :                                 osValues += CPLSPrintf(
     576          88 :                                     "%u", static_cast<GUInt32>(pdstempl[i]));
     577             :                             }
     578             :                             else
     579             :                             {
     580        5077 :                                 m_anPDSTemplateAssembledValues.push_back(
     581        5077 :                                     pdstempl[i]);
     582        5077 :                                 osValues += CPLSPrintf("%d", pdstempl[i]);
     583             :                             }
     584             :                         }
     585         314 :                         GDALRasterBand::SetMetadataItem(
     586             :                             "GRIB_PDS_TEMPLATE_ASSEMBLED_VALUES", osValues);
     587             :                     }
     588             :                     else
     589             :                     {
     590           2 :                         CPLDebug(
     591             :                             "GRIB",
     592             :                             "Cannot expose GRIB_PDS_TEMPLATE_ASSEMBLED_VALUES "
     593             :                             "as we would expect %d bytes from the "
     594             :                             "tables, but %d are available",
     595             :                             nTemplateByteCount, nTemplateFoundByteCount);
     596             :                     }
     597             : 
     598         316 :                     free(mappds->ext);
     599         316 :                     free(mappds);
     600             :                 }
     601             :             }
     602         319 :             free(pdstempl);
     603         319 :             free(coordlist);
     604             : 
     605         319 :             CPLFree(pabyBody);
     606             : 
     607         638 :             FindNoDataGrib2(false);
     608             :         }
     609             :         else
     610             :         {
     611           0 :             CPLDebug("GRIB", "Invalid section size for section %d", 4);
     612             :         }
     613             :     }
     614             : }
     615             : 
     616             : /************************************************************************/
     617             : /*                        FindNoDataGrib2()                             */
     618             : /************************************************************************/
     619             : 
     620         319 : void GRIBRasterBand::FindNoDataGrib2(bool bSeekToStart)
     621             : {
     622             :     // There is no easy way in the degrib API to retrieve the nodata value
     623             :     // without decompressing the data point section (which is slow), so
     624             :     // retrieve nodata value by parsing section 5 (Data Representation Section)
     625             :     // We also check section 6 to see if there is a bitmap
     626         319 :     GRIBDataset *poGDS = static_cast<GRIBDataset *>(poDS);
     627         319 :     CPLAssert(m_nGribVersion == 2);
     628             : 
     629         319 :     if (m_bHasLookedForNoData)
     630           0 :         return;
     631         319 :     m_bHasLookedForNoData = true;
     632             : 
     633         319 :     if (bSeekToStart)
     634             :     {
     635             :         // Skip over section 0
     636           0 :         VSIFSeekL(poGDS->fp, start + 16, SEEK_SET);
     637             :     }
     638             : 
     639         319 :     GByte abyHead[5] = {0};
     640         319 :     VSIFReadL(abyHead, 5, 1, poGDS->fp);
     641             : 
     642             :     // Skip to section 5
     643         319 :     GUInt32 nSectSize = 0;
     644         319 :     while (abyHead[4] != 5)
     645             :     {
     646           0 :         memcpy(&nSectSize, abyHead, 4);
     647           0 :         CPL_MSBPTR32(&nSectSize);
     648             : 
     649           0 :         if (nSectSize < 5 ||
     650           0 :             VSIFSeekL(poGDS->fp, nSectSize - 5, SEEK_CUR) != 0 ||
     651           0 :             VSIFReadL(abyHead, 5, 1, poGDS->fp) != 1)
     652           0 :             break;
     653             :     }
     654             : 
     655             :     // See http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_sect5.shtml
     656         319 :     if (abyHead[4] == 5)
     657             :     {
     658         319 :         memcpy(&nSectSize, abyHead, 4);
     659         319 :         CPL_MSBPTR32(&nSectSize);
     660         319 :         if (nSectSize >= 11 && nSectSize <= 100000 /* arbitrary upper limit */)
     661             :         {
     662         319 :             GByte *pabyBody = static_cast<GByte *>(CPLMalloc(nSectSize));
     663         319 :             memcpy(pabyBody, abyHead, 5);
     664         319 :             VSIFReadL(pabyBody + 5, 1, nSectSize - 5, poGDS->fp);
     665             : 
     666         319 :             GUInt16 nDRTN = 0;
     667         319 :             memcpy(&nDRTN, pabyBody + 10 - 1, 2);
     668         319 :             CPL_MSBPTR16(&nDRTN);
     669             : 
     670         319 :             GDALRasterBand::SetMetadataItem("DRS_DRTN", CPLSPrintf("%d", nDRTN),
     671             :                                             "GRIB");
     672         319 :             if ((nDRTN == GS5_SIMPLE || nDRTN == GS5_CMPLX ||
     673         176 :                  nDRTN == GS5_CMPLXSEC || nDRTN == GS5_JPEG2000 ||
     674         120 :                  nDRTN == GS5_PNG) &&
     675         229 :                 nSectSize >= 20)
     676             :             {
     677             :                 float fRef;
     678         229 :                 memcpy(&fRef, pabyBody + 12 - 1, 4);
     679         229 :                 CPL_MSBPTR32(&fRef);
     680         229 :                 GDALRasterBand::SetMetadataItem(
     681             :                     "DRS_REF_VALUE", CPLSPrintf("%.10f", fRef), "GRIB");
     682             : 
     683             :                 GUInt16 nBinaryScaleFactorUnsigned;
     684         229 :                 memcpy(&nBinaryScaleFactorUnsigned, pabyBody + 16 - 1, 2);
     685         229 :                 CPL_MSBPTR16(&nBinaryScaleFactorUnsigned);
     686         229 :                 const int nBSF =
     687             :                     (nBinaryScaleFactorUnsigned & 0x8000)
     688          25 :                         ? -static_cast<int>(nBinaryScaleFactorUnsigned & 0x7FFF)
     689         229 :                         : static_cast<int>(nBinaryScaleFactorUnsigned);
     690         229 :                 GDALRasterBand::SetMetadataItem("DRS_BINARY_SCALE_FACTOR",
     691             :                                                 CPLSPrintf("%d", nBSF), "GRIB");
     692             : 
     693             :                 GUInt16 nDecimalScaleFactorUnsigned;
     694         229 :                 memcpy(&nDecimalScaleFactorUnsigned, pabyBody + 18 - 1, 2);
     695         229 :                 CPL_MSBPTR16(&nDecimalScaleFactorUnsigned);
     696         229 :                 const int nDSF =
     697             :                     (nDecimalScaleFactorUnsigned & 0x8000)
     698          19 :                         ? -static_cast<int>(nDecimalScaleFactorUnsigned &
     699             :                                             0x7FFF)
     700         229 :                         : static_cast<int>(nDecimalScaleFactorUnsigned);
     701         229 :                 GDALRasterBand::SetMetadataItem("DRS_DECIMAL_SCALE_FACTOR",
     702             :                                                 CPLSPrintf("%d", nDSF), "GRIB");
     703             : 
     704         229 :                 const int nBits = pabyBody[20 - 1];
     705         229 :                 GDALRasterBand::SetMetadataItem(
     706             :                     "DRS_NBITS", CPLSPrintf("%d", nBits), "GRIB");
     707             :             }
     708             : 
     709             :             // 2 = Grid Point Data - Complex Packing
     710             :             // 3 = Grid Point Data - Complex Packing and Spatial Differencing
     711         319 :             if ((nDRTN == GS5_CMPLX || nDRTN == GS5_CMPLXSEC) &&
     712          54 :                 nSectSize >= 31)
     713             :             {
     714          54 :                 const int nMiss = pabyBody[23 - 1];
     715          54 :                 if (nMiss == 1 || nMiss == 2)
     716             :                 {
     717          35 :                     const int original_field_type = pabyBody[21 - 1];
     718          35 :                     if (original_field_type == 0)  // Floating Point
     719             :                     {
     720             :                         float fTemp;
     721          27 :                         memcpy(&fTemp, &pabyBody[24 - 1], 4);
     722          27 :                         CPL_MSBPTR32(&fTemp);
     723          27 :                         m_dfNoData = fTemp;
     724          27 :                         m_bHasNoData = true;
     725          27 :                         if (nMiss == 2)
     726             :                         {
     727           0 :                             memcpy(&fTemp, &pabyBody[28 - 1], 4);
     728           0 :                             CPL_MSBPTR32(&fTemp);
     729           0 :                             CPLDebug("GRIB",
     730             :                                      "Secondary missing value also set for "
     731             :                                      "band %d : %f",
     732             :                                      nBand, fTemp);
     733             :                         }
     734             :                     }
     735           8 :                     else if (original_field_type == 1)  // Integer
     736             :                     {
     737             :                         int iTemp;
     738           8 :                         memcpy(&iTemp, &pabyBody[24 - 1], 4);
     739           8 :                         CPL_MSBPTR32(&iTemp);
     740           8 :                         m_dfNoData = iTemp;
     741           8 :                         m_bHasNoData = true;
     742           8 :                         if (nMiss == 2)
     743             :                         {
     744           0 :                             memcpy(&iTemp, &pabyBody[28 - 1], 4);
     745           0 :                             CPL_MSBPTR32(&iTemp);
     746           0 :                             CPLDebug("GRIB",
     747             :                                      "Secondary missing value also set for "
     748             :                                      "band %d : %d",
     749             :                                      nBand, iTemp);
     750             :                         }
     751             :                     }
     752             :                     else
     753             :                     {
     754             :                         // FIXME What to do? Blindly convert to float?
     755           0 :                         CPLDebug("GRIB",
     756             :                                  "Complex Packing - Type of Original Field "
     757             :                                  "Values for band %d:  %u",
     758             :                                  nBand, original_field_type);
     759             :                     }
     760             :                 }
     761             :             }
     762             : 
     763         319 :             if (nDRTN == GS5_CMPLXSEC && nSectSize >= 48)
     764             :             {
     765          21 :                 const int nOrder = pabyBody[48 - 1];
     766          21 :                 GDALRasterBand::SetMetadataItem(
     767             :                     "DRS_SPATIAL_DIFFERENCING_ORDER", CPLSPrintf("%d", nOrder),
     768             :                     "GRIB");
     769             :             }
     770             : 
     771         319 :             CPLFree(pabyBody);
     772             :         }
     773           0 :         else if (nSectSize > 5)
     774             :         {
     775           0 :             VSIFSeekL(poGDS->fp, nSectSize - 5, SEEK_CUR);
     776             :         }
     777             :     }
     778             : 
     779         319 :     if (!m_bHasNoData)
     780             :     {
     781             :         // Check bitmap section
     782         284 :         GByte abySection6[6] = {0};
     783         284 :         VSIFReadL(abySection6, 6, 1, poGDS->fp);
     784             :         // Is there a bitmap ?
     785         284 :         if (abySection6[4] == 6 && abySection6[5] == 0)
     786             :         {
     787           6 :             m_dfNoData = 9999.0;  // Same value as in metaparse.cpp:ParseGrid()
     788           6 :             m_bHasNoData = true;
     789             :         }
     790             :     }
     791             : }
     792             : 
     793             : /************************************************************************/
     794             : /*                         GetDescription()                             */
     795             : /************************************************************************/
     796             : 
     797          23 : const char *GRIBRasterBand::GetDescription() const
     798             : {
     799          23 :     if (longFstLevel == nullptr)
     800           0 :         return GDALPamRasterBand::GetDescription();
     801             : 
     802          23 :     return longFstLevel;
     803             : }
     804             : 
     805             : /************************************************************************/
     806             : /*                             LoadData()                               */
     807             : /************************************************************************/
     808             : 
     809        9044 : CPLErr GRIBRasterBand::LoadData()
     810             : 
     811             : {
     812        9044 :     if (!m_Grib_Data)
     813             :     {
     814         178 :         GRIBDataset *poGDS = static_cast<GRIBDataset *>(poDS);
     815             : 
     816         178 :         if (poGDS->bCacheOnlyOneBand)
     817             :         {
     818             :             // In "one-band-at-a-time" strategy, if the last recently used
     819             :             // band is not that one, uncache it. We could use a smarter strategy
     820             :             // based on a LRU, but that's a bit overkill for now.
     821           0 :             poGDS->poLastUsedBand->UncacheData();
     822           0 :             poGDS->nCachedBytes = 0;
     823             :         }
     824             :         else
     825             :         {
     826             :             // Once we have cached more than nCachedBytesThreshold bytes, we
     827             :             // will switch to "one-band-at-a-time" strategy, instead of caching
     828             :             // all bands that have been accessed.
     829         178 :             if (poGDS->nCachedBytes > poGDS->nCachedBytesThreshold)
     830             :             {
     831             :                 GUIntBig nMinCacheSize =
     832           0 :                     1 + static_cast<GUIntBig>(poGDS->nRasterXSize) *
     833           0 :                             poGDS->nRasterYSize * poGDS->nBands *
     834           0 :                             GDALGetDataTypeSizeBytes(eDataType) / 1024 / 1024;
     835           0 :                 CPLDebug("GRIB",
     836             :                          "Maximum band cache size reached for this dataset. "
     837             :                          "Caching only one band at a time from now, which can "
     838             :                          "negatively affect performance. Consider "
     839             :                          "increasing GRIB_CACHEMAX to a higher value (in MB), "
     840             :                          "at least " CPL_FRMT_GUIB " in that instance",
     841             :                          nMinCacheSize);
     842           0 :                 for (int i = 0; i < poGDS->nBands; i++)
     843             :                 {
     844             :                     reinterpret_cast<GRIBRasterBand *>(
     845           0 :                         poGDS->GetRasterBand(i + 1))
     846           0 :                         ->UncacheData();
     847             :                 }
     848           0 :                 poGDS->nCachedBytes = 0;
     849           0 :                 poGDS->bCacheOnlyOneBand = TRUE;
     850             :             }
     851             :         }
     852             : 
     853             :         // we don't seem to have any way to detect errors in this!
     854         178 :         if (m_Grib_MetaData != nullptr)
     855             :         {
     856         133 :             MetaFree(m_Grib_MetaData);
     857         133 :             delete m_Grib_MetaData;
     858         133 :             m_Grib_MetaData = nullptr;
     859             :         }
     860         178 :         ReadGribData(poGDS->fp, start, subgNum, &m_Grib_Data, &m_Grib_MetaData);
     861         178 :         if (!m_Grib_Data)
     862             :         {
     863           0 :             CPLError(CE_Failure, CPLE_AppDefined, "Out of memory.");
     864           0 :             if (m_Grib_MetaData != nullptr)
     865             :             {
     866           0 :                 MetaFree(m_Grib_MetaData);
     867           0 :                 delete m_Grib_MetaData;
     868           0 :                 m_Grib_MetaData = nullptr;
     869             :             }
     870           0 :             return CE_Failure;
     871             :         }
     872             : 
     873             :         // Check the band matches the dataset as a whole, size wise. (#3246)
     874         178 :         nGribDataXSize = m_Grib_MetaData->gds.Nx;
     875         178 :         nGribDataYSize = m_Grib_MetaData->gds.Ny;
     876         178 :         if (nGribDataXSize <= 0 || nGribDataYSize <= 0)
     877             :         {
     878           0 :             CPLError(CE_Failure, CPLE_AppDefined,
     879             :                      "Band %d of GRIB dataset is %dx%d.", nBand, nGribDataXSize,
     880             :                      nGribDataYSize);
     881           0 :             MetaFree(m_Grib_MetaData);
     882           0 :             delete m_Grib_MetaData;
     883           0 :             m_Grib_MetaData = nullptr;
     884           0 :             return CE_Failure;
     885             :         }
     886             : 
     887         178 :         poGDS->nCachedBytes += static_cast<GIntBig>(nGribDataXSize) *
     888         178 :                                nGribDataYSize * sizeof(double);
     889         178 :         poGDS->poLastUsedBand = this;
     890             : 
     891         178 :         if (nGribDataXSize != nRasterXSize || nGribDataYSize != nRasterYSize)
     892             :         {
     893          24 :             CPLError(CE_Warning, CPLE_AppDefined,
     894             :                      "Band %d of GRIB dataset is %dx%d, while the first band "
     895             :                      "and dataset is %dx%d.  Georeferencing of band %d may "
     896             :                      "be incorrect, and data access may be incomplete.",
     897             :                      nBand, nGribDataXSize, nGribDataYSize, nRasterXSize,
     898             :                      nRasterYSize, nBand);
     899             :         }
     900             :     }
     901             : 
     902        9044 :     return CE_None;
     903             : }
     904             : 
     905             : /************************************************************************/
     906             : /*                             GetMetaData()                            */
     907             : /************************************************************************/
     908          86 : char **GRIBRasterBand::GetMetadata(const char *pszDomain)
     909             : {
     910          86 :     FindMetaData();
     911         138 :     if (m_nGribVersion == 2 &&
     912          52 :         CPLTestBool(CPLGetConfigOption("GRIB_PDS_ALL_BANDS", "ON")))
     913             :     {
     914          52 :         FindPDSTemplateGRIB2();
     915             :     }
     916          86 :     return GDALPamRasterBand::GetMetadata(pszDomain);
     917             : }
     918             : 
     919             : /************************************************************************/
     920             : /*                             GetMetaDataItem()                        */
     921             : /************************************************************************/
     922         693 : const char *GRIBRasterBand::GetMetadataItem(const char *pszName,
     923             :                                             const char *pszDomain)
     924             : {
     925         693 :     FindMetaData();
     926        1231 :     if (m_nGribVersion == 2 &&
     927         538 :         CPLTestBool(CPLGetConfigOption("GRIB_PDS_ALL_BANDS", "ON")))
     928             :     {
     929         537 :         FindPDSTemplateGRIB2();
     930             :     }
     931         693 :     return GDALPamRasterBand::GetMetadataItem(pszName, pszDomain);
     932             : }
     933             : 
     934             : /************************************************************************/
     935             : /*                             IReadBlock()                             */
     936             : /************************************************************************/
     937             : 
     938        9044 : CPLErr GRIBRasterBand::IReadBlock(int /* nBlockXOff */, int nBlockYOff,
     939             :                                   void *pImage)
     940             : 
     941             : {
     942        9044 :     CPLErr eErr = LoadData();
     943        9044 :     if (eErr != CE_None)
     944           0 :         return eErr;
     945             : 
     946        9044 :     GRIBDataset *poGDS = static_cast<GRIBDataset *>(poDS);
     947             : 
     948             :     // The image as read is always upside down to our normal
     949             :     // orientation so we need to effectively flip it at this
     950             :     // point.  We also need to deal with bands that are a different
     951             :     // size than the dataset as a whole.
     952             : 
     953        9044 :     if (nGribDataXSize == nRasterXSize && nGribDataYSize == nRasterYSize &&
     954        8552 :         poGDS->nSplitAndSwapColumn == 0)
     955             :     {
     956             :         // Simple 1:1 case.
     957        6893 :         memcpy(pImage,
     958        6893 :                m_Grib_Data + static_cast<size_t>(nRasterXSize) *
     959        6893 :                                  (nRasterYSize - nBlockYOff - 1),
     960        6893 :                nRasterXSize * sizeof(double));
     961             : 
     962        6893 :         return CE_None;
     963             :     }
     964             : 
     965        2151 :     memset(pImage, 0, sizeof(double) * nRasterXSize);
     966             : 
     967        2151 :     if (nBlockYOff >= nGribDataYSize)  // Off image?
     968          57 :         return CE_None;
     969             : 
     970        2094 :     int nSplitAndSwapColumn = poGDS->nSplitAndSwapColumn;
     971        2094 :     if (nRasterXSize != nGribDataXSize)
     972         435 :         nSplitAndSwapColumn = 0;
     973             : 
     974        2094 :     const int nCopyWords = std::min(nRasterXSize, nGribDataXSize);
     975             : 
     976        2094 :     memcpy(pImage,
     977        2094 :            m_Grib_Data +
     978        2094 :                static_cast<size_t>(nGribDataXSize) *
     979        2094 :                    (nGribDataYSize - nBlockYOff - 1) +
     980             :                nSplitAndSwapColumn,
     981        2094 :            (nCopyWords - nSplitAndSwapColumn) * sizeof(double));
     982             : 
     983        2094 :     if (nSplitAndSwapColumn > 0)
     984        1659 :         memcpy(reinterpret_cast<void *>(reinterpret_cast<double *>(pImage) +
     985        1659 :                                         nCopyWords - nSplitAndSwapColumn),
     986        1659 :                m_Grib_Data + static_cast<size_t>(nGribDataXSize) *
     987        1659 :                                  (nGribDataYSize - nBlockYOff - 1),
     988        1659 :                nSplitAndSwapColumn * sizeof(double));
     989             : 
     990        2094 :     return CE_None;
     991             : }
     992             : 
     993             : /************************************************************************/
     994             : /*                           GetNoDataValue()                           */
     995             : /************************************************************************/
     996             : 
     997         125 : double GRIBRasterBand::GetNoDataValue(int *pbSuccess)
     998             : {
     999         125 :     if (m_bHasLookedForNoData)
    1000             :     {
    1001         105 :         if (pbSuccess)
    1002         105 :             *pbSuccess = m_bHasNoData;
    1003         105 :         return m_dfNoData;
    1004             :     }
    1005             : 
    1006          20 :     m_bHasLookedForNoData = true;
    1007          20 :     if (m_Grib_MetaData == nullptr)
    1008             :     {
    1009          18 :         GRIBDataset *poGDS = static_cast<GRIBDataset *>(poDS);
    1010          18 :         ReadGribData(poGDS->fp, start, subgNum, nullptr, &m_Grib_MetaData);
    1011          18 :         if (m_Grib_MetaData == nullptr)
    1012             :         {
    1013           0 :             m_bHasNoData = false;
    1014           0 :             m_dfNoData = 0;
    1015           0 :             if (pbSuccess)
    1016           0 :                 *pbSuccess = m_bHasNoData;
    1017           0 :             return m_dfNoData;
    1018             :         }
    1019             :     }
    1020             : 
    1021          20 :     if (m_Grib_MetaData->gridAttrib.f_miss == 0)
    1022             :     {
    1023           4 :         m_bHasNoData = false;
    1024           4 :         m_dfNoData = 0;
    1025           4 :         if (pbSuccess)
    1026           4 :             *pbSuccess = m_bHasNoData;
    1027           4 :         return m_dfNoData;
    1028             :     }
    1029             : 
    1030          16 :     if (m_Grib_MetaData->gridAttrib.f_miss == 2)
    1031             :     {
    1032             :         // What TODO?
    1033           0 :         CPLDebug("GRIB", "Secondary missing value also set for band %d : %f",
    1034           0 :                  nBand, m_Grib_MetaData->gridAttrib.missSec);
    1035             :     }
    1036             : 
    1037          16 :     m_bHasNoData = true;
    1038          16 :     m_dfNoData = m_Grib_MetaData->gridAttrib.missPri;
    1039          16 :     if (pbSuccess)
    1040          16 :         *pbSuccess = m_bHasNoData;
    1041          16 :     return m_dfNoData;
    1042             : }
    1043             : 
    1044             : /************************************************************************/
    1045             : /*                            ReadGribData()                            */
    1046             : /************************************************************************/
    1047             : 
    1048         523 : void GRIBRasterBand::ReadGribData(VSILFILE *fp, vsi_l_offset start, int subgNum,
    1049             :                                   double **data, grib_MetaData **metaData)
    1050             : {
    1051             :     // Initialization, for calling the ReadGrib2Record function.
    1052         523 :     sInt4 f_endMsg = 1;  // 1 if we read the last grid in a GRIB message, or we
    1053             :                          // haven't read any messages.
    1054             :     // int subgNum = 0; // The subgrid in the message that we are interested in.
    1055         523 :     sChar f_unit = 2;       // None = 0, English = 1, Metric = 2
    1056         523 :     double majEarth = 0.0;  // -radEarth if < 6000 ignore, otherwise use this
    1057             :                             // to override the radEarth in the GRIB1 or GRIB2
    1058             :                             // message.  Needed because NCEP uses 6371.2 but
    1059             :                             // GRIB1 could only state 6367.47.
    1060         523 :     double minEarth = 0.0;  // -minEarth if < 6000 ignore, otherwise use this
    1061             :                             // to override the minEarth in the GRIB1 or GRIB2
    1062             :                             // message.
    1063         523 :     sChar f_SimpleVer = 4;  // Which version of the simple NDFD Weather table
    1064             :                             // to use. (1 is 6/2003) (2 is 1/2004) (3 is
    1065             :                             // 2/2004) (4 is 11/2004) (default 4)
    1066             :     LatLon lwlf;            // Lower left corner (cookie slicing) -lwlf
    1067             :     LatLon uprt;            // Upper right corner (cookie slicing) -uprt
    1068             :     IS_dataType is;  // Un-parsed meta data for this GRIB2 message. As well
    1069             :                      // as some memory used by the unpacker.
    1070             : 
    1071         523 :     lwlf.lat = -100;  // lat == -100 instructs the GRIB decoder that we don't
    1072             :                       // want a subgrid
    1073             : 
    1074         523 :     IS_Init(&is);
    1075             : 
    1076             :     const char *pszGribNormalizeUnits =
    1077         523 :         CPLGetConfigOption("GRIB_NORMALIZE_UNITS", "YES");
    1078         523 :     if (!CPLTestBool(pszGribNormalizeUnits))
    1079           2 :         f_unit = 0;  // Do not normalize units to metric.
    1080             : 
    1081         523 :     start = FindTrueStart(fp, start);
    1082             :     // Read GRIB message from file position "start".
    1083         523 :     VSIFSeekL(fp, start, SEEK_SET);
    1084         523 :     uInt4 grib_DataLen = 0;  // Size of Grib_Data.
    1085         523 :     *metaData = new grib_MetaData();
    1086         523 :     MetaInit(*metaData);
    1087         523 :     const int simpWWA = 0;  // seem to be unused in degrib
    1088         523 :     ReadGrib2Record(fp, f_unit, data, &grib_DataLen, *metaData, &is, subgNum,
    1089             :                     majEarth, minEarth, f_SimpleVer, simpWWA, &f_endMsg, &lwlf,
    1090             :                     &uprt);
    1091             : 
    1092             :     // No intention to show errors, just swallow it and free the memory.
    1093         523 :     char *errMsg = errSprintf(nullptr);
    1094         523 :     if (errMsg != nullptr)
    1095         339 :         CPLDebug("GRIB", "%s", errMsg);
    1096         523 :     free(errMsg);
    1097         523 :     IS_Free(&is);
    1098         523 : }
    1099             : 
    1100             : /************************************************************************/
    1101             : /*                            UncacheData()                             */
    1102             : /************************************************************************/
    1103             : 
    1104         431 : void GRIBRasterBand::UncacheData()
    1105             : {
    1106         431 :     if (m_Grib_Data)
    1107         178 :         free(m_Grib_Data);
    1108         431 :     m_Grib_Data = nullptr;
    1109         431 :     if (m_Grib_MetaData)
    1110             :     {
    1111         362 :         MetaFree(m_Grib_MetaData);
    1112         362 :         delete m_Grib_MetaData;
    1113             :     }
    1114         431 :     m_Grib_MetaData = nullptr;
    1115         431 : }
    1116             : 
    1117             : /************************************************************************/
    1118             : /*                           ~GRIBRasterBand()                          */
    1119             : /************************************************************************/
    1120             : 
    1121         839 : GRIBRasterBand::~GRIBRasterBand()
    1122             : {
    1123         431 :     if (longFstLevel != nullptr)
    1124         431 :         CPLFree(longFstLevel);
    1125         431 :     UncacheData();
    1126         839 : }
    1127             : 
    1128             : /************************************************************************/
    1129             : /*                           InventoryWrapperGrib                       */
    1130             : /************************************************************************/
    1131             : class InventoryWrapperGrib : public gdal::grib::InventoryWrapper
    1132             : {
    1133             :   public:
    1134         304 :     explicit InventoryWrapperGrib(VSILFILE *fp) : gdal::grib::InventoryWrapper()
    1135             :     {
    1136         304 :         result_ = GRIB2Inventory(fp, &inv_, &inv_len_, 0 /* all messages */,
    1137             :                                  &num_messages_);
    1138         304 :     }
    1139             : 
    1140         608 :     ~InventoryWrapperGrib() override
    1141         304 :     {
    1142         304 :         if (inv_ == nullptr)
    1143           0 :             return;
    1144         719 :         for (uInt4 i = 0; i < inv_len_; i++)
    1145             :         {
    1146         415 :             GRIB2InventoryFree(inv_ + i);
    1147             :         }
    1148         304 :         free(inv_);
    1149         608 :     }
    1150             : };
    1151             : 
    1152             : /************************************************************************/
    1153             : /*                           InventoryWrapperSidecar                    */
    1154             : /************************************************************************/
    1155             : 
    1156             : class InventoryWrapperSidecar : public gdal::grib::InventoryWrapper
    1157             : {
    1158             :   public:
    1159           3 :     explicit InventoryWrapperSidecar(VSILFILE *fp)
    1160           3 :         : gdal::grib::InventoryWrapper()
    1161             :     {
    1162           3 :         result_ = -1;
    1163           3 :         VSIFSeekL(fp, 0, SEEK_END);
    1164           3 :         size_t length = static_cast<size_t>(VSIFTellL(fp));
    1165           3 :         if (length > 4 * 1024 * 1024)
    1166           0 :             return;
    1167           3 :         std::string psSidecar;
    1168           3 :         psSidecar.resize(length);
    1169           3 :         VSIFSeekL(fp, 0, SEEK_SET);
    1170           3 :         if (VSIFReadL(&psSidecar[0], length, 1, fp) != 1)
    1171           0 :             return;
    1172             : 
    1173             :         CPLStringList aosMsgs(
    1174             :             CSLTokenizeString2(psSidecar.c_str(), "\n",
    1175           3 :                                CSLT_PRESERVEQUOTES | CSLT_STRIPLEADSPACES));
    1176           3 :         inv_len_ = aosMsgs.size();
    1177           3 :         inv_ = static_cast<inventoryType *>(
    1178           3 :             CPLMalloc(inv_len_ * sizeof(inventoryType)));
    1179             : 
    1180          29 :         for (size_t i = 0; i < inv_len_; ++i)
    1181             :         {
    1182             :             // We are parsing
    1183             :             // "msgNum[.subgNum]:start:dontcare:name1:name2:name3" For NOMADS:
    1184             :             // "msgNum[.subgNum]:start:reftime:var:level:time"
    1185             :             CPLStringList aosTokens(CSLTokenizeString2(
    1186          26 :                 aosMsgs[i], ":", CSLT_PRESERVEQUOTES | CSLT_ALLOWEMPTYTOKENS));
    1187          26 :             CPLStringList aosNum;
    1188             : 
    1189          26 :             if (aosTokens.size() < 6)
    1190           0 :                 goto err_sidecar;
    1191             : 
    1192          26 :             aosNum = CPLStringList(CSLTokenizeString2(aosTokens[0], ".", 0));
    1193          26 :             if (aosNum.size() < 1)
    1194           0 :                 goto err_sidecar;
    1195             : 
    1196             :             // FindMetaData will retrieve the correct version number
    1197             :             char *endptr;
    1198          26 :             strtol(aosNum[0], &endptr, 10);
    1199          26 :             if (*endptr != 0)
    1200           0 :                 goto err_sidecar;
    1201             : 
    1202          26 :             if (aosNum.size() < 2)
    1203          24 :                 inv_[i].subgNum = 0;
    1204             :             else
    1205             :             {
    1206           2 :                 auto subgNum = strtol(aosNum[1], &endptr, 10);
    1207           2 :                 if (*endptr != 0)
    1208           0 :                     goto err_sidecar;
    1209           2 :                 if (subgNum <= 0 || subgNum > 65536)
    1210           0 :                     goto err_sidecar;
    1211             :                 // .idx file use a 1-based indexing, whereas DEGRIB uses a
    1212             :                 // 0-based one
    1213           2 :                 subgNum--;
    1214           2 :                 inv_[i].subgNum = static_cast<unsigned short>(subgNum);
    1215             :             }
    1216             : 
    1217          26 :             inv_[i].start = strtoll(aosTokens[1], &endptr, 10);
    1218          26 :             if (*endptr != 0)
    1219           0 :                 goto err_sidecar;
    1220             : 
    1221          26 :             inv_[i].unitName = nullptr;
    1222          26 :             inv_[i].comment = nullptr;
    1223          26 :             inv_[i].element = nullptr;
    1224          26 :             inv_[i].shortFstLevel = nullptr;
    1225             :             // This is going into the description field ->
    1226             :             // the only one available before loading the metadata
    1227          26 :             inv_[i].longFstLevel = VSIStrdup(CPLSPrintf(
    1228             :                 "%s:%s:%s", aosTokens[3], aosTokens[4], aosTokens[5]));
    1229             : 
    1230          26 :             continue;
    1231             : 
    1232           0 :         err_sidecar:
    1233           0 :             CPLDebug("GRIB",
    1234             :                      "Failed parsing sidecar entry '%s', "
    1235             :                      "falling back to constructing an inventory",
    1236             :                      aosMsgs[i]);
    1237           0 :             inv_len_ = static_cast<unsigned>(i);
    1238           0 :             return;
    1239             :         }
    1240             : 
    1241           3 :         result_ = inv_len_;
    1242             :     }
    1243             : 
    1244           6 :     ~InventoryWrapperSidecar() override
    1245           3 :     {
    1246           3 :         if (inv_ == nullptr)
    1247           0 :             return;
    1248             : 
    1249          29 :         for (unsigned i = 0; i < inv_len_; i++)
    1250          26 :             VSIFree(inv_[i].longFstLevel);
    1251             : 
    1252           3 :         VSIFree(inv_);
    1253           6 :     }
    1254             : };
    1255             : 
    1256             : /************************************************************************/
    1257             : /* ==================================================================== */
    1258             : /*                              GRIBDataset                             */
    1259             : /* ==================================================================== */
    1260             : /************************************************************************/
    1261             : 
    1262         307 : GRIBDataset::GRIBDataset()
    1263             :     : fp(nullptr), nCachedBytes(0),
    1264             :       // Switch caching strategy once 100 MB threshold is reached.
    1265             :       // Why 100 MB? --> Why not.
    1266         614 :       nCachedBytesThreshold(static_cast<GIntBig>(atoi(
    1267             :                                 CPLGetConfigOption("GRIB_CACHEMAX", "100"))) *
    1268         307 :                             1024 * 1024),
    1269         307 :       bCacheOnlyOneBand(FALSE), nSplitAndSwapColumn(0), poLastUsedBand(nullptr)
    1270             : {
    1271         307 :     adfGeoTransform[0] = 0.0;
    1272         307 :     adfGeoTransform[1] = 1.0;
    1273         307 :     adfGeoTransform[2] = 0.0;
    1274         307 :     adfGeoTransform[3] = 0.0;
    1275         307 :     adfGeoTransform[4] = 0.0;
    1276         307 :     adfGeoTransform[5] = 1.0;
    1277         307 : }
    1278             : 
    1279             : /************************************************************************/
    1280             : /*                            ~GRIBDataset()                             */
    1281             : /************************************************************************/
    1282             : 
    1283         614 : GRIBDataset::~GRIBDataset()
    1284             : 
    1285             : {
    1286         307 :     FlushCache(true);
    1287         307 :     if (fp != nullptr)
    1288         303 :         VSIFCloseL(fp);
    1289         614 : }
    1290             : 
    1291             : /************************************************************************/
    1292             : /*                          GetGeoTransform()                           */
    1293             : /************************************************************************/
    1294             : 
    1295         133 : CPLErr GRIBDataset::GetGeoTransform(double *padfTransform)
    1296             : 
    1297             : {
    1298         133 :     memcpy(padfTransform, adfGeoTransform, sizeof(double) * 6);
    1299         133 :     return CE_None;
    1300             : }
    1301             : 
    1302             : /************************************************************************/
    1303             : /*                                Inventory()                           */
    1304             : /************************************************************************/
    1305             : 
    1306             : std::unique_ptr<gdal::grib::InventoryWrapper>
    1307         303 : GRIBDataset::Inventory(VSILFILE *fp, GDALOpenInfo *poOpenInfo)
    1308             : {
    1309         303 :     std::unique_ptr<gdal::grib::InventoryWrapper> pInventories;
    1310             : 
    1311         303 :     VSIFSeekL(fp, 0, SEEK_SET);
    1312         909 :     CPLString sSideCarFilename = CPLString(poOpenInfo->pszFilename) + ".idx";
    1313         303 :     VSILFILE *fpSideCar = nullptr;
    1314         303 :     if (CPLTestBool(CSLFetchNameValueDef(poOpenInfo->papszOpenOptions,
    1315         603 :                                          "USE_IDX", "YES")) &&
    1316         300 :         ((fpSideCar = VSIFOpenL(sSideCarFilename, "rb")) != nullptr))
    1317             :     {
    1318           3 :         CPLDebug("GRIB", "Reading inventories from sidecar file %s",
    1319             :                  sSideCarFilename.c_str());
    1320             :         // Contains an GRIB2 message inventory of the file.
    1321           3 :         pInventories = std::make_unique<InventoryWrapperSidecar>(fpSideCar);
    1322           3 :         if (pInventories->result() <= 0 || pInventories->length() == 0)
    1323           0 :             pInventories = nullptr;
    1324           3 :         VSIFCloseL(fpSideCar);
    1325             :     }
    1326             :     else
    1327         300 :         CPLDebug("GRIB", "Failed opening sidecar %s", sSideCarFilename.c_str());
    1328             : 
    1329         303 :     if (pInventories == nullptr)
    1330             :     {
    1331         300 :         CPLDebug("GRIB", "Reading inventories from GRIB file %s",
    1332             :                  poOpenInfo->pszFilename);
    1333             :         // Contains an GRIB2 message inventory of the file.
    1334         300 :         pInventories = std::make_unique<InventoryWrapperGrib>(fp);
    1335             :     }
    1336             : 
    1337         606 :     return pInventories;
    1338             : }
    1339             : 
    1340             : /************************************************************************/
    1341             : /*                                Open()                                */
    1342             : /************************************************************************/
    1343             : 
    1344         309 : GDALDataset *GRIBDataset::Open(GDALOpenInfo *poOpenInfo)
    1345             : 
    1346             : {
    1347             : #ifndef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    1348             :     // During fuzzing, do not use Identify to reject crazy content.
    1349         309 :     if (!GRIBDriverIdentify(poOpenInfo))
    1350           2 :         return nullptr;
    1351             : #endif
    1352         307 :     if (poOpenInfo->fpL == nullptr)
    1353           0 :         return nullptr;
    1354             : 
    1355             :     // A fast "probe" on the header that is partially read in memory.
    1356         307 :     char *buff = nullptr;
    1357         307 :     uInt4 buffLen = 0;
    1358         307 :     sInt4 sect0[SECT0LEN_WORD] = {0};
    1359         307 :     uInt4 gribLen = 0;
    1360         307 :     int version = 0;
    1361             : 
    1362             :     // grib is not thread safe, make sure not to cause problems
    1363             :     // for other thread safe formats
    1364         614 :     CPLMutexHolderD(&hGRIBMutex);
    1365             : 
    1366         614 :     CPLString tmpFilename;
    1367         307 :     tmpFilename.Printf("/vsimem/gribdataset-%p", poOpenInfo);
    1368             : 
    1369         307 :     VSILFILE *memfp = VSIFileFromMemBuffer(tmpFilename, poOpenInfo->pabyHeader,
    1370         307 :                                            poOpenInfo->nHeaderBytes, FALSE);
    1371         614 :     if (memfp == nullptr ||
    1372         307 :         ReadSECT0(memfp, &buff, &buffLen, -1, sect0, &gribLen, &version) < 0)
    1373             :     {
    1374           0 :         if (memfp != nullptr)
    1375             :         {
    1376           0 :             VSIFCloseL(memfp);
    1377           0 :             VSIUnlink(tmpFilename);
    1378             :         }
    1379           0 :         free(buff);
    1380           0 :         char *errMsg = errSprintf(nullptr);
    1381           0 :         if (errMsg != nullptr && strstr(errMsg, "Ran out of file") == nullptr)
    1382           0 :             CPLDebug("GRIB", "%s", errMsg);
    1383           0 :         free(errMsg);
    1384           0 :         return nullptr;
    1385             :     }
    1386         307 :     VSIFCloseL(memfp);
    1387         307 :     VSIUnlink(tmpFilename);
    1388         307 :     free(buff);
    1389             : 
    1390             :     // Confirm the requested access is supported.
    1391         307 :     if (poOpenInfo->eAccess == GA_Update)
    1392             :     {
    1393           0 :         CPLError(CE_Failure, CPLE_NotSupported,
    1394             :                  "The GRIB driver does not support update access to existing "
    1395             :                  "datasets.");
    1396           0 :         return nullptr;
    1397             :     }
    1398             : 
    1399         307 :     if (poOpenInfo->nOpenFlags & GDAL_OF_MULTIDIM_RASTER)
    1400             :     {
    1401           4 :         return OpenMultiDim(poOpenInfo);
    1402             :     }
    1403             : 
    1404             :     // Create a corresponding GDALDataset.
    1405         303 :     GRIBDataset *poDS = new GRIBDataset();
    1406             : 
    1407         303 :     poDS->fp = poOpenInfo->fpL;
    1408         303 :     poOpenInfo->fpL = nullptr;
    1409             : 
    1410             :     // Make an inventory of the GRIB file.
    1411             :     // The inventory does not contain all the information needed for
    1412             :     // creating the RasterBands (especially the x and y size), therefore
    1413             :     // the first GRIB band is also read for some additional metadata.
    1414             :     // The band-data that is read is stored into the first RasterBand,
    1415             :     // simply so that the same portion of the file is not read twice.
    1416             : 
    1417         606 :     auto pInventories = Inventory(poDS->fp, poOpenInfo);
    1418         303 :     if (pInventories->result() <= 0)
    1419             :     {
    1420           9 :         char *errMsg = errSprintf(nullptr);
    1421           9 :         if (errMsg != nullptr)
    1422           8 :             CPLDebug("GRIB", "%s", errMsg);
    1423           9 :         free(errMsg);
    1424             : 
    1425           9 :         CPLError(CE_Failure, CPLE_OpenFailed,
    1426             :                  "%s is a grib file, "
    1427             :                  "but no raster dataset was successfully identified.",
    1428             :                  poOpenInfo->pszFilename);
    1429             :         // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own
    1430             :         // hGRIBMutex.
    1431           9 :         CPLReleaseMutex(hGRIBMutex);
    1432           9 :         delete poDS;
    1433           9 :         CPLAcquireMutex(hGRIBMutex, 1000.0);
    1434           9 :         return nullptr;
    1435             :     }
    1436             : 
    1437             :     // Create band objects.
    1438         702 :     for (uInt4 i = 0; i < pInventories->length(); ++i)
    1439             :     {
    1440         408 :         inventoryType *psInv = pInventories->get(i);
    1441         408 :         GRIBRasterBand *gribBand = nullptr;
    1442         408 :         uInt4 bandNr = i + 1;
    1443             : 
    1444         408 :         if (bandNr == 1)
    1445             :         {
    1446             :             // Important: set DataSet extents before creating first RasterBand
    1447             :             // in it.
    1448         294 :             grib_MetaData *metaData = nullptr;
    1449         294 :             GRIBRasterBand::ReadGribData(poDS->fp, 0, psInv->subgNum, nullptr,
    1450             :                                          &metaData);
    1451         294 :             if (metaData == nullptr || metaData->gds.Nx < 1 ||
    1452         294 :                 metaData->gds.Ny < 1)
    1453             :             {
    1454           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
    1455             :                          "%s is a grib file, "
    1456             :                          "but no raster dataset was successfully identified.",
    1457             :                          poOpenInfo->pszFilename);
    1458             :                 // Release hGRIBMutex otherwise we'll deadlock with GDALDataset
    1459             :                 // own hGRIBMutex.
    1460           0 :                 CPLReleaseMutex(hGRIBMutex);
    1461           0 :                 delete poDS;
    1462           0 :                 CPLAcquireMutex(hGRIBMutex, 1000.0);
    1463           0 :                 if (metaData != nullptr)
    1464             :                 {
    1465           0 :                     MetaFree(metaData);
    1466           0 :                     delete metaData;
    1467             :                 }
    1468           0 :                 return nullptr;
    1469             :             }
    1470         294 :             psInv->GribVersion = metaData->GribVersion;
    1471             : 
    1472             :             // Set the DataSet's x,y size, georeference and projection from
    1473             :             // the first GRIB band.
    1474         294 :             poDS->SetGribMetaData(metaData);
    1475         294 :             gribBand = new GRIBRasterBand(poDS, bandNr, psInv);
    1476             : 
    1477         294 :             if (psInv->GribVersion == 2)
    1478         287 :                 gribBand->FindPDSTemplateGRIB2();
    1479             : 
    1480         294 :             gribBand->m_Grib_MetaData = metaData;
    1481             :         }
    1482             :         else
    1483             :         {
    1484         114 :             gribBand = new GRIBRasterBand(poDS, bandNr, psInv);
    1485             :         }
    1486         408 :         poDS->SetBand(bandNr, gribBand);
    1487             :     }
    1488             : 
    1489             :     // Initialize any PAM information.
    1490         294 :     poDS->SetDescription(poOpenInfo->pszFilename);
    1491             : 
    1492             :     // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own
    1493             :     // hGRIBMutex.
    1494         294 :     CPLReleaseMutex(hGRIBMutex);
    1495         294 :     poDS->TryLoadXML();
    1496             : 
    1497             :     // Check for external overviews.
    1498         294 :     poDS->oOvManager.Initialize(poDS, poOpenInfo->pszFilename,
    1499             :                                 poOpenInfo->GetSiblingFiles());
    1500         294 :     CPLAcquireMutex(hGRIBMutex, 1000.0);
    1501             : 
    1502         294 :     return poDS;
    1503             : }
    1504             : 
    1505             : /************************************************************************/
    1506             : /*                          GRIBSharedResource                          */
    1507             : /************************************************************************/
    1508             : 
    1509             : struct GRIBSharedResource
    1510             : {
    1511             :     VSILFILE *m_fp = nullptr;
    1512             :     vsi_l_offset m_nOffsetCurData = static_cast<vsi_l_offset>(-1);
    1513             :     std::vector<double> m_adfCurData{};
    1514             :     std::string m_osFilename;
    1515             :     std::shared_ptr<GDALPamMultiDim> m_poPAM{};
    1516             : 
    1517             :     GRIBSharedResource(const std::string &osFilename, VSILFILE *fp);
    1518             :     ~GRIBSharedResource();
    1519             : 
    1520             :     const std::vector<double> &LoadData(vsi_l_offset nOffset, int subgNum);
    1521             : 
    1522          23 :     const std::shared_ptr<GDALPamMultiDim> &GetPAM()
    1523             :     {
    1524          23 :         return m_poPAM;
    1525             :     }
    1526             : };
    1527             : 
    1528           4 : GRIBSharedResource::GRIBSharedResource(const std::string &osFilename,
    1529           4 :                                        VSILFILE *fp)
    1530             :     : m_fp(fp), m_osFilename(osFilename),
    1531           4 :       m_poPAM(std::make_shared<GDALPamMultiDim>(osFilename))
    1532             : {
    1533           4 : }
    1534             : 
    1535           4 : GRIBSharedResource::~GRIBSharedResource()
    1536             : {
    1537           4 :     if (m_fp)
    1538           4 :         VSIFCloseL(m_fp);
    1539           4 : }
    1540             : 
    1541             : /************************************************************************/
    1542             : /*                                GRIBGroup                             */
    1543             : /************************************************************************/
    1544             : 
    1545             : class GRIBArray;
    1546             : 
    1547             : class GRIBGroup final : public GDALGroup
    1548             : {
    1549             :     friend class GRIBArray;
    1550             :     std::shared_ptr<GRIBSharedResource> m_poShared{};
    1551             :     std::vector<std::shared_ptr<GDALMDArray>> m_poArrays{};
    1552             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    1553             :     std::map<std::string, std::shared_ptr<GDALDimension>> m_oMapDims{};
    1554             :     int m_nHorizDimCounter = 0;
    1555             :     std::shared_ptr<GDALGroup> m_memRootGroup{};
    1556             : 
    1557             :   public:
    1558           4 :     explicit GRIBGroup(const std::shared_ptr<GRIBSharedResource> &poShared)
    1559           4 :         : GDALGroup(std::string(), "/"), m_poShared(poShared)
    1560             :     {
    1561             :         std::unique_ptr<GDALDataset> poTmpDS(
    1562           4 :             MEMDataset::CreateMultiDimensional("", nullptr, nullptr));
    1563           4 :         m_memRootGroup = poTmpDS->GetRootGroup();
    1564           4 :     }
    1565             : 
    1566          36 :     void AddArray(const std::shared_ptr<GDALMDArray> &array)
    1567             :     {
    1568          36 :         m_poArrays.emplace_back(array);
    1569          36 :     }
    1570             : 
    1571             :     std::vector<std::string>
    1572             :     GetMDArrayNames(CSLConstList papszOptions) const override;
    1573             :     std::shared_ptr<GDALMDArray>
    1574             :     OpenMDArray(const std::string &osName,
    1575             :                 CSLConstList papszOptions) const override;
    1576             : 
    1577             :     std::vector<std::shared_ptr<GDALDimension>>
    1578           3 :     GetDimensions(CSLConstList) const override
    1579             :     {
    1580           3 :         return m_dims;
    1581             :     }
    1582             : };
    1583             : 
    1584             : /************************************************************************/
    1585             : /*                                GRIBArray                             */
    1586             : /************************************************************************/
    1587             : 
    1588             : class GRIBArray final : public GDALPamMDArray
    1589             : {
    1590             :     std::shared_ptr<GRIBSharedResource> m_poShared;
    1591             :     std::vector<std::shared_ptr<GDALDimension>> m_dims{};
    1592             :     GDALExtendedDataType m_dt = GDALExtendedDataType::Create(GDT_Float64);
    1593             :     std::shared_ptr<OGRSpatialReference> m_poSRS{};
    1594             :     std::vector<vsi_l_offset> m_anOffsets{};
    1595             :     std::vector<int> m_anSubgNums{};
    1596             :     std::vector<double> m_adfTimes{};
    1597             :     std::vector<std::shared_ptr<GDALAttribute>> m_attributes{};
    1598             :     std::string m_osUnit{};
    1599             :     std::vector<GByte> m_abyNoData{};
    1600             : 
    1601             :     GRIBArray(const std::string &osName,
    1602             :               const std::shared_ptr<GRIBSharedResource> &poShared);
    1603             : 
    1604             :   protected:
    1605             :     bool IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    1606             :                const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    1607             :                const GDALExtendedDataType &bufferDataType,
    1608             :                void *pDstBuffer) const override;
    1609             : 
    1610             :   public:
    1611             :     static std::shared_ptr<GRIBArray>
    1612          23 :     Create(const std::string &osName,
    1613             :            const std::shared_ptr<GRIBSharedResource> &poShared)
    1614             :     {
    1615          23 :         auto ar(std::shared_ptr<GRIBArray>(new GRIBArray(osName, poShared)));
    1616          23 :         ar->SetSelf(ar);
    1617          23 :         return ar;
    1618             :     }
    1619             : 
    1620             :     void Init(GRIBGroup *poGroup, GRIBDataset *poDS, GRIBRasterBand *poBand,
    1621             :               inventoryType *psInv);
    1622             :     void ExtendTimeDim(vsi_l_offset nOffset, int subgNum, double dfValidTime);
    1623             :     void Finalize(GRIBGroup *poGroup, inventoryType *psInv);
    1624             : 
    1625           0 :     bool IsWritable() const override
    1626             :     {
    1627           0 :         return false;
    1628             :     }
    1629             : 
    1630           4 :     const std::string &GetFilename() const override
    1631             :     {
    1632           4 :         return m_poShared->m_osFilename;
    1633             :     }
    1634             : 
    1635             :     const std::vector<std::shared_ptr<GDALDimension>> &
    1636          30 :     GetDimensions() const override
    1637             :     {
    1638          30 :         return m_dims;
    1639             :     }
    1640             : 
    1641          11 :     const GDALExtendedDataType &GetDataType() const override
    1642             :     {
    1643          11 :         return m_dt;
    1644             :     }
    1645             : 
    1646           1 :     std::shared_ptr<OGRSpatialReference> GetSpatialRef() const override
    1647             :     {
    1648           1 :         return m_poSRS;
    1649             :     }
    1650             : 
    1651             :     std::vector<std::shared_ptr<GDALAttribute>>
    1652          14 :     GetAttributes(CSLConstList) const override
    1653             :     {
    1654          14 :         return m_attributes;
    1655             :     }
    1656             : 
    1657           1 :     const std::string &GetUnit() const override
    1658             :     {
    1659           1 :         return m_osUnit;
    1660             :     }
    1661             : 
    1662           1 :     const void *GetRawNoDataValue() const override
    1663             :     {
    1664           1 :         return m_abyNoData.empty() ? nullptr : m_abyNoData.data();
    1665             :     }
    1666             : };
    1667             : 
    1668             : /************************************************************************/
    1669             : /*                          GetMDArrayNames()                           */
    1670             : /************************************************************************/
    1671             : 
    1672           3 : std::vector<std::string> GRIBGroup::GetMDArrayNames(CSLConstList) const
    1673             : {
    1674           3 :     std::vector<std::string> ret;
    1675          31 :     for (const auto &array : m_poArrays)
    1676             :     {
    1677          28 :         ret.push_back(array->GetName());
    1678             :     }
    1679           3 :     return ret;
    1680             : }
    1681             : 
    1682             : /************************************************************************/
    1683             : /*                            OpenMDArray()                             */
    1684             : /************************************************************************/
    1685             : 
    1686           3 : std::shared_ptr<GDALMDArray> GRIBGroup::OpenMDArray(const std::string &osName,
    1687             :                                                     CSLConstList) const
    1688             : {
    1689          12 :     for (const auto &array : m_poArrays)
    1690             :     {
    1691          11 :         if (array->GetName() == osName)
    1692           2 :             return array;
    1693             :     }
    1694           1 :     return nullptr;
    1695             : }
    1696             : 
    1697             : /************************************************************************/
    1698             : /*                             GRIBArray()                              */
    1699             : /************************************************************************/
    1700             : 
    1701          23 : GRIBArray::GRIBArray(const std::string &osName,
    1702          23 :                      const std::shared_ptr<GRIBSharedResource> &poShared)
    1703             :     : GDALAbstractMDArray("/", osName),
    1704          23 :       GDALPamMDArray("/", osName, poShared->GetPAM()), m_poShared(poShared)
    1705             : {
    1706          23 : }
    1707             : 
    1708             : /************************************************************************/
    1709             : /*                                Init()                                */
    1710             : /************************************************************************/
    1711             : 
    1712          23 : void GRIBArray::Init(GRIBGroup *poGroup, GRIBDataset *poDS,
    1713             :                      GRIBRasterBand *poBand, inventoryType *psInv)
    1714             : {
    1715          23 :     std::shared_ptr<GDALDimension> poDimX;
    1716          23 :     std::shared_ptr<GDALDimension> poDimY;
    1717             : 
    1718             :     double adfGT[6];
    1719          23 :     poDS->GetGeoTransform(adfGT);
    1720             : 
    1721          40 :     for (int i = 1; i <= poGroup->m_nHorizDimCounter; i++)
    1722             :     {
    1723          34 :         std::string osXLookup("X");
    1724          34 :         std::string osYLookup("Y");
    1725          34 :         if (i > 1)
    1726             :         {
    1727          15 :             osXLookup += CPLSPrintf("%d", i);
    1728          15 :             osYLookup += CPLSPrintf("%d", i);
    1729             :         }
    1730          34 :         auto oIterX = poGroup->m_oMapDims.find(osXLookup);
    1731          34 :         auto oIterY = poGroup->m_oMapDims.find(osYLookup);
    1732          34 :         CPLAssert(oIterX != poGroup->m_oMapDims.end());
    1733          34 :         CPLAssert(oIterY != poGroup->m_oMapDims.end());
    1734          34 :         if (oIterX->second->GetSize() ==
    1735          51 :                 static_cast<size_t>(poDS->GetRasterXSize()) &&
    1736          17 :             oIterY->second->GetSize() ==
    1737          17 :                 static_cast<size_t>(poDS->GetRasterYSize()))
    1738             :         {
    1739          17 :             bool bOK = true;
    1740          17 :             auto poVar = oIterX->second->GetIndexingVariable();
    1741          17 :             constexpr double EPSILON = 1e-10;
    1742          17 :             if (poVar)
    1743             :             {
    1744          17 :                 GUInt64 nStart = 0;
    1745          17 :                 size_t nCount = 1;
    1746          17 :                 double dfVal = 0;
    1747          17 :                 poVar->Read(&nStart, &nCount, nullptr, nullptr, m_dt, &dfVal);
    1748          17 :                 if (std::fabs(dfVal - (adfGT[0] + 0.5 * adfGT[1])) >
    1749          17 :                     EPSILON * std::fabs(dfVal))
    1750             :                 {
    1751           0 :                     bOK = false;
    1752             :                 }
    1753             :             }
    1754          17 :             if (bOK)
    1755             :             {
    1756          17 :                 poVar = oIterY->second->GetIndexingVariable();
    1757          17 :                 if (poVar)
    1758             :                 {
    1759          17 :                     GUInt64 nStart = 0;
    1760          17 :                     size_t nCount = 1;
    1761          17 :                     double dfVal = 0;
    1762          17 :                     poVar->Read(&nStart, &nCount, nullptr, nullptr, m_dt,
    1763             :                                 &dfVal);
    1764          17 :                     if (std::fabs(dfVal -
    1765          17 :                                   (adfGT[3] + poDS->nRasterYSize * adfGT[5] -
    1766          17 :                                    0.5 * adfGT[5])) >
    1767          17 :                         EPSILON * std::fabs(dfVal))
    1768             :                     {
    1769           0 :                         bOK = false;
    1770             :                     }
    1771             :                 }
    1772             :             }
    1773          17 :             if (bOK)
    1774             :             {
    1775          17 :                 poDimX = oIterX->second;
    1776          17 :                 poDimY = oIterY->second;
    1777          17 :                 break;
    1778             :             }
    1779             :         }
    1780             :     }
    1781             : 
    1782          23 :     if (!poDimX || !poDimY)
    1783             :     {
    1784           6 :         poGroup->m_nHorizDimCounter++;
    1785             :         {
    1786          12 :             std::string osName("Y");
    1787           6 :             if (poGroup->m_nHorizDimCounter >= 2)
    1788             :             {
    1789           2 :                 osName = CPLSPrintf("Y%d", poGroup->m_nHorizDimCounter);
    1790             :             }
    1791             : 
    1792          12 :             poDimY = std::make_shared<GDALDimensionWeakIndexingVar>(
    1793           6 :                 poGroup->GetFullName(), osName, GDAL_DIM_TYPE_HORIZONTAL_Y,
    1794          18 :                 std::string(), poDS->GetRasterYSize());
    1795           6 :             poGroup->m_oMapDims[osName] = poDimY;
    1796           6 :             poGroup->m_dims.emplace_back(poDimY);
    1797             : 
    1798             :             auto var = GDALMDArrayRegularlySpaced::Create(
    1799             :                 "/", poDimY->GetName(), poDimY,
    1800          12 :                 adfGT[3] + poDS->GetRasterYSize() * adfGT[5], -adfGT[5], 0.5);
    1801           6 :             poDimY->SetIndexingVariable(var);
    1802           6 :             poGroup->AddArray(var);
    1803             :         }
    1804             : 
    1805             :         {
    1806          12 :             std::string osName("X");
    1807           6 :             if (poGroup->m_nHorizDimCounter >= 2)
    1808             :             {
    1809           2 :                 osName = CPLSPrintf("X%d", poGroup->m_nHorizDimCounter);
    1810             :             }
    1811             : 
    1812          12 :             poDimX = std::make_shared<GDALDimensionWeakIndexingVar>(
    1813           6 :                 poGroup->GetFullName(), osName, GDAL_DIM_TYPE_HORIZONTAL_X,
    1814          18 :                 std::string(), poDS->GetRasterXSize());
    1815           6 :             poGroup->m_oMapDims[osName] = poDimX;
    1816           6 :             poGroup->m_dims.emplace_back(poDimX);
    1817             : 
    1818             :             auto var = GDALMDArrayRegularlySpaced::Create(
    1819          12 :                 "/", poDimX->GetName(), poDimX, adfGT[0], adfGT[1], 0.5);
    1820           6 :             poDimX->SetIndexingVariable(var);
    1821           6 :             poGroup->AddArray(var);
    1822             :         }
    1823             :     }
    1824             : 
    1825          23 :     m_dims.emplace_back(poDimY);
    1826          23 :     m_dims.emplace_back(poDimX);
    1827          23 :     if (poDS->m_poSRS.get())
    1828             :     {
    1829          23 :         m_poSRS.reset(poDS->m_poSRS->Clone());
    1830          23 :         if (poDS->m_poSRS->GetDataAxisToSRSAxisMapping() ==
    1831          46 :             std::vector<int>{2, 1})
    1832          22 :             m_poSRS->SetDataAxisToSRSAxisMapping({1, 2});
    1833             :         else
    1834           1 :             m_poSRS->SetDataAxisToSRSAxisMapping({2, 1});
    1835             :     }
    1836             : 
    1837             :     const char *pszGribNormalizeUnits =
    1838          23 :         CPLGetConfigOption("GRIB_NORMALIZE_UNITS", "YES");
    1839          23 :     const bool bMetricUnits = CPLTestBool(pszGribNormalizeUnits);
    1840             : 
    1841          46 :     m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1842          46 :         GetFullName(), "name", psInv->element));
    1843          46 :     m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1844          23 :         GetFullName(), "long_name",
    1845          69 :         ConvertUnitInText(bMetricUnits, psInv->comment)));
    1846          23 :     m_osUnit = ConvertUnitInText(bMetricUnits, psInv->unitName);
    1847          23 :     if (!m_osUnit.empty() && m_osUnit[0] == '[' && m_osUnit.back() == ']')
    1848             :     {
    1849          23 :         m_osUnit = m_osUnit.substr(1, m_osUnit.size() - 2);
    1850             :     }
    1851          46 :     m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1852          46 :         GetFullName(), "first_level", psInv->shortFstLevel));
    1853             : 
    1854          23 :     if (poBand->m_nDisciplineCode >= 0)
    1855             :     {
    1856          14 :         m_attributes.emplace_back(std::make_shared<GDALAttributeNumeric>(
    1857          14 :             GetFullName(), "discipline_code", poBand->m_nDisciplineCode));
    1858             :     }
    1859          23 :     if (!poBand->m_osDisciplineName.empty())
    1860             :     {
    1861          14 :         m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1862          14 :             GetFullName(), "discipline_name", poBand->m_osDisciplineName));
    1863             :     }
    1864          23 :     if (poBand->m_nCenter >= 0)
    1865             :     {
    1866          14 :         m_attributes.emplace_back(std::make_shared<GDALAttributeNumeric>(
    1867          14 :             GetFullName(), "center_code", poBand->m_nCenter));
    1868             :     }
    1869          23 :     if (!poBand->m_osCenterName.empty())
    1870             :     {
    1871          14 :         m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1872          14 :             GetFullName(), "center_name", poBand->m_osCenterName));
    1873             :     }
    1874          23 :     if (poBand->m_nSubCenter >= 0)
    1875             :     {
    1876          12 :         m_attributes.emplace_back(std::make_shared<GDALAttributeNumeric>(
    1877          12 :             GetFullName(), "subcenter_code", poBand->m_nSubCenter));
    1878             :     }
    1879          23 :     if (!poBand->m_osSubCenterName.empty())
    1880             :     {
    1881           0 :         m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1882           0 :             GetFullName(), "subcenter_name", poBand->m_osSubCenterName));
    1883             :     }
    1884          23 :     if (!poBand->m_osSignRefTimeName.empty())
    1885             :     {
    1886          14 :         m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1887           7 :             GetFullName(), "signification_of_ref_time",
    1888          14 :             poBand->m_osSignRefTimeName));
    1889             :     }
    1890          23 :     if (!poBand->m_osRefTime.empty())
    1891             :     {
    1892          14 :         m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1893          14 :             GetFullName(), "reference_time_iso8601", poBand->m_osRefTime));
    1894             :     }
    1895          23 :     if (!poBand->m_osProductionStatus.empty())
    1896             :     {
    1897          14 :         m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1898          14 :             GetFullName(), "production_status", poBand->m_osProductionStatus));
    1899             :     }
    1900          23 :     if (!poBand->m_osType.empty())
    1901             :     {
    1902          14 :         m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1903          14 :             GetFullName(), "type", poBand->m_osType));
    1904             :     }
    1905          23 :     if (poBand->m_nPDTN >= 0)
    1906             :     {
    1907          14 :         m_attributes.emplace_back(std::make_shared<GDALAttributeNumeric>(
    1908           7 :             GetFullName(), "product_definition_template_number",
    1909          14 :             poBand->m_nPDTN));
    1910             :     }
    1911          23 :     if (!poBand->m_anPDSTemplateAssembledValues.empty())
    1912             :     {
    1913          14 :         m_attributes.emplace_back(std::make_shared<GDALAttributeNumeric>(
    1914           7 :             GetFullName(), "product_definition_numbers",
    1915          14 :             poBand->m_anPDSTemplateAssembledValues));
    1916             :     }
    1917             : 
    1918          23 :     int bHasNoData = FALSE;
    1919          23 :     double dfNoData = poBand->GetNoDataValue(&bHasNoData);
    1920          23 :     if (bHasNoData)
    1921             :     {
    1922          14 :         m_abyNoData.resize(sizeof(double));
    1923          14 :         memcpy(&m_abyNoData[0], &dfNoData, sizeof(double));
    1924             :     }
    1925          23 : }
    1926             : 
    1927             : /************************************************************************/
    1928             : /*                         ExtendTimeDim()                              */
    1929             : /************************************************************************/
    1930             : 
    1931          24 : void GRIBArray::ExtendTimeDim(vsi_l_offset nOffset, int subgNum,
    1932             :                               double dfValidTime)
    1933             : {
    1934          24 :     m_anOffsets.push_back(nOffset);
    1935          24 :     m_anSubgNums.push_back(subgNum);
    1936          24 :     m_adfTimes.push_back(dfValidTime);
    1937          24 : }
    1938             : 
    1939             : /************************************************************************/
    1940             : /*                           Finalize()                                 */
    1941             : /************************************************************************/
    1942             : 
    1943          23 : void GRIBArray::Finalize(GRIBGroup *poGroup, inventoryType *psInv)
    1944             : {
    1945          23 :     CPLAssert(!m_adfTimes.empty());
    1946          23 :     CPLAssert(m_anOffsets.size() == m_adfTimes.size());
    1947             : 
    1948          23 :     if (m_adfTimes.size() == 1)
    1949             :     {
    1950          44 :         m_attributes.emplace_back(std::make_shared<GDALAttributeNumeric>(
    1951          44 :             GetFullName(), "forecast_time", psInv->foreSec));
    1952          44 :         m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1953          44 :             GetFullName(), "forecast_time_unit", "sec"));
    1954          44 :         m_attributes.emplace_back(std::make_shared<GDALAttributeNumeric>(
    1955          44 :             GetFullName(), "reference_time", psInv->refTime));
    1956          44 :         m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1957          44 :             GetFullName(), "reference_time_unit", "sec UTC"));
    1958          44 :         m_attributes.emplace_back(std::make_shared<GDALAttributeNumeric>(
    1959          44 :             GetFullName(), "validity_time", m_adfTimes[0]));
    1960          44 :         m_attributes.emplace_back(std::make_shared<GDALAttributeString>(
    1961          44 :             GetFullName(), "validity_time_unit", "sec UTC"));
    1962          22 :         return;
    1963             :     }
    1964             : 
    1965           1 :     std::shared_ptr<GDALDimension> poDimTime;
    1966             : 
    1967           3 :     for (const auto &poDim : poGroup->m_dims)
    1968             :     {
    1969           2 :         if (STARTS_WITH(poDim->GetName().c_str(), "TIME") &&
    1970           0 :             poDim->GetSize() == m_adfTimes.size())
    1971             :         {
    1972           0 :             auto poVar = poDim->GetIndexingVariable();
    1973           0 :             if (poVar)
    1974             :             {
    1975           0 :                 GUInt64 nStart = 0;
    1976           0 :                 size_t nCount = 1;
    1977           0 :                 double dfStartTime = 0;
    1978           0 :                 poVar->Read(&nStart, &nCount, nullptr, nullptr, m_dt,
    1979             :                             &dfStartTime);
    1980           0 :                 if (dfStartTime == m_adfTimes[0])
    1981             :                 {
    1982           0 :                     poDimTime = poDim;
    1983           0 :                     break;
    1984             :                 }
    1985             :             }
    1986             :         }
    1987             :     }
    1988             : 
    1989           1 :     if (!poDimTime)
    1990             :     {
    1991           2 :         std::string osName("TIME");
    1992           1 :         int counter = 2;
    1993           1 :         while (poGroup->m_oMapDims.find(osName) != poGroup->m_oMapDims.end())
    1994             :         {
    1995           0 :             osName = CPLSPrintf("TIME%d", counter);
    1996           0 :             counter++;
    1997             :         }
    1998             : 
    1999           2 :         poDimTime = std::make_shared<GDALDimensionWeakIndexingVar>(
    2000           1 :             poGroup->GetFullName(), osName, GDAL_DIM_TYPE_TEMPORAL,
    2001           3 :             std::string(), m_adfTimes.size());
    2002           1 :         poGroup->m_oMapDims[osName] = poDimTime;
    2003           1 :         poGroup->m_dims.emplace_back(poDimTime);
    2004             : 
    2005           1 :         auto var = poGroup->m_memRootGroup->CreateMDArray(
    2006             :             poDimTime->GetName(),
    2007           4 :             std::vector<std::shared_ptr<GDALDimension>>{poDimTime},
    2008           3 :             GDALExtendedDataType::Create(GDT_Float64), nullptr);
    2009           1 :         poDimTime->SetIndexingVariable(var);
    2010           1 :         poGroup->AddArray(var);
    2011             : 
    2012           1 :         GUInt64 nStart = 0;
    2013           1 :         size_t nCount = m_adfTimes.size();
    2014           1 :         var->SetUnit("sec UTC");
    2015           1 :         const GUInt64 anStart[] = {nStart};
    2016           1 :         const size_t anCount[] = {nCount};
    2017           2 :         var->Write(anStart, anCount, nullptr, nullptr, var->GetDataType(),
    2018           1 :                    &m_adfTimes[0]);
    2019           1 :         auto attr = var->CreateAttribute("long_name", {},
    2020           3 :                                          GDALExtendedDataType::CreateString());
    2021           1 :         attr->Write("validity_time");
    2022             :     }
    2023             : 
    2024           1 :     m_dims.insert(m_dims.begin(), poDimTime);
    2025           1 :     if (m_poSRS)
    2026             :     {
    2027           2 :         auto mapping = m_poSRS->GetDataAxisToSRSAxisMapping();
    2028           3 :         for (auto &v : mapping)
    2029           2 :             v += 1;
    2030           1 :         m_poSRS->SetDataAxisToSRSAxisMapping(mapping);
    2031             :     }
    2032             : }
    2033             : 
    2034             : /************************************************************************/
    2035             : /*                              LoadData()                              */
    2036             : /************************************************************************/
    2037             : 
    2038           6 : const std::vector<double> &GRIBSharedResource::LoadData(vsi_l_offset nOffset,
    2039             :                                                         int subgNum)
    2040             : {
    2041           6 :     if (m_nOffsetCurData == nOffset)
    2042           1 :         return m_adfCurData;
    2043             : 
    2044           5 :     grib_MetaData *metadata = nullptr;
    2045           5 :     double *data = nullptr;
    2046           5 :     GRIBRasterBand::ReadGribData(m_fp, nOffset, subgNum, &data, &metadata);
    2047           5 :     if (data == nullptr || metadata == nullptr)
    2048             :     {
    2049           0 :         if (metadata != nullptr)
    2050             :         {
    2051           0 :             MetaFree(metadata);
    2052           0 :             delete metadata;
    2053             :         }
    2054           0 :         free(data);
    2055           0 :         m_adfCurData.clear();
    2056           0 :         return m_adfCurData;
    2057             :     }
    2058           5 :     const int nx = metadata->gds.Nx;
    2059           5 :     const int ny = metadata->gds.Ny;
    2060           5 :     MetaFree(metadata);
    2061           5 :     delete metadata;
    2062           5 :     if (nx <= 0 || ny <= 0)
    2063             :     {
    2064           0 :         free(data);
    2065           0 :         m_adfCurData.clear();
    2066           0 :         return m_adfCurData;
    2067             :     }
    2068           5 :     const size_t nPointCount = static_cast<size_t>(nx) * ny;
    2069           5 :     const size_t nByteCount = nPointCount * sizeof(double);
    2070             : #ifdef FUZZING_BUILD_MODE_UNSAFE_FOR_PRODUCTION
    2071             :     if (nByteCount > static_cast<size_t>(INT_MAX))
    2072             :     {
    2073             :         CPLError(CE_Failure, CPLE_OutOfMemory,
    2074             :                  "Too large memory allocation attempt");
    2075             :         free(data);
    2076             :         m_adfCurData.clear();
    2077             :         return m_adfCurData;
    2078             :     }
    2079             : #endif
    2080             :     try
    2081             :     {
    2082           5 :         m_adfCurData.resize(nPointCount);
    2083             :     }
    2084           0 :     catch (const std::exception &e)
    2085             :     {
    2086           0 :         CPLError(CE_Failure, CPLE_OutOfMemory, "%s", e.what());
    2087           0 :         free(data);
    2088           0 :         m_adfCurData.clear();
    2089           0 :         return m_adfCurData;
    2090             :     }
    2091           5 :     m_nOffsetCurData = nOffset;
    2092           5 :     memcpy(&m_adfCurData[0], data, nByteCount);
    2093           5 :     free(data);
    2094           5 :     return m_adfCurData;
    2095             : }
    2096             : 
    2097             : /************************************************************************/
    2098             : /*                             IRead()                                  */
    2099             : /************************************************************************/
    2100             : 
    2101           4 : bool GRIBArray::IRead(const GUInt64 *arrayStartIdx, const size_t *count,
    2102             :                       const GInt64 *arrayStep, const GPtrDiff_t *bufferStride,
    2103             :                       const GDALExtendedDataType &bufferDataType,
    2104             :                       void *pDstBuffer) const
    2105             : {
    2106           4 :     const size_t nBufferDTSize(bufferDataType.GetSize());
    2107           4 :     if (m_dims.size() == 2)
    2108             :     {
    2109           2 :         auto &vals = m_poShared->LoadData(m_anOffsets[0], m_anSubgNums[0]);
    2110           2 :         constexpr int Y_IDX = 0;
    2111           2 :         constexpr int X_IDX = 1;
    2112           4 :         if (vals.empty() ||
    2113           2 :             vals.size() != m_dims[Y_IDX]->GetSize() * m_dims[X_IDX]->GetSize())
    2114           0 :             return false;
    2115           2 :         const size_t nWidth(static_cast<size_t>(m_dims[X_IDX]->GetSize()));
    2116           2 :         const bool bDirectCopy = m_dt == bufferDataType &&
    2117           3 :                                  arrayStep[X_IDX] == 1 &&
    2118           1 :                                  bufferStride[X_IDX] == 1;
    2119         150 :         for (size_t j = 0; j < count[Y_IDX]; j++)
    2120             :         {
    2121         148 :             const size_t y = static_cast<size_t>(arrayStartIdx[Y_IDX] +
    2122         148 :                                                  j * arrayStep[Y_IDX]);
    2123         148 :             GByte *pabyDstPtr = static_cast<GByte *>(pDstBuffer) +
    2124         148 :                                 j * bufferStride[Y_IDX] * nBufferDTSize;
    2125         148 :             const size_t x = static_cast<size_t>(arrayStartIdx[X_IDX]);
    2126         148 :             const double *srcPtr = &vals[y * nWidth + x];
    2127         148 :             if (bDirectCopy)
    2128             :             {
    2129          74 :                 memcpy(pabyDstPtr, srcPtr, count[X_IDX] * sizeof(double));
    2130             :             }
    2131             :             else
    2132             :             {
    2133          74 :                 const auto dstPtrInc = bufferStride[X_IDX] * nBufferDTSize;
    2134        4958 :                 for (size_t i = 0; i < count[X_IDX]; i++)
    2135             :                 {
    2136        4884 :                     GDALExtendedDataType::CopyValue(srcPtr, m_dt, pabyDstPtr,
    2137             :                                                     bufferDataType);
    2138        4884 :                     srcPtr += static_cast<std::ptrdiff_t>(arrayStep[X_IDX]);
    2139        4884 :                     pabyDstPtr += dstPtrInc;
    2140             :                 }
    2141             :             }
    2142             :         }
    2143           2 :         return true;
    2144             :     }
    2145             : 
    2146           2 :     constexpr int T_IDX = 0;
    2147           2 :     constexpr int Y_IDX = 1;
    2148           2 :     constexpr int X_IDX = 2;
    2149           2 :     const size_t nWidth(static_cast<size_t>(m_dims[X_IDX]->GetSize()));
    2150           3 :     const bool bDirectCopy = m_dt == bufferDataType && arrayStep[X_IDX] == 1 &&
    2151           1 :                              bufferStride[X_IDX] == 1;
    2152           6 :     for (size_t k = 0; k < count[T_IDX]; k++)
    2153             :     {
    2154           4 :         const size_t tIdx =
    2155           4 :             static_cast<size_t>(arrayStartIdx[T_IDX] + k * arrayStep[T_IDX]);
    2156           4 :         CPLAssert(tIdx < m_anOffsets.size());
    2157             :         auto &vals =
    2158           4 :             m_poShared->LoadData(m_anOffsets[tIdx], m_anSubgNums[tIdx]);
    2159           8 :         if (vals.empty() ||
    2160           4 :             vals.size() != m_dims[Y_IDX]->GetSize() * m_dims[X_IDX]->GetSize())
    2161           0 :             return false;
    2162         520 :         for (size_t j = 0; j < count[Y_IDX]; j++)
    2163             :         {
    2164         516 :             const size_t y = static_cast<size_t>(arrayStartIdx[Y_IDX] +
    2165         516 :                                                  j * arrayStep[Y_IDX]);
    2166         516 :             GByte *pabyDstPtr =
    2167             :                 static_cast<GByte *>(pDstBuffer) +
    2168         516 :                 (k * bufferStride[T_IDX] + j * bufferStride[Y_IDX]) *
    2169             :                     nBufferDTSize;
    2170         516 :             const size_t x = static_cast<size_t>(arrayStartIdx[X_IDX]);
    2171         516 :             const double *srcPtr = &vals[y * nWidth + x];
    2172         516 :             if (bDirectCopy)
    2173             :             {
    2174         258 :                 memcpy(pabyDstPtr, srcPtr, count[X_IDX] * sizeof(double));
    2175             :             }
    2176             :             else
    2177             :             {
    2178         258 :                 const auto dstPtrInc = bufferStride[X_IDX] * nBufferDTSize;
    2179       45924 :                 for (size_t i = 0; i < count[X_IDX]; i++)
    2180             :                 {
    2181       45666 :                     GDALExtendedDataType::CopyValue(srcPtr, m_dt, pabyDstPtr,
    2182             :                                                     bufferDataType);
    2183       45666 :                     srcPtr += static_cast<std::ptrdiff_t>(arrayStep[X_IDX]);
    2184       45666 :                     pabyDstPtr += dstPtrInc;
    2185             :                 }
    2186             :             }
    2187             :         }
    2188             :     }
    2189             : 
    2190           2 :     return true;
    2191             : }
    2192             : 
    2193             : /************************************************************************/
    2194             : /*                          OpenMultiDim()                              */
    2195             : /************************************************************************/
    2196             : 
    2197           4 : GDALDataset *GRIBDataset::OpenMultiDim(GDALOpenInfo *poOpenInfo)
    2198             : 
    2199             : {
    2200             :     auto poShared = std::make_shared<GRIBSharedResource>(
    2201           8 :         poOpenInfo->pszFilename, poOpenInfo->fpL);
    2202           8 :     auto poRootGroup = std::make_shared<GRIBGroup>(poShared);
    2203           4 :     poOpenInfo->fpL = nullptr;
    2204             : 
    2205           4 :     VSIFSeekL(poShared->m_fp, 0, SEEK_SET);
    2206             : 
    2207             :     // Contains an GRIB2 message inventory of the file.
    2208             :     // We can't use the potential .idx file
    2209           8 :     auto pInventories = std::make_unique<InventoryWrapperGrib>(poShared->m_fp);
    2210             : 
    2211           4 :     if (pInventories->result() <= 0)
    2212             :     {
    2213           0 :         char *errMsg = errSprintf(nullptr);
    2214           0 :         if (errMsg != nullptr)
    2215           0 :             CPLDebug("GRIB", "%s", errMsg);
    2216           0 :         free(errMsg);
    2217             : 
    2218           0 :         CPLError(CE_Failure, CPLE_OpenFailed,
    2219             :                  "%s is a grib file, "
    2220             :                  "but no raster dataset was successfully identified.",
    2221             :                  poOpenInfo->pszFilename);
    2222           0 :         return nullptr;
    2223             :     }
    2224             : 
    2225             :     // Create band objects.
    2226           4 :     GRIBDataset *poDS = new GRIBDataset();
    2227           4 :     poDS->fp = poShared->m_fp;
    2228             : 
    2229           4 :     std::shared_ptr<GRIBArray> poArray;
    2230           8 :     std::set<std::string> oSetArrayNames;
    2231           8 :     std::string osElement;
    2232           8 :     std::string osShortFstLevel;
    2233           4 :     double dfRefTime = 0;
    2234           4 :     double dfForecastTime = 0;
    2235          28 :     for (uInt4 i = 0; i < pInventories->length(); ++i)
    2236             :     {
    2237          24 :         inventoryType *psInv = pInventories->get(i);
    2238          24 :         uInt4 bandNr = i + 1;
    2239             : 
    2240             :         // GRIB messages can be preceded by "garbage". GRIB2Inventory()
    2241             :         // does not return the offset to the real start of the message
    2242             :         GByte abyHeader[1024 + 1];
    2243          24 :         VSIFSeekL(poShared->m_fp, psInv->start, SEEK_SET);
    2244             :         size_t nRead =
    2245          24 :             VSIFReadL(abyHeader, 1, sizeof(abyHeader) - 1, poShared->m_fp);
    2246          24 :         abyHeader[nRead] = 0;
    2247             :         // Find the real offset of the fist message
    2248          24 :         const char *pasHeader = reinterpret_cast<char *>(abyHeader);
    2249          24 :         int nOffsetFirstMessage = 0;
    2250         144 :         for (int j = 0; j < poOpenInfo->nHeaderBytes - 3; j++)
    2251             :         {
    2252         144 :             if (STARTS_WITH_CI(pasHeader + j, "GRIB")
    2253             : #ifdef ENABLE_TDLP
    2254             :                 || STARTS_WITH_CI(pasHeader + j, "TDLP")
    2255             : #endif
    2256             :             )
    2257             :             {
    2258          24 :                 nOffsetFirstMessage = j;
    2259          24 :                 break;
    2260             :             }
    2261             :         }
    2262          24 :         psInv->start += nOffsetFirstMessage;
    2263             : 
    2264          44 :         if (poArray == nullptr || osElement != psInv->element ||
    2265          45 :             osShortFstLevel != psInv->shortFstLevel ||
    2266           1 :             !((dfRefTime == psInv->refTime &&
    2267           1 :                dfForecastTime != psInv->foreSec) ||
    2268           0 :               (dfRefTime != psInv->refTime &&
    2269           0 :                dfForecastTime == psInv->foreSec)))
    2270             :         {
    2271          23 :             if (poArray)
    2272             :             {
    2273          19 :                 poArray->Finalize(poRootGroup.get(), pInventories->get(i - 1));
    2274          19 :                 poRootGroup->AddArray(poArray);
    2275             :             }
    2276             : 
    2277          23 :             grib_MetaData *metaData = nullptr;
    2278          23 :             GRIBRasterBand::ReadGribData(poShared->m_fp, psInv->start,
    2279          23 :                                          psInv->subgNum, nullptr, &metaData);
    2280          23 :             if (metaData == nullptr || metaData->gds.Nx < 1 ||
    2281          23 :                 metaData->gds.Ny < 1)
    2282             :             {
    2283           0 :                 CPLError(CE_Failure, CPLE_OpenFailed,
    2284             :                          "%s is a grib file, "
    2285             :                          "but no raster dataset was successfully identified.",
    2286             :                          poOpenInfo->pszFilename);
    2287             : 
    2288           0 :                 if (metaData != nullptr)
    2289             :                 {
    2290           0 :                     MetaFree(metaData);
    2291           0 :                     delete metaData;
    2292             :                 }
    2293           0 :                 poDS->fp = nullptr;
    2294           0 :                 CPLReleaseMutex(hGRIBMutex);
    2295           0 :                 delete poDS;
    2296           0 :                 CPLAcquireMutex(hGRIBMutex, 1000.0);
    2297           0 :                 return nullptr;
    2298             :             }
    2299          23 :             psInv->GribVersion = metaData->GribVersion;
    2300             : 
    2301             :             // Set the DataSet's x,y size, georeference and projection from
    2302             :             // the first GRIB band.
    2303          23 :             poDS->SetGribMetaData(metaData);
    2304             : 
    2305             :             // coverity[tainted_data]
    2306          46 :             GRIBRasterBand gribBand(poDS, bandNr, psInv);
    2307          23 :             if (psInv->GribVersion == 2)
    2308           7 :                 gribBand.FindPDSTemplateGRIB2();
    2309          23 :             osElement = psInv->element;
    2310          23 :             osShortFstLevel = psInv->shortFstLevel;
    2311          23 :             dfRefTime = psInv->refTime;
    2312          23 :             dfForecastTime = psInv->foreSec;
    2313             : 
    2314          46 :             std::string osRadix(osElement + '_' + osShortFstLevel);
    2315          46 :             std::string osName(osRadix);
    2316          23 :             int nCounter = 2;
    2317          23 :             while (oSetArrayNames.find(osName) != oSetArrayNames.end())
    2318             :             {
    2319           0 :                 osName = osRadix + CPLSPrintf("_%d", nCounter);
    2320           0 :                 nCounter++;
    2321             :             }
    2322          23 :             oSetArrayNames.insert(osName);
    2323          23 :             poArray = GRIBArray::Create(osName, poShared);
    2324          23 :             poArray->Init(poRootGroup.get(), poDS, &gribBand, psInv);
    2325          23 :             poArray->ExtendTimeDim(psInv->start, psInv->subgNum,
    2326             :                                    psInv->validTime);
    2327             : 
    2328          23 :             MetaFree(metaData);
    2329          23 :             delete metaData;
    2330             :         }
    2331             :         else
    2332             :         {
    2333           1 :             poArray->ExtendTimeDim(psInv->start, psInv->subgNum,
    2334             :                                    psInv->validTime);
    2335             :         }
    2336             :     }
    2337             : 
    2338           4 :     if (poArray)
    2339             :     {
    2340           8 :         poArray->Finalize(poRootGroup.get(),
    2341           4 :                           pInventories->get(pInventories->length() - 1));
    2342           4 :         poRootGroup->AddArray(poArray);
    2343             :     }
    2344             : 
    2345           4 :     poDS->fp = nullptr;
    2346           4 :     poDS->m_poRootGroup = poRootGroup;
    2347             : 
    2348           4 :     poDS->SetDescription(poOpenInfo->pszFilename);
    2349             : 
    2350             :     // Release hGRIBMutex otherwise we'll deadlock with GDALDataset own
    2351             :     // hGRIBMutex.
    2352           4 :     CPLReleaseMutex(hGRIBMutex);
    2353           4 :     poDS->TryLoadXML();
    2354           4 :     CPLAcquireMutex(hGRIBMutex, 1000.0);
    2355             : 
    2356           4 :     return poDS;
    2357             : }
    2358             : 
    2359             : /************************************************************************/
    2360             : /*                            SetMetadata()                             */
    2361             : /************************************************************************/
    2362             : 
    2363         317 : void GRIBDataset::SetGribMetaData(grib_MetaData *meta)
    2364             : {
    2365         317 :     nRasterXSize = meta->gds.Nx;
    2366         317 :     nRasterYSize = meta->gds.Ny;
    2367             : 
    2368             :     // Image projection.
    2369         317 :     OGRSpatialReference oSRS;
    2370         317 :     oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2371             : 
    2372         317 :     switch (meta->gds.projType)
    2373             :     {
    2374         128 :         case GS3_LATLON:
    2375             :         case GS3_GAUSSIAN_LATLON:
    2376             :             // No projection, only latlon system (geographic).
    2377         128 :             break;
    2378           2 :         case GS3_ROTATED_LATLON:
    2379             :             // will apply pole rotation afterwards
    2380           2 :             break;
    2381          26 :         case GS3_MERCATOR:
    2382          26 :             if (meta->gds.orientLon == 0.0)
    2383             :             {
    2384          26 :                 if (meta->gds.meshLat == 0.0)
    2385           5 :                     oSRS.SetMercator(0.0, 0.0, 1.0, 0.0, 0.0);
    2386             :                 else
    2387          21 :                     oSRS.SetMercator2SP(meta->gds.meshLat, 0.0, 0.0, 0.0, 0.0);
    2388             :             }
    2389             :             else
    2390             :             {
    2391           0 :                 CPLError(CE_Warning, CPLE_NotSupported,
    2392             :                          "Orientation of the grid != 0 not supported");
    2393           0 :                 return;
    2394             :             }
    2395          26 :             break;
    2396         133 :         case GS3_TRANSVERSE_MERCATOR:
    2397         133 :             oSRS.SetTM(meta->gds.latitude_of_origin,
    2398             :                        Lon360to180(meta->gds.central_meridian),
    2399         133 :                        std::abs(meta->gds.scaleLat1 - 0.9996) < 1e8
    2400             :                            ? 0.9996
    2401             :                            : meta->gds.scaleLat1,
    2402             :                        meta->gds.x0, meta->gds.y0);
    2403         133 :             break;
    2404           9 :         case GS3_POLAR:
    2405           9 :             oSRS.SetPS(meta->gds.meshLat, meta->gds.orientLon, 1.0, 0.0, 0.0);
    2406           9 :             break;
    2407           8 :         case GS3_LAMBERT:
    2408           8 :             oSRS.SetLCC(meta->gds.scaleLat1, meta->gds.scaleLat2,
    2409             :                         meta->gds.meshLat, Lon360to180(meta->gds.orientLon),
    2410             :                         0.0, 0.0);
    2411           8 :             break;
    2412           5 :         case GS3_ALBERS_EQUAL_AREA:
    2413           5 :             oSRS.SetACEA(meta->gds.scaleLat1, meta->gds.scaleLat2,
    2414             :                          meta->gds.meshLat, Lon360to180(meta->gds.orientLon),
    2415             :                          0.0, 0.0);
    2416           5 :             break;
    2417             : 
    2418           0 :         case GS3_ORTHOGRAPHIC:
    2419             : 
    2420             :             // oSRS.SetOrthographic( 0.0, meta->gds.orientLon,
    2421             :             //                       meta->gds.lon2, meta->gds.lat2);
    2422             : 
    2423             :             // oSRS.SetGEOS( meta->gds.orientLon, meta->gds.stretchFactor,
    2424             :             //               meta->gds.lon2, meta->gds.lat2);
    2425             : 
    2426             :             // TODO: Hardcoded for now. How to parse the meta->gds section?
    2427           0 :             oSRS.SetGEOS(0, 35785831, 0, 0);
    2428           0 :             break;
    2429           6 :         case GS3_LAMBERT_AZIMUTHAL:
    2430           6 :             oSRS.SetLAEA(meta->gds.meshLat, Lon360to180(meta->gds.orientLon),
    2431             :                          0.0, 0.0);
    2432           6 :             break;
    2433             : 
    2434           0 :         case GS3_EQUATOR_EQUIDIST:
    2435           0 :             break;
    2436           0 :         case GS3_AZIMUTH_RANGE:
    2437           0 :             break;
    2438             :     }
    2439             : 
    2440         317 :     if (oSRS.IsProjected())
    2441             :     {
    2442         187 :         oSRS.SetLinearUnits("Metre", 1.0);
    2443             :     }
    2444             : 
    2445         317 :     const bool bHaveEarthModel =
    2446         317 :         meta->gds.majEarth > 0.0 && meta->gds.minEarth > 0.0;
    2447             :     // In meters.
    2448             :     const double a = bHaveEarthModel
    2449         317 :                          ? meta->gds.majEarth * 1.0e3
    2450           2 :                          : CPLAtof(CPLGetConfigOption("GRIB_DEFAULT_SEMI_MAJOR",
    2451         317 :                                                       "6377563.396"));
    2452             :     const double b =
    2453             :         bHaveEarthModel
    2454         319 :             ? meta->gds.minEarth * 1.0e3
    2455           2 :             : (meta->gds.f_sphere
    2456           2 :                    ? a
    2457           0 :                    : CPLAtof(CPLGetConfigOption("GRIB_DEFAULT_SEMI_MINOR",
    2458         317 :                                                 "6356256.910")));
    2459         317 :     if (meta->gds.majEarth == 0 || meta->gds.minEarth == 0)
    2460             :     {
    2461           0 :         CPLDebug("GRIB", "No earth model. Assuming a=%f and b=%f", a, b);
    2462             :     }
    2463         317 :     else if (meta->gds.majEarth < 0 || meta->gds.minEarth < 0)
    2464             :     {
    2465             :         const char *pszUseDefaultSpheroid =
    2466           2 :             CPLGetConfigOption("GRIB_USE_DEFAULT_SPHEROID", nullptr);
    2467           2 :         if (!pszUseDefaultSpheroid)
    2468             :         {
    2469           1 :             CPLError(CE_Failure, CPLE_AppDefined,
    2470             :                      "The GRIB file contains invalid values for the spheroid. "
    2471             :                      "You may set the GRIB_USE_DEFAULT_SPHEROID configuration "
    2472             :                      "option to YES to use a default spheroid with "
    2473             :                      "a=%f and b=%f",
    2474             :                      a, b);
    2475           1 :             return;
    2476             :         }
    2477           1 :         else if (!CPLTestBool(pszUseDefaultSpheroid))
    2478             :         {
    2479           0 :             return;
    2480             :         }
    2481           1 :         CPLDebug("GRIB", "Invalid earth model. Assuming a=%f and b=%f", a, b);
    2482             :     }
    2483             : 
    2484         316 :     if (meta->gds.f_sphere || (a == b))
    2485             :     {
    2486          81 :         oSRS.SetGeogCS("Coordinate System imported from GRIB file", nullptr,
    2487             :                        "Sphere", a, 0.0);
    2488             :     }
    2489             :     else
    2490             :     {
    2491         235 :         const double fInv = a / (a - b);
    2492         335 :         if (std::abs(a - 6378137.0) < 0.01 &&
    2493         100 :             std::abs(fInv - 298.257223563) < 1e-9)  // WGS84
    2494             :         {
    2495          98 :             if (meta->gds.projType == GS3_LATLON)
    2496          68 :                 oSRS.SetFromUserInput(SRS_WKT_WGS84_LAT_LONG);
    2497             :             else
    2498             :             {
    2499          30 :                 oSRS.SetGeogCS("Coordinate System imported from GRIB file",
    2500             :                                "WGS_1984", "WGS 84", 6378137., 298.257223563);
    2501             :             }
    2502             :         }
    2503         139 :         else if (std::abs(a - 6378137.0) < 0.01 &&
    2504           2 :                  std::abs(fInv - 298.257222101) < 1e-9)  // GRS80
    2505             :         {
    2506           2 :             oSRS.SetGeogCS("Coordinate System imported from GRIB file", nullptr,
    2507             :                            "GRS80", 6378137., 298.257222101);
    2508             :         }
    2509             :         else
    2510             :         {
    2511         135 :             oSRS.SetGeogCS("Coordinate System imported from GRIB file", nullptr,
    2512             :                            "Spheroid imported from GRIB file", a, fInv);
    2513             :         }
    2514             :     }
    2515             : 
    2516         316 :     if (meta->gds.projType == GS3_ROTATED_LATLON)
    2517             :     {
    2518           2 :         oSRS.SetDerivedGeogCRSWithPoleRotationGRIBConvention(
    2519             :             oSRS.GetName(), meta->gds.southLat, Lon360to180(meta->gds.southLon),
    2520             :             meta->gds.angleRotate);
    2521             :     }
    2522             : 
    2523         316 :     OGRSpatialReference oLL;  // Construct the "geographic" part of oSRS.
    2524         316 :     oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
    2525         316 :     oLL.CopyGeogCSFrom(&oSRS);
    2526             : 
    2527         316 :     double rMinX = 0.0;
    2528         316 :     double rMaxY = 0.0;
    2529         316 :     double rPixelSizeX = 0.0;
    2530         316 :     double rPixelSizeY = 0.0;
    2531         316 :     bool bError = false;
    2532         316 :     if (meta->gds.projType == GS3_ORTHOGRAPHIC)
    2533             :     {
    2534             :         // This is what should work, but it doesn't. Dx seems to have an
    2535             :         // inverse relation with pixel size.
    2536             :         // rMinX = -meta->gds.Dx * (meta->gds.Nx / 2);
    2537             :         // rMaxY = meta->gds.Dy * (meta->gds.Ny / 2);
    2538             :         // Hardcoded for now, assumption: GEOS projection, full disc (like MSG).
    2539           0 :         const double geosExtentInMeters = 11137496.552;
    2540           0 :         rMinX = -(geosExtentInMeters / 2);
    2541           0 :         rMaxY = geosExtentInMeters / 2;
    2542           0 :         rPixelSizeX = geosExtentInMeters / meta->gds.Nx;
    2543           0 :         rPixelSizeY = geosExtentInMeters / meta->gds.Ny;
    2544             :     }
    2545         316 :     else if (meta->gds.projType == GS3_TRANSVERSE_MERCATOR)
    2546             :     {
    2547         132 :         rMinX = meta->gds.x1;
    2548         132 :         rMaxY = meta->gds.y2;
    2549         132 :         rPixelSizeX = meta->gds.Dx;
    2550         132 :         rPixelSizeY = meta->gds.Dy;
    2551             :     }
    2552         184 :     else if (oSRS.IsProjected() && meta->gds.projType != GS3_ROTATED_LATLON)
    2553             :     {
    2554             :         // Longitude in degrees, to be transformed to meters (or degrees in
    2555             :         // case of latlon).
    2556          54 :         rMinX = meta->gds.lon1;
    2557             :         // Latitude in degrees, to be transformed to meters.
    2558          54 :         rMaxY = meta->gds.lat1;
    2559             : 
    2560          54 :         if (m_poSRS == nullptr || m_poLL == nullptr ||
    2561          54 :             !m_poSRS->IsSame(&oSRS) || !m_poLL->IsSame(&oLL))
    2562             :         {
    2563         108 :             m_poCT = std::unique_ptr<OGRCoordinateTransformation>(
    2564          54 :                 OGRCreateCoordinateTransformation(&(oLL), &(oSRS)));
    2565             :         }
    2566             : 
    2567             :         // Transform it to meters.
    2568          54 :         if ((m_poCT != nullptr) && m_poCT->Transform(1, &rMinX, &rMaxY))
    2569             :         {
    2570          54 :             if (meta->gds.scan == GRIB2BIT_2)  // Y is minY, GDAL wants maxY.
    2571             :             {
    2572             :                 // -1 because we GDAL needs the coordinates of the centre of
    2573             :                 // the pixel.
    2574          54 :                 rMaxY += (meta->gds.Ny - 1) * meta->gds.Dy;
    2575             :             }
    2576          54 :             rPixelSizeX = meta->gds.Dx;
    2577          54 :             rPixelSizeY = meta->gds.Dy;
    2578             :         }
    2579             :         else
    2580             :         {
    2581           0 :             rMinX = 0.0;
    2582           0 :             rMaxY = 0.0;
    2583             : 
    2584           0 :             rPixelSizeX = 1.0;
    2585           0 :             rPixelSizeY = -1.0;
    2586             : 
    2587           0 :             bError = true;
    2588           0 :             CPLError(CE_Warning, CPLE_AppDefined,
    2589             :                      "Unable to perform coordinate transformations, so the "
    2590             :                      "correct projected geotransform could not be deduced "
    2591             :                      "from the lat/long control points.  "
    2592             :                      "Defaulting to ungeoreferenced.");
    2593             :         }
    2594             :     }
    2595             :     else
    2596             :     {
    2597             :         // Longitude in degrees, to be transformed to meters (or degrees in
    2598             :         // case of latlon).
    2599         130 :         rMinX = meta->gds.lon1;
    2600             :         // Latitude in degrees, to be transformed to meters.
    2601         130 :         rMaxY = meta->gds.lat1;
    2602             : 
    2603         130 :         double rMinY = meta->gds.lat2;
    2604         130 :         double rMaxX = meta->gds.lon2;
    2605         130 :         if (meta->gds.lat2 > rMaxY)
    2606             :         {
    2607         100 :             rMaxY = meta->gds.lat2;
    2608         100 :             rMinY = meta->gds.lat1;
    2609             :         }
    2610             : 
    2611         130 :         if (meta->gds.Nx == 1)
    2612          16 :             rPixelSizeX = meta->gds.Dx;
    2613         114 :         else if (meta->gds.lon1 > meta->gds.lon2)
    2614          13 :             rPixelSizeX = (360.0 - (meta->gds.lon1 - meta->gds.lon2)) /
    2615          13 :                           (meta->gds.Nx - 1);
    2616             :         else
    2617         101 :             rPixelSizeX =
    2618         101 :                 (meta->gds.lon2 - meta->gds.lon1) / (meta->gds.Nx - 1);
    2619             : 
    2620         130 :         if (meta->gds.Ny == 1)
    2621          16 :             rPixelSizeY = meta->gds.Dy;
    2622             :         else
    2623         114 :             rPixelSizeY = (rMaxY - rMinY) / (meta->gds.Ny - 1);
    2624             : 
    2625             :         // Do some sanity checks for cases that can't be handled by the above
    2626             :         // pixel size corrections. GRIB1 has a minimum precision of 0.001
    2627             :         // for latitudes and longitudes, so we'll allow a bit higher than that.
    2628         130 :         if (rPixelSizeX < 0 || fabs(rPixelSizeX - meta->gds.Dx) > 0.002)
    2629           0 :             rPixelSizeX = meta->gds.Dx;
    2630             : 
    2631         130 :         if (rPixelSizeY < 0 || fabs(rPixelSizeY - meta->gds.Dy) > 0.002)
    2632           0 :             rPixelSizeY = meta->gds.Dy;
    2633             : 
    2634             :         // GRIB2 files have longitudes in the [0-360] range
    2635             :         // Shift them to the traditional [-180,180] longitude range
    2636             :         // See https://github.com/OSGeo/gdal/issues/4524
    2637         182 :         if ((rMinX + rPixelSizeX >= 180 || rMaxX - rPixelSizeX >= 180) &&
    2638          52 :             CPLTestBool(
    2639             :                 CPLGetConfigOption("GRIB_ADJUST_LONGITUDE_RANGE", "YES")))
    2640             :         {
    2641          50 :             if (rPixelSizeX * nRasterXSize > 360 + rPixelSizeX / 4)
    2642           0 :                 CPLDebug("GRIB", "Cannot properly handle GRIB2 files with "
    2643             :                                  "overlaps and 0-360 longitudes");
    2644          50 :             else if (fabs(360 - rPixelSizeX * nRasterXSize) < rPixelSizeX / 4 &&
    2645          19 :                      rMinX <= 180 && meta->gds.projType == GS3_LATLON)
    2646             :             {
    2647             :                 // Find the first row number east of the antimeridian
    2648           9 :                 const int nSplitAndSwapColumnCandidate =
    2649           9 :                     static_cast<int>(ceil((180 - rMinX) / rPixelSizeX));
    2650           9 :                 if (nSplitAndSwapColumnCandidate < nRasterXSize)
    2651             :                 {
    2652           9 :                     nSplitAndSwapColumn = nSplitAndSwapColumnCandidate;
    2653           9 :                     CPLDebug("GRIB",
    2654             :                              "Rewrapping around the antimeridian at column %d",
    2655             :                              nSplitAndSwapColumn);
    2656           9 :                     rMinX = -180;
    2657           9 :                 }
    2658             :             }
    2659          41 :             else if (Lon360to180(rMinX) > Lon360to180(rMaxX))
    2660             :             {
    2661           2 :                 CPLDebug("GRIB", "GRIB with 0-360 longitudes spanning across "
    2662             :                                  "the antimeridian");
    2663           2 :                 rMinX = Lon360to180(rMinX);
    2664             :             }
    2665             :             else
    2666             :             {
    2667          39 :                 CPLDebug("GRIB", "Shifting longitudes from %lf:%lf to %lf:%lf",
    2668             :                          rMinX, rMaxX, Lon360to180(rMinX), Lon360to180(rMaxX));
    2669          39 :                 rMinX = Lon360to180(rMinX);
    2670             :             }
    2671             :         }
    2672             :     }
    2673             : 
    2674             :     // https://gdal.org/user/raster_data_model.html :
    2675             :     //   we need the top left corner of the top left pixel.
    2676             :     //   At the moment we have the center of the pixel.
    2677         316 :     rMinX -= rPixelSizeX / 2;
    2678         316 :     rMaxY += rPixelSizeY / 2;
    2679             : 
    2680         316 :     adfGeoTransform[0] = rMinX;
    2681         316 :     adfGeoTransform[3] = rMaxY;
    2682         316 :     adfGeoTransform[1] = rPixelSizeX;
    2683         316 :     adfGeoTransform[5] = -rPixelSizeY;
    2684             : 
    2685         316 :     if (bError)
    2686           0 :         m_poSRS.reset();
    2687             :     else
    2688         316 :         m_poSRS.reset(oSRS.Clone());
    2689         316 :     m_poLL = std::unique_ptr<OGRSpatialReference>(oLL.Clone());
    2690             : }
    2691             : 
    2692             : /************************************************************************/
    2693             : /*                       GDALDeregister_GRIB()                          */
    2694             : /************************************************************************/
    2695             : 
    2696         852 : static void GDALDeregister_GRIB(GDALDriver *)
    2697             : {
    2698         852 :     if (hGRIBMutex != nullptr)
    2699             :     {
    2700           1 :         CPLDestroyMutex(hGRIBMutex);
    2701           1 :         hGRIBMutex = nullptr;
    2702             :     }
    2703         852 : }
    2704             : 
    2705             : /************************************************************************/
    2706             : /*                          GDALGRIBDriver                              */
    2707             : /************************************************************************/
    2708             : 
    2709             : class GDALGRIBDriver : public GDALDriver
    2710             : {
    2711             :     bool m_bHasFullInitMetadata = false;
    2712             : 
    2713             :   public:
    2714        1217 :     GDALGRIBDriver() = default;
    2715             : 
    2716             :     char **GetMetadata(const char *pszDomain = "") override;
    2717             :     const char *GetMetadataItem(const char *pszName,
    2718             :                                 const char *pszDomain) override;
    2719             : };
    2720             : 
    2721             : /************************************************************************/
    2722             : /*                            GetMetadata()                             */
    2723             : /************************************************************************/
    2724             : 
    2725         272 : char **GDALGRIBDriver::GetMetadata(const char *pszDomain)
    2726             : {
    2727         272 :     if (pszDomain == nullptr || EQUAL(pszDomain, ""))
    2728             :     {
    2729             :         // Defer until necessary the setting of the CreationOptionList
    2730             :         // to let a chance to JPEG2000 drivers to have been loaded.
    2731         272 :         if (!m_bHasFullInitMetadata)
    2732             :         {
    2733           7 :             m_bHasFullInitMetadata = true;
    2734             : 
    2735          14 :             std::vector<CPLString> aosJ2KDrivers;
    2736          35 :             for (size_t i = 0; i < CPL_ARRAYSIZE(apszJ2KDrivers); i++)
    2737             :             {
    2738          28 :                 if (GDALGetDriverByName(apszJ2KDrivers[i]) != nullptr)
    2739             :                 {
    2740          14 :                     aosJ2KDrivers.push_back(apszJ2KDrivers[i]);
    2741             :                 }
    2742             :             }
    2743             : 
    2744             :             CPLString osCreationOptionList(
    2745             :                 "<CreationOptionList>"
    2746             :                 "   <Option name='DATA_ENCODING' type='string-select' "
    2747             :                 "default='AUTO' "
    2748             :                 "description='How data is encoded internally'>"
    2749             :                 "       <Value>AUTO</Value>"
    2750             :                 "       <Value>SIMPLE_PACKING</Value>"
    2751             :                 "       <Value>COMPLEX_PACKING</Value>"
    2752          14 :                 "       <Value>IEEE_FLOATING_POINT</Value>");
    2753           7 :             if (GDALGetDriverByName("PNG") != nullptr)
    2754           7 :                 osCreationOptionList += "       <Value>PNG</Value>";
    2755           7 :             if (!aosJ2KDrivers.empty())
    2756           7 :                 osCreationOptionList += "       <Value>JPEG2000</Value>";
    2757             :             osCreationOptionList +=
    2758             :                 "   </Option>"
    2759             :                 "   <Option name='NBITS' type='int' default='0' "
    2760             :                 "description='Number of bits per value'/>"
    2761             :                 "   <Option name='DECIMAL_SCALE_FACTOR' type='int' default='0' "
    2762             :                 "description='Value such that raw values are multiplied by "
    2763             :                 "10^DECIMAL_SCALE_FACTOR before integer encoding'/>"
    2764             :                 "   <Option name='SPATIAL_DIFFERENCING_ORDER' type='int' "
    2765             :                 "default='0' "
    2766           7 :                 "description='Order of spatial difference' min='0' max='2'/>";
    2767           7 :             if (!aosJ2KDrivers.empty())
    2768             :             {
    2769             :                 osCreationOptionList +=
    2770             :                     "   <Option name='COMPRESSION_RATIO' type='int' "
    2771             :                     "default='1' min='1' max='100' "
    2772             :                     "description='N:1 target compression ratio for JPEG2000'/>"
    2773             :                     "   <Option name='JPEG2000_DRIVER' type='string-select' "
    2774           7 :                     "description='Explicitly select a JPEG2000 driver'>";
    2775          21 :                 for (size_t i = 0; i < aosJ2KDrivers.size(); i++)
    2776             :                 {
    2777             :                     osCreationOptionList +=
    2778          14 :                         "       <Value>" + aosJ2KDrivers[i] + "</Value>";
    2779             :                 }
    2780           7 :                 osCreationOptionList += "   </Option>";
    2781             :             }
    2782             :             osCreationOptionList +=
    2783             :                 "   <Option name='DISCIPLINE' type='int' "
    2784             :                 "description='Discipline of the processed data'/>"
    2785             :                 "   <Option name='IDS' type='string' "
    2786             :                 "description='String equivalent to the GRIB_IDS metadata "
    2787             :                 "item'/>"
    2788             :                 "   <Option name='IDS_CENTER' type='int' "
    2789             :                 "description='Originating/generating center'/>"
    2790             :                 "   <Option name='IDS_SUBCENTER' type='int' "
    2791             :                 "description='Originating/generating subcenter'/>"
    2792             :                 "   <Option name='IDS_MASTER_TABLE' type='int' "
    2793             :                 "description='GRIB master tables version number'/>"
    2794             :                 "   <Option name='IDS_SIGNF_REF_TIME' type='int' "
    2795             :                 "description='Significance of Reference Time'/>"
    2796             :                 "   <Option name='IDS_REF_TIME' type='string' "
    2797             :                 "description='Reference time as YYYY-MM-DDTHH:MM:SSZ'/>"
    2798             :                 "   <Option name='IDS_PROD_STATUS' type='int' "
    2799             :                 "description='Production Status of Processed data'/>"
    2800             :                 "   <Option name='IDS_TYPE' type='int' "
    2801             :                 "description='Type of processed data'/>"
    2802             :                 "   <Option name='PDS_PDTN' type='int' "
    2803             :                 "description='Product Definition Template Number'/>"
    2804             :                 "   <Option name='PDS_TEMPLATE_NUMBERS' type='string' "
    2805             :                 "description='Product definition template raw numbers'/>"
    2806             :                 "   <Option name='PDS_TEMPLATE_ASSEMBLED_VALUES' type='string' "
    2807             :                 "description='Product definition template assembled values'/>"
    2808             :                 "   <Option name='INPUT_UNIT' type='string' "
    2809             :                 "description='Unit of input values. Only for temperatures. C "
    2810             :                 "or K'/>"
    2811             :                 "   <Option name='BAND_*' type='string' "
    2812             :                 "description='Override options at band level'/>"
    2813           7 :                 "</CreationOptionList>";
    2814             : 
    2815           7 :             SetMetadataItem(GDAL_DMD_CREATIONOPTIONLIST, osCreationOptionList);
    2816             :         }
    2817             :     }
    2818         272 :     return GDALDriver::GetMetadata(pszDomain);
    2819             : }
    2820             : 
    2821             : /************************************************************************/
    2822             : /*                          GetMetadataItem()                           */
    2823             : /************************************************************************/
    2824             : 
    2825       51090 : const char *GDALGRIBDriver::GetMetadataItem(const char *pszName,
    2826             :                                             const char *pszDomain)
    2827             : {
    2828       51090 :     if (pszDomain == nullptr || EQUAL(pszDomain, ""))
    2829             :     {
    2830             :         // Defer until necessary the setting of the CreationOptionList
    2831             :         // to let a chance to JPEG2000 drivers to have been loaded.
    2832       51090 :         if (EQUAL(pszName, GDAL_DMD_CREATIONOPTIONLIST))
    2833         159 :             GetMetadata();
    2834             :     }
    2835       51090 :     return GDALDriver::GetMetadataItem(pszName, pszDomain);
    2836             : }
    2837             : 
    2838             : /************************************************************************/
    2839             : /*                         GDALRegister_GRIB()                          */
    2840             : /************************************************************************/
    2841             : 
    2842        1512 : void GDALRegister_GRIB()
    2843             : 
    2844             : {
    2845        1512 :     if (GDALGetDriverByName(DRIVER_NAME) != nullptr)
    2846         295 :         return;
    2847             : 
    2848        1217 :     GDALDriver *poDriver = new GDALGRIBDriver();
    2849        1217 :     GRIBDriverSetCommonMetadata(poDriver);
    2850             : 
    2851        1217 :     poDriver->pfnOpen = GRIBDataset::Open;
    2852        1217 :     poDriver->pfnCreateCopy = GRIBDataset::CreateCopy;
    2853        1217 :     poDriver->pfnUnloadDriver = GDALDeregister_GRIB;
    2854             : 
    2855             : #ifdef USE_AEC
    2856        1217 :     poDriver->SetMetadataItem("HAVE_AEC", "YES");
    2857             : #endif
    2858             : 
    2859        1217 :     GetGDALDriverManager()->RegisterDriver(poDriver);
    2860             : }

Generated by: LCOV version 1.14