LCOV - code coverage report
Current view: top level - frmts/grib - gribdataset.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1224 1434 85.4 %
Date: 2026-01-16 04:37:55 Functions: 60 64 93.8 %

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

Generated by: LCOV version 1.14