Coverage Report

Created: 2025-07-23 09:13

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