Coverage Report

Created: 2025-07-23 09:13

/src/gdal/frmts/grib/gribcreatecopy.cpp
Line
Count
Source (jump to first uncovered line)
1
/******************************************************************************
2
 *
3
 * Project:  GRIB Driver
4
 * Purpose:  GDALDataset driver for GRIB translator for write support
5
 * Author:   Even Rouault <even dot rouault at spatialys dot com>
6
 *
7
 ******************************************************************************
8
 * Copyright (c) 2017, Even Rouault <even dot rouault at spatialys dot com>
9
 *
10
 * SPDX-License-Identifier: MIT
11
 ******************************************************************************
12
 *
13
 */
14
15
/* Support for GRIB2 write capabilities has been funded by Meteorological */
16
/* Service of Canada */
17
18
#include "cpl_port.h"
19
#include "gribdataset.h"
20
#include "gdal_priv_templates.hpp"
21
#include "memdataset.h"
22
23
#include <algorithm>
24
#include <cmath>
25
#include <limits>
26
27
#include "degrib/degrib/meta.h"
28
CPL_C_START
29
#include "degrib/g2clib/grib2.h"
30
CPL_C_END
31
32
/************************************************************************/
33
/*                         Lon180to360()                                */
34
/************************************************************************/
35
36
static inline double Lon180to360(double lon)
37
23.6k
{
38
23.6k
    if (lon == 180)
39
0
        return 180;
40
23.6k
    return fmod(fmod(lon, 360) + 360, 360);
41
23.6k
}
42
43
/************************************************************************/
44
/*                             WriteByte()                              */
45
/************************************************************************/
46
47
static bool WriteByte(VSILFILE *fp, int nVal)
48
1.29M
{
49
1.29M
    GByte byVal = static_cast<GByte>(nVal);
50
1.29M
    return VSIFWriteL(&byVal, 1, sizeof(byVal), fp) == sizeof(byVal);
51
1.29M
}
52
53
/************************************************************************/
54
/*                            WriteSByte()                              */
55
/************************************************************************/
56
57
static bool WriteSByte(VSILFILE *fp, int nVal)
58
0
{
59
0
    signed char sVal = static_cast<signed char>(nVal);
60
0
    if (sVal == std::numeric_limits<signed char>::min())
61
0
        sVal = std::numeric_limits<signed char>::min() + 1;
62
0
    GByte nUnsignedVal = (sVal < 0) ? static_cast<GByte>(-sVal) | 0x80U
63
0
                                    : static_cast<GByte>(sVal);
64
0
    return VSIFWriteL(&nUnsignedVal, 1, sizeof(nUnsignedVal), fp) ==
65
0
           sizeof(nUnsignedVal);
66
0
}
67
68
/************************************************************************/
69
/*                            WriteUInt16()                             */
70
/************************************************************************/
71
72
static bool WriteUInt16(VSILFILE *fp, int nVal)
73
231k
{
74
231k
    GUInt16 usVal = static_cast<GUInt16>(nVal);
75
231k
    CPL_MSBPTR16(&usVal);
76
231k
    return VSIFWriteL(&usVal, 1, sizeof(usVal), fp) == sizeof(usVal);
77
231k
}
78
79
/************************************************************************/
80
/*                             WriteInt16()                             */
81
/************************************************************************/
82
83
static bool WriteInt16(VSILFILE *fp, int nVal)
84
25.2k
{
85
25.2k
    GInt16 sVal = static_cast<GInt16>(nVal);
86
25.2k
    if (sVal == std::numeric_limits<GInt16>::min())
87
0
        sVal = std::numeric_limits<GInt16>::min() + 1;
88
25.2k
    GUInt16 nUnsignedVal = (sVal < 0) ? static_cast<GUInt16>(-sVal) | 0x8000U
89
25.2k
                                      : static_cast<GUInt16>(sVal);
90
25.2k
    CPL_MSBPTR16(&nUnsignedVal);
91
25.2k
    return VSIFWriteL(&nUnsignedVal, 1, sizeof(nUnsignedVal), fp) ==
92
25.2k
           sizeof(nUnsignedVal);
93
25.2k
}
94
95
/************************************************************************/
96
/*                             WriteUInt32()                            */
97
/************************************************************************/
98
99
static bool WriteUInt32(VSILFILE *fp, GUInt32 nVal)
100
717k
{
101
717k
    CPL_MSBPTR32(&nVal);
102
717k
    return VSIFWriteL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal);
103
717k
}
104
105
/************************************************************************/
106
/*                             WriteInt32()                             */
107
/************************************************************************/
108
109
static bool WriteInt32(VSILFILE *fp, GInt32 nVal)
110
203k
{
111
203k
    if (nVal == std::numeric_limits<GInt32>::min())
112
76.5k
        nVal = std::numeric_limits<GInt32>::min() + 1;
113
203k
    GUInt32 nUnsignedVal = (nVal < 0)
114
203k
                               ? static_cast<GUInt32>(-nVal) | 0x80000000U
115
203k
                               : static_cast<GUInt32>(nVal);
116
203k
    CPL_MSBPTR32(&nUnsignedVal);
117
203k
    return VSIFWriteL(&nUnsignedVal, 1, sizeof(nUnsignedVal), fp) ==
118
203k
           sizeof(nUnsignedVal);
119
203k
}
120
121
/************************************************************************/
122
/*                            WriteFloat32()                            */
123
/************************************************************************/
124
125
static bool WriteFloat32(VSILFILE *fp, float fVal)
126
13.9k
{
127
13.9k
    CPL_MSBPTR32(&fVal);
128
13.9k
    return VSIFWriteL(&fVal, 1, sizeof(fVal), fp) == sizeof(fVal);
129
13.9k
}
130
131
/************************************************************************/
132
/*                         PatchSectionSize()                           */
133
/************************************************************************/
134
135
static void PatchSectionSize(VSILFILE *fp, vsi_l_offset nStartSection)
136
57.9k
{
137
57.9k
    vsi_l_offset nCurOffset = VSIFTellL(fp);
138
57.9k
    VSIFSeekL(fp, nStartSection, SEEK_SET);
139
57.9k
    GUInt32 nSect3Size = static_cast<GUInt32>(nCurOffset - nStartSection);
140
57.9k
    WriteUInt32(fp, nSect3Size);
141
57.9k
    VSIFSeekL(fp, nCurOffset, SEEK_SET);
142
57.9k
}
143
144
/************************************************************************/
145
/*                         GRIB2Section3Writer                          */
146
/************************************************************************/
147
148
class GRIB2Section3Writer
149
{
150
    VSILFILE *fp;
151
    GDALDataset *poSrcDS;
152
    OGRSpatialReference oSRS;
153
    const char *pszProjection;
154
    double dfLLX, dfLLY, dfURX, dfURY;
155
    GDALGeoTransform m_gt{};
156
    int nSplitAndSwapColumn = 0;
157
158
    bool WriteScaled(double dfVal, double dfUnit);
159
    bool TransformToGeo(double &dfX, double &dfY);
160
    bool WriteEllipsoidAndRasterSize();
161
162
    bool WriteGeographic();
163
    bool WriteRotatedLatLon(double dfLatSouthernPole, double dfLonSouthernPole,
164
                            double dfAxisRotation);
165
    bool WriteMercator1SP();
166
    bool WriteMercator2SP(OGRSpatialReference *poSRS = nullptr);
167
    bool WriteTransverseMercator();
168
    bool WritePolarStereographic();
169
    bool WriteLCC1SP();
170
    bool WriteLCC2SPOrAEA(OGRSpatialReference *poSRS = nullptr);
171
    bool WriteLAEA();
172
173
  public:
174
    GRIB2Section3Writer(VSILFILE *fpIn, GDALDataset *poSrcDSIn);
175
176
    inline int SplitAndSwap() const
177
28.9k
    {
178
28.9k
        return nSplitAndSwapColumn;
179
28.9k
    }
180
181
    bool Write();
182
};
183
184
/************************************************************************/
185
/*                        GRIB2Section3Writer()                         */
186
/************************************************************************/
187
188
GRIB2Section3Writer::GRIB2Section3Writer(VSILFILE *fpIn, GDALDataset *poSrcDSIn)
189
28.9k
    : fp(fpIn), poSrcDS(poSrcDSIn)
190
28.9k
{
191
28.9k
    oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
192
28.9k
    oSRS.importFromWkt(poSrcDS->GetProjectionRef());
193
28.9k
    pszProjection = oSRS.GetAttrValue("PROJECTION");
194
195
28.9k
    poSrcDS->GetGeoTransform(m_gt);
196
197
28.9k
    dfLLX = m_gt[0] + m_gt[1] / 2;
198
28.9k
    dfLLY = m_gt[3] + m_gt[5] / 2 + (poSrcDS->GetRasterYSize() - 1) * m_gt[5];
199
28.9k
    dfURX = m_gt[0] + m_gt[1] / 2 + (poSrcDS->GetRasterXSize() - 1) * m_gt[1];
200
28.9k
    dfURY = m_gt[3] + m_gt[5] / 2;
201
28.9k
    if (dfURY < dfLLY)
202
7.81k
    {
203
7.81k
        double dfTemp = dfURY;
204
7.81k
        dfURY = dfLLY;
205
7.81k
        dfLLY = dfTemp;
206
7.81k
    }
207
28.9k
}
208
209
/************************************************************************/
210
/*                     WriteEllipsoidAndRasterSize()                    */
211
/************************************************************************/
212
213
bool GRIB2Section3Writer::WriteEllipsoidAndRasterSize()
214
28.9k
{
215
28.9k
    const double dfSemiMajor = oSRS.GetSemiMajor();
216
28.9k
    const double dfSemiMinor = oSRS.GetSemiMinor();
217
28.9k
    const double dfInvFlattening = oSRS.GetInvFlattening();
218
28.9k
    if (std::abs(dfSemiMajor - 6378137.0) < 0.01 &&
219
28.9k
        std::abs(dfInvFlattening - 298.257223563) < 1e-9)  // WGS84
220
9.27k
    {
221
9.27k
        WriteByte(fp, 5);  // WGS84
222
9.27k
        WriteByte(fp, GRIB2MISSING_u1);
223
9.27k
        WriteUInt32(fp, GRIB2MISSING_u4);
224
9.27k
        WriteByte(fp, GRIB2MISSING_u1);
225
9.27k
        WriteUInt32(fp, GRIB2MISSING_u4);
226
9.27k
        WriteByte(fp, GRIB2MISSING_u1);
227
9.27k
        WriteUInt32(fp, GRIB2MISSING_u4);
228
9.27k
    }
229
19.6k
    else if (std::abs(dfSemiMajor - 6378137.0) < 0.01 &&
230
19.6k
             std::abs(dfInvFlattening - 298.257222101) < 1e-9)  // GRS80
231
1.12k
    {
232
1.12k
        WriteByte(fp, 4);  // GRS80
233
1.12k
        WriteByte(fp, GRIB2MISSING_u1);
234
1.12k
        WriteUInt32(fp, GRIB2MISSING_u4);
235
1.12k
        WriteByte(fp, GRIB2MISSING_u1);
236
1.12k
        WriteUInt32(fp, GRIB2MISSING_u4);
237
1.12k
        WriteByte(fp, GRIB2MISSING_u1);
238
1.12k
        WriteUInt32(fp, GRIB2MISSING_u4);
239
1.12k
    }
240
18.5k
    else if (dfInvFlattening == 0)
241
6.96k
    {
242
        // Earth assumed spherical with radius specified (in m)
243
        // by data producer
244
6.96k
        WriteByte(fp, 1);
245
6.96k
        WriteByte(fp, 2);  // scale = * 100
246
6.96k
        WriteUInt32(fp, static_cast<GUInt32>(dfSemiMajor * 100 + 0.5));
247
6.96k
        WriteByte(fp, GRIB2MISSING_u1);
248
6.96k
        WriteUInt32(fp, GRIB2MISSING_u4);
249
6.96k
        WriteByte(fp, GRIB2MISSING_u1);
250
6.96k
        WriteUInt32(fp, GRIB2MISSING_u4);
251
6.96k
    }
252
11.6k
    else
253
11.6k
    {
254
        // Earth assumed oblate spheroid with major and minor axes
255
        // specified (in m) by data producer
256
11.6k
        WriteByte(fp, 7);
257
11.6k
        WriteByte(fp, GRIB2MISSING_u1);
258
11.6k
        WriteUInt32(fp, GRIB2MISSING_u4);
259
11.6k
        WriteByte(fp, 2);  // scale = * 100
260
11.6k
        WriteUInt32(fp, static_cast<GUInt32>(dfSemiMajor * 100 + 0.5));
261
11.6k
        WriteByte(fp, 2);  // scale = * 100
262
11.6k
        WriteUInt32(fp, static_cast<GUInt32>(dfSemiMinor * 100 + 0.5));
263
11.6k
    }
264
28.9k
    WriteUInt32(fp, poSrcDS->GetRasterXSize());
265
28.9k
    WriteUInt32(fp, poSrcDS->GetRasterYSize());
266
267
28.9k
    return true;
268
28.9k
}
269
270
/************************************************************************/
271
/*                            WriteScaled()                             */
272
/************************************************************************/
273
274
bool GRIB2Section3Writer::WriteScaled(double dfVal, double dfUnit)
275
198k
{
276
198k
    return WriteInt32(fp, static_cast<GInt32>(floor(dfVal / dfUnit + 0.5)));
277
198k
}
278
279
/************************************************************************/
280
/*                          WriteGeographic()                           */
281
/************************************************************************/
282
283
bool GRIB2Section3Writer::WriteGeographic()
284
10.3k
{
285
10.3k
    WriteUInt16(fp, GS3_LATLON);  // Grid template number
286
287
10.3k
    WriteEllipsoidAndRasterSize();
288
289
10.3k
    if (dfLLX < 0 &&
290
10.3k
        CPLTestBool(CPLGetConfigOption("GRIB_ADJUST_LONGITUDE_RANGE", "YES")))
291
2.47k
    {
292
2.47k
        CPLDebug("GRIB", "Source longitude range is %lf to %lf", dfLLX, dfURX);
293
2.47k
        double dfOrigLLX = dfLLX;
294
2.47k
        dfLLX = Lon180to360(dfLLX);
295
2.47k
        dfURX = Lon180to360(dfURX);
296
297
2.47k
        if (dfLLX > dfURX)
298
126
        {
299
126
            if (fabs(360 - poSrcDS->GetRasterXSize() * m_gt[1]) < m_gt[1] / 4)
300
0
            {
301
                // Find the first row number east of the prime meridian
302
0
                nSplitAndSwapColumn =
303
0
                    static_cast<int>(ceil((0 - dfOrigLLX) / m_gt[1]));
304
0
                CPLDebug("GRIB",
305
0
                         "Rewrapping around the prime meridian at column %d",
306
0
                         nSplitAndSwapColumn);
307
0
                dfLLX = 0;
308
0
                dfURX = 360 - m_gt[1];
309
0
            }
310
126
            else
311
126
            {
312
126
                CPLDebug("GRIB", "Writing a GRIB with 0-360 longitudes "
313
126
                                 "crossing the prime meridian");
314
126
            }
315
126
        }
316
2.47k
        CPLDebug("GRIB", "Target longitudes range is %lf %lf", dfLLX, dfURX);
317
2.47k
    }
318
319
10.3k
    WriteUInt32(fp, 0);  // Basic angle. 0 equivalent of 1
320
    // Subdivisions of basic angle used. ~0 equivalent of 10^6
321
10.3k
    WriteUInt32(fp, GRIB2MISSING_u4);
322
10.3k
    const double dfAngUnit = 1e-6;
323
10.3k
    WriteScaled(dfLLY, dfAngUnit);
324
10.3k
    WriteScaled(dfLLX, dfAngUnit);
325
10.3k
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
326
10.3k
    WriteScaled(dfURY, dfAngUnit);
327
10.3k
    WriteScaled(dfURX, dfAngUnit);
328
10.3k
    WriteScaled(m_gt[1], dfAngUnit);
329
10.3k
    WriteScaled(fabs(m_gt[5]), dfAngUnit);
330
10.3k
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
331
332
10.3k
    return true;
333
10.3k
}
334
335
/************************************************************************/
336
/*                         WriteRotatedLatLon()                         */
337
/************************************************************************/
338
339
bool GRIB2Section3Writer::WriteRotatedLatLon(double dfLatSouthernPole,
340
                                             double dfLonSouthernPole,
341
                                             double dfAxisRotation)
342
6.76k
{
343
6.76k
    WriteUInt16(fp, GS3_ROTATED_LATLON);  // Grid template number
344
345
6.76k
    WriteEllipsoidAndRasterSize();
346
347
6.76k
    if (dfLLX < 0 &&
348
6.76k
        CPLTestBool(CPLGetConfigOption("GRIB_ADJUST_LONGITUDE_RANGE", "YES")))
349
74
    {
350
74
        CPLDebug("GRIB", "Source longitude range is %lf to %lf", dfLLX, dfURX);
351
74
        double dfOrigLLX = dfLLX;
352
74
        dfLLX = Lon180to360(dfLLX);
353
74
        dfURX = Lon180to360(dfURX);
354
355
74
        if (dfLLX > dfURX)
356
11
        {
357
11
            if (fabs(360 - poSrcDS->GetRasterXSize() * m_gt[1]) < m_gt[1] / 4)
358
0
            {
359
                // Find the first row number east of the prime meridian
360
0
                nSplitAndSwapColumn =
361
0
                    static_cast<int>(ceil((0 - dfOrigLLX) / m_gt[1]));
362
0
                CPLDebug("GRIB",
363
0
                         "Rewrapping around the prime meridian at column %d",
364
0
                         nSplitAndSwapColumn);
365
0
                dfLLX = 0;
366
0
                dfURX = 360 - m_gt[1];
367
0
            }
368
11
            else
369
11
            {
370
11
                CPLDebug("GRIB", "Writing a GRIB with 0-360 longitudes "
371
11
                                 "crossing the prime meridian");
372
11
            }
373
11
        }
374
74
        CPLDebug("GRIB", "Target longitudes range is %lf %lf", dfLLX, dfURX);
375
74
    }
376
377
6.76k
    WriteUInt32(fp, 0);  // Basic angle. 0 equivalent of 1
378
    // Subdivisions of basic angle used. ~0 equivalent of 10^6
379
6.76k
    WriteUInt32(fp, GRIB2MISSING_u4);
380
6.76k
    const double dfAngUnit = 1e-6;
381
6.76k
    WriteScaled(dfLLY, dfAngUnit);
382
6.76k
    WriteScaled(dfLLX, dfAngUnit);
383
6.76k
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
384
6.76k
    WriteScaled(dfURY, dfAngUnit);
385
6.76k
    WriteScaled(dfURX, dfAngUnit);
386
6.76k
    WriteScaled(m_gt[1], dfAngUnit);
387
6.76k
    WriteScaled(fabs(m_gt[5]), dfAngUnit);
388
6.76k
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
389
6.76k
    WriteScaled(dfLatSouthernPole, dfAngUnit);
390
6.76k
    WriteScaled(Lon180to360(dfLonSouthernPole), dfAngUnit);
391
6.76k
    WriteScaled(dfAxisRotation, dfAngUnit);
392
393
6.76k
    return true;
394
6.76k
}
395
396
/************************************************************************/
397
/*                           TransformToGeo()                           */
398
/************************************************************************/
399
400
bool GRIB2Section3Writer::TransformToGeo(double &dfX, double &dfY)
401
10.6k
{
402
10.6k
    OGRSpatialReference oLL;  // Construct the "geographic" part of oSRS.
403
10.6k
    oLL.CopyGeogCSFrom(&oSRS);
404
10.6k
    oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
405
10.6k
    OGRCoordinateTransformation *poTransformSRSToLL =
406
10.6k
        OGRCreateCoordinateTransformation(&(oSRS), &(oLL));
407
10.6k
    if (poTransformSRSToLL == nullptr ||
408
10.6k
        !poTransformSRSToLL->Transform(1, &dfX, &dfY))
409
0
    {
410
0
        delete poTransformSRSToLL;
411
0
        return false;
412
0
    }
413
10.6k
    delete poTransformSRSToLL;
414
10.6k
    if (dfX < 0.0)
415
2.15k
        dfX += 360.0;
416
10.6k
    return true;
417
10.6k
}
418
419
/************************************************************************/
420
/*                           WriteMercator1SP()                         */
421
/************************************************************************/
422
423
bool GRIB2Section3Writer::WriteMercator1SP()
424
10
{
425
10
    if (oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0) != 0.0)
426
0
    {
427
0
        CPLError(CE_Failure, CPLE_NotSupported,
428
0
                 "Mercator_1SP with central_meridian != 0 not supported");
429
0
        return false;
430
0
    }
431
10
    if (oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
432
0
    {
433
0
        CPLError(CE_Failure, CPLE_NotSupported,
434
0
                 "Mercator_1SP with latitude_of_origin != 0 not supported");
435
0
        return false;
436
0
    }
437
438
10
    OGRSpatialReference *poMerc2SP =
439
10
        oSRS.convertToOtherProjection(SRS_PT_MERCATOR_2SP);
440
10
    if (poMerc2SP == nullptr)
441
0
    {
442
0
        CPLError(CE_Failure, CPLE_NotSupported,
443
0
                 "Cannot get Mercator_2SP formulation");
444
0
        return false;
445
0
    }
446
447
10
    bool bRet = WriteMercator2SP(poMerc2SP);
448
10
    delete poMerc2SP;
449
10
    return bRet;
450
10
}
451
452
/************************************************************************/
453
/*                           WriteMercator2SP()                         */
454
/************************************************************************/
455
456
bool GRIB2Section3Writer::WriteMercator2SP(OGRSpatialReference *poSRS)
457
10
{
458
10
    if (poSRS == nullptr)
459
0
        poSRS = &oSRS;
460
461
10
    if (poSRS->GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0) != 0.0)
462
0
    {
463
0
        CPLError(CE_Failure, CPLE_NotSupported,
464
0
                 "Mercator_2SP with central_meridian != 0 not supported");
465
0
        return false;
466
0
    }
467
10
    if (poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0) != 0.0)
468
0
    {
469
0
        CPLError(CE_Failure, CPLE_NotSupported,
470
0
                 "Mercator_2SP with latitude_of_origin != 0 not supported");
471
0
        return false;
472
0
    }
473
474
10
    WriteUInt16(fp, GS3_MERCATOR);  // Grid template number
475
476
10
    WriteEllipsoidAndRasterSize();
477
478
10
    if (!TransformToGeo(dfLLX, dfLLY) || !TransformToGeo(dfURX, dfURY))
479
0
        return false;
480
481
10
    const double dfAngUnit = 1e-6;
482
10
    WriteScaled(dfLLY, dfAngUnit);
483
10
    WriteScaled(dfLLX, dfAngUnit);
484
10
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
485
10
    WriteScaled(poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0),
486
10
                dfAngUnit);
487
10
    WriteScaled(dfURY, dfAngUnit);
488
10
    WriteScaled(dfURX, dfAngUnit);
489
10
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
490
10
    WriteInt32(fp, 0);          // angle of the grid
491
10
    const double dfLinearUnit = 1e-3;
492
10
    WriteScaled(m_gt[1], dfLinearUnit);
493
10
    WriteScaled(fabs(m_gt[5]), dfLinearUnit);
494
495
10
    return true;
496
10
}
497
498
/************************************************************************/
499
/*                      WriteTransverseMercator()                       */
500
/************************************************************************/
501
502
bool GRIB2Section3Writer::WriteTransverseMercator()
503
1.20k
{
504
1.20k
    WriteUInt16(fp, GS3_TRANSVERSE_MERCATOR);  // Grid template number
505
1.20k
    WriteEllipsoidAndRasterSize();
506
507
1.20k
    const double dfAngUnit = 1e-6;
508
1.20k
    WriteScaled(oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0),
509
1.20k
                dfAngUnit);
510
1.20k
    WriteScaled(Lon180to360(oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)),
511
1.20k
                dfAngUnit);
512
1.20k
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
513
1.20k
    float fScale =
514
1.20k
        static_cast<float>(oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0));
515
1.20k
    WriteFloat32(fp, fScale);
516
1.20k
    const double dfLinearUnit = 1e-2;
517
1.20k
    WriteScaled(oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0), dfLinearUnit);
518
1.20k
    WriteScaled(oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0), dfLinearUnit);
519
1.20k
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
520
1.20k
    WriteScaled(m_gt[1], dfLinearUnit);
521
1.20k
    WriteScaled(fabs(m_gt[5]), dfLinearUnit);
522
1.20k
    WriteScaled(dfLLX, dfLinearUnit);
523
1.20k
    WriteScaled(dfLLY, dfLinearUnit);
524
1.20k
    WriteScaled(dfURX, dfLinearUnit);
525
1.20k
    WriteScaled(dfURY, dfLinearUnit);
526
527
1.20k
    return true;
528
1.20k
}
529
530
/************************************************************************/
531
/*                       WritePolarStereographic()                       */
532
/************************************************************************/
533
534
bool GRIB2Section3Writer::WritePolarStereographic()
535
10.5k
{
536
10.5k
    WriteUInt16(fp, GS3_POLAR);  // Grid template number
537
10.5k
    WriteEllipsoidAndRasterSize();
538
539
10.5k
    if (!TransformToGeo(dfLLX, dfLLY))
540
0
        return false;
541
542
10.5k
    const double dfAngUnit = 1e-6;
543
10.5k
    WriteScaled(dfLLY, dfAngUnit);
544
10.5k
    WriteScaled(dfLLX, dfAngUnit);
545
10.5k
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
546
10.5k
    const double dfLatOrigin =
547
10.5k
        oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
548
10.5k
    WriteScaled(dfLatOrigin, dfAngUnit);
549
10.5k
    WriteScaled(Lon180to360(oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)),
550
10.5k
                dfAngUnit);
551
10.5k
    const double dfLinearUnit = 1e-3;
552
10.5k
    WriteScaled(m_gt[1], dfLinearUnit);
553
10.5k
    WriteScaled(fabs(m_gt[5]), dfLinearUnit);
554
    // Projection center flag: BIT1=0 North Pole, BIT1=1 South Pole
555
10.5k
    WriteByte(fp, (dfLatOrigin < 0) ? GRIB2BIT_1 : 0);
556
10.5k
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
557
558
10.5k
    return true;
559
10.5k
}
560
561
/************************************************************************/
562
/*                            WriteLCC1SP()                             */
563
/************************************************************************/
564
565
bool GRIB2Section3Writer::WriteLCC1SP()
566
0
{
567
0
    OGRSpatialReference *poLCC2SP =
568
0
        oSRS.convertToOtherProjection(SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP);
569
0
    if (poLCC2SP == nullptr)
570
0
    {
571
0
        CPLError(CE_Failure, CPLE_NotSupported,
572
0
                 "Cannot get Lambert_Conformal_Conic_2SP formulation");
573
0
        return false;
574
0
    }
575
576
0
    bool bRet = WriteLCC2SPOrAEA(poLCC2SP);
577
0
    delete poLCC2SP;
578
0
    return bRet;
579
0
}
580
581
/************************************************************************/
582
/*                            WriteLCC2SPOrAEA()                        */
583
/************************************************************************/
584
585
bool GRIB2Section3Writer::WriteLCC2SPOrAEA(OGRSpatialReference *poSRS)
586
0
{
587
0
    if (poSRS == nullptr)
588
0
        poSRS = &oSRS;
589
0
    if (EQUAL(poSRS->GetAttrValue("PROJECTION"),
590
0
              SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP))
591
0
        WriteUInt16(fp, GS3_LAMBERT);  // Grid template number
592
0
    else
593
0
        WriteUInt16(fp, GS3_ALBERS_EQUAL_AREA);  // Grid template number
594
595
0
    WriteEllipsoidAndRasterSize();
596
597
0
    if (!TransformToGeo(dfLLX, dfLLY))
598
0
        return false;
599
600
0
    const double dfAngUnit = 1e-6;
601
0
    WriteScaled(dfLLY, dfAngUnit);
602
0
    WriteScaled(dfLLX, dfAngUnit);
603
    // Resolution and component flags. "not applicable" ==> 0 ?
604
0
    WriteByte(fp, 0);
605
0
    WriteScaled(poSRS->GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0),
606
0
                dfAngUnit);
607
0
    WriteScaled(Lon180to360(oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)),
608
0
                dfAngUnit);
609
0
    const double dfLinearUnit = 1e-3;
610
0
    WriteScaled(m_gt[1], dfLinearUnit);
611
0
    WriteScaled(fabs(m_gt[5]), dfLinearUnit);
612
0
    WriteByte(fp, 0);           // Projection centre flag
613
0
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
614
0
    WriteScaled(poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0),
615
0
                dfAngUnit);
616
0
    WriteScaled(poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_2, 0.0),
617
0
                dfAngUnit);
618
    // Latitude of the southern pole of projection
619
0
    WriteUInt32(fp, GRIB2MISSING_u4);
620
    // Longitude of the southern pole of projection
621
0
    WriteUInt32(fp, GRIB2MISSING_u4);
622
0
    return true;
623
0
}
624
625
/************************************************************************/
626
/*                              WriteLAEA()                             */
627
/************************************************************************/
628
629
bool GRIB2Section3Writer::WriteLAEA()
630
0
{
631
0
    WriteUInt16(fp, GS3_LAMBERT_AZIMUTHAL);  // Grid template number
632
633
0
    WriteEllipsoidAndRasterSize();
634
635
0
    if (!TransformToGeo(dfLLX, dfLLY) || !TransformToGeo(dfURX, dfURY))
636
0
        return false;
637
638
0
    const bool bNormalizeLongitude =
639
0
        CPLTestBool(CPLGetConfigOption("GRIB_ADJUST_LONGITUDE_RANGE", "YES"));
640
641
0
    const double dfAngUnit = 1e-6;
642
0
    WriteScaled(dfLLY, dfAngUnit);
643
0
    if (!bNormalizeLongitude && dfLLX > 360)
644
0
        dfLLX -= 360;
645
0
    WriteScaled(dfLLX, dfAngUnit);
646
0
    WriteScaled(oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_CENTER, 0.0),
647
0
                dfAngUnit);
648
0
    const double dfLonCenter =
649
0
        oSRS.GetNormProjParm(SRS_PP_LONGITUDE_OF_CENTER, 0.0);
650
0
    WriteScaled(bNormalizeLongitude ? Lon180to360(dfLonCenter) : dfLonCenter,
651
0
                dfAngUnit);
652
0
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
653
0
    const double dfLinearUnit = 1e-3;
654
0
    WriteScaled(m_gt[1], dfLinearUnit);
655
0
    WriteScaled(fabs(m_gt[5]), dfLinearUnit);
656
0
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
657
0
    return true;
658
0
}
659
660
/************************************************************************/
661
/*                                Write()                               */
662
/************************************************************************/
663
664
bool GRIB2Section3Writer::Write()
665
28.9k
{
666
    // Section 3: Grid Definition Section
667
28.9k
    vsi_l_offset nStartSection = VSIFTellL(fp);
668
669
28.9k
    WriteUInt32(fp, GRIB2MISSING_u4);  // section size
670
671
28.9k
    WriteByte(fp, 3);  // section number
672
673
    // Source of grid definition = Specified in Code Table 3.1
674
28.9k
    WriteByte(fp, 0);
675
676
28.9k
    const GUInt32 nDataPoints =
677
28.9k
        static_cast<GUInt32>(poSrcDS->GetRasterXSize()) *
678
28.9k
        poSrcDS->GetRasterYSize();
679
28.9k
    WriteUInt32(fp, nDataPoints);
680
681
    // Number of octets for optional list of numbers defining number of points
682
28.9k
    WriteByte(fp, 0);
683
684
    // Interpretation of list of numbers defining number of points =
685
    // No appended list
686
28.9k
    WriteByte(fp, 0);
687
688
28.9k
    bool bRet = false;
689
28.9k
    if (oSRS.IsGeographic())
690
17.1k
    {
691
17.1k
        if (oSRS.IsDerivedGeographic())
692
6.76k
        {
693
6.76k
            const OGR_SRSNode *poConversion =
694
6.76k
                oSRS.GetAttrNode("DERIVINGCONVERSION");
695
6.76k
            const char *pszMethod = oSRS.GetAttrValue("METHOD");
696
6.76k
            if (!pszMethod)
697
0
                pszMethod = "unknown";
698
699
6.76k
            std::map<std::string, double> oValMap;
700
6.76k
            if (poConversion)
701
6.76k
            {
702
40.5k
                for (int iChild = 0; iChild < poConversion->GetChildCount();
703
33.8k
                     iChild++)
704
33.8k
                {
705
33.8k
                    const OGR_SRSNode *poNode = poConversion->GetChild(iChild);
706
33.8k
                    if (!EQUAL(poNode->GetValue(), "PARAMETER") ||
707
33.8k
                        poNode->GetChildCount() <= 2)
708
13.5k
                        continue;
709
20.2k
                    const char *pszParamStr = poNode->GetChild(0)->GetValue();
710
20.2k
                    const char *pszParamVal = poNode->GetChild(1)->GetValue();
711
20.2k
                    oValMap[pszParamStr] = CPLAtof(pszParamVal);
712
20.2k
                }
713
6.76k
            }
714
715
6.76k
            if (poConversion && EQUAL(pszMethod, "PROJ ob_tran o_proj=longlat"))
716
0
            {
717
0
                const double dfLon0 = oValMap["lon_0"];
718
0
                const double dfLonp = oValMap["o_lon_p"];
719
0
                const double dfLatp = oValMap["o_lat_p"];
720
721
0
                const double dfLatSouthernPole = -dfLatp;
722
0
                const double dfLonSouthernPole = dfLon0;
723
0
                const double dfAxisRotation = -dfLonp;
724
0
                bRet = WriteRotatedLatLon(dfLatSouthernPole, dfLonSouthernPole,
725
0
                                          dfAxisRotation);
726
0
            }
727
6.76k
            else if (poConversion &&
728
6.76k
                     EQUAL(pszMethod, "Pole rotation (netCDF CF convention)"))
729
0
            {
730
0
                const double dfGridNorthPoleLat =
731
0
                    oValMap["Grid north pole latitude (netCDF CF convention)"];
732
0
                const double dfGridNorthPoleLong =
733
0
                    oValMap["Grid north pole longitude (netCDF CF convention)"];
734
0
                const double dfNorthPoleGridLong =
735
0
                    oValMap["North pole grid longitude (netCDF CF convention)"];
736
737
0
                const double dfLon0 = 180.0 + dfGridNorthPoleLong;
738
0
                const double dfLonp = dfNorthPoleGridLong;
739
0
                const double dfLatp = dfGridNorthPoleLat;
740
741
0
                const double dfLatSouthernPole = -dfLatp;
742
0
                const double dfLonSouthernPole = dfLon0;
743
0
                const double dfAxisRotation = -dfLonp;
744
0
                bRet = WriteRotatedLatLon(dfLatSouthernPole, dfLonSouthernPole,
745
0
                                          dfAxisRotation);
746
0
            }
747
6.76k
            else if (poConversion &&
748
6.76k
                     EQUAL(pszMethod, "Pole rotation (GRIB convention)"))
749
6.76k
            {
750
6.76k
                const double dfLatSouthernPole =
751
6.76k
                    oValMap["Latitude of the southern pole (GRIB convention)"];
752
6.76k
                const double dfLonSouthernPole =
753
6.76k
                    oValMap["Longitude of the southern pole (GRIB convention)"];
754
6.76k
                const double dfAxisRotation =
755
6.76k
                    oValMap["Axis rotation (GRIB convention)"];
756
757
6.76k
                bRet = WriteRotatedLatLon(dfLatSouthernPole, dfLonSouthernPole,
758
6.76k
                                          dfAxisRotation);
759
6.76k
            }
760
0
            else
761
0
            {
762
0
                CPLError(CE_Failure, CPLE_NotSupported,
763
0
                         "Unsupported method for DerivedGeographicCRS: %s",
764
0
                         pszMethod);
765
0
                return false;
766
0
            }
767
6.76k
        }
768
10.3k
        else
769
10.3k
        {
770
10.3k
            bRet = WriteGeographic();
771
10.3k
        }
772
17.1k
    }
773
11.8k
    else if (pszProjection && EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
774
10
    {
775
10
        bRet = WriteMercator1SP();
776
10
    }
777
11.8k
    else if (pszProjection && EQUAL(pszProjection, SRS_PT_MERCATOR_2SP))
778
0
    {
779
0
        bRet = WriteMercator2SP();
780
0
    }
781
11.8k
    else if (pszProjection && EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
782
1.20k
    {
783
1.20k
        bRet = WriteTransverseMercator();
784
1.20k
    }
785
10.5k
    else if (pszProjection && EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC))
786
10.5k
    {
787
10.5k
        bRet = WritePolarStereographic();
788
10.5k
    }
789
0
    else if (pszProjection != nullptr &&
790
0
             EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP))
791
0
    {
792
0
        bRet = WriteLCC1SP();
793
0
    }
794
0
    else if (pszProjection != nullptr &&
795
0
             (EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ||
796
0
              EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA)))
797
0
    {
798
0
        bRet = WriteLCC2SPOrAEA();
799
0
    }
800
0
    else if (pszProjection &&
801
0
             EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA))
802
0
    {
803
0
        bRet = WriteLAEA();
804
0
    }
805
806
28.9k
    PatchSectionSize(fp, nStartSection);
807
808
28.9k
    return bRet;
809
28.9k
}
810
811
/************************************************************************/
812
/*                         GetBandOption()                              */
813
/************************************************************************/
814
815
static const char *GetBandOption(char **papszOptions, GDALDataset *poSrcDS,
816
                                 int nBand, const char *pszKey,
817
                                 const char *pszDefault)
818
724k
{
819
724k
    const char *pszVal = CSLFetchNameValue(
820
724k
        papszOptions, CPLSPrintf("BAND_%d_%s", nBand, pszKey));
821
724k
    if (pszVal == nullptr)
822
724k
    {
823
724k
        pszVal = CSLFetchNameValue(papszOptions, pszKey);
824
724k
    }
825
724k
    if (pszVal == nullptr && poSrcDS != nullptr)
826
289k
    {
827
289k
        pszVal = poSrcDS->GetRasterBand(nBand)->GetMetadataItem(
828
289k
            (CPLString("GRIB_") + pszKey).c_str());
829
289k
    }
830
724k
    if (pszVal == nullptr)
831
724k
    {
832
724k
        pszVal = pszDefault;
833
724k
    }
834
724k
    return pszVal;
835
724k
}
836
837
/************************************************************************/
838
/*                        GRIB2Section567Writer                         */
839
/************************************************************************/
840
841
class GRIB2Section567Writer
842
{
843
    VSILFILE *m_fp;
844
    GDALDataset *m_poSrcDS;
845
    int m_nBand;
846
    int m_nXSize;
847
    int m_nYSize;
848
    GUInt32 m_nDataPoints;
849
    GDALDataType m_eDT;
850
    GDALGeoTransform m_gt{};
851
    int m_nDecimalScaleFactor;
852
    double m_dfDecimalScale;
853
    float m_fMin;
854
    float m_fMax;
855
    double m_dfMinScaled;
856
    int m_nBits;
857
    bool m_bUseZeroBits;
858
    float m_fValOffset;
859
    int m_bHasNoData;
860
    double m_dfNoData;
861
    int m_nSplitAndSwap;
862
863
    float *GetFloatData();
864
    bool WriteSimplePacking();
865
    bool WriteComplexPacking(int nSpatialDifferencingOrder);
866
    bool WriteIEEE(GDALProgressFunc pfnProgress, void *pProgressData);
867
    bool WritePNG();
868
    bool WriteJPEG2000(char **papszOptions);
869
870
  public:
871
    GRIB2Section567Writer(VSILFILE *fp, GDALDataset *poSrcDS, int nBand,
872
                          int nSplitAndSwap);
873
874
    bool Write(float fValOffset, char **papszOptions,
875
               GDALProgressFunc pfnProgress, void *pProgressData);
876
    void WriteComplexPackingNoData();
877
};
878
879
/************************************************************************/
880
/*                      GRIB2Section567Writer()                         */
881
/************************************************************************/
882
883
GRIB2Section567Writer::GRIB2Section567Writer(VSILFILE *fp, GDALDataset *poSrcDS,
884
                                             int nBand, int nSplitAndSwap)
885
28.9k
    : m_fp(fp), m_poSrcDS(poSrcDS), m_nBand(nBand),
886
28.9k
      m_nXSize(poSrcDS->GetRasterXSize()), m_nYSize(poSrcDS->GetRasterYSize()),
887
28.9k
      m_nDataPoints(static_cast<GUInt32>(m_nXSize) * m_nYSize),
888
28.9k
      m_eDT(m_poSrcDS->GetRasterBand(m_nBand)->GetRasterDataType()),
889
28.9k
      m_nDecimalScaleFactor(0), m_dfDecimalScale(1.0), m_fMin(0.0f),
890
28.9k
      m_fMax(0.0f), m_dfMinScaled(0.0), m_nBits(0), m_bUseZeroBits(false),
891
28.9k
      m_fValOffset(0.0), m_bHasNoData(false), m_dfNoData(0.0),
892
28.9k
      m_nSplitAndSwap(nSplitAndSwap)
893
28.9k
{
894
28.9k
    m_poSrcDS->GetGeoTransform(m_gt);
895
28.9k
    m_dfNoData = m_poSrcDS->GetRasterBand(nBand)->GetNoDataValue(&m_bHasNoData);
896
28.9k
}
897
898
/************************************************************************/
899
/*                          GetFloatData()                              */
900
/************************************************************************/
901
902
float *GRIB2Section567Writer::GetFloatData()
903
12.6k
{
904
12.6k
    float *pafData =
905
12.6k
        static_cast<float *>(VSI_MALLOC2_VERBOSE(m_nDataPoints, sizeof(float)));
906
12.6k
    if (pafData == nullptr)
907
0
    {
908
0
        return nullptr;
909
0
    }
910
12.6k
    CPLErr eErr = m_poSrcDS->GetRasterBand(m_nBand)->RasterIO(
911
12.6k
        GF_Read, m_nSplitAndSwap, 0, m_nXSize - m_nSplitAndSwap, m_nYSize,
912
12.6k
        pafData + (m_gt[5] < 0 ? (m_nYSize - 1) * m_nXSize : 0),
913
12.6k
        m_nXSize - m_nSplitAndSwap, m_nYSize, GDT_Float32, sizeof(float),
914
12.6k
        m_gt[5] < 0 ? -static_cast<GSpacing>(m_nXSize * sizeof(float))
915
12.6k
                    : static_cast<GSpacing>(m_nXSize * sizeof(float)),
916
12.6k
        nullptr);
917
12.6k
    if (eErr != CE_None)
918
3
    {
919
3
        VSIFree(pafData);
920
3
        return nullptr;
921
3
    }
922
12.6k
    if (m_nSplitAndSwap > 0)
923
0
    {
924
0
        eErr = m_poSrcDS->GetRasterBand(m_nBand)->RasterIO(
925
0
            GF_Read, 0, 0, m_nSplitAndSwap, m_nYSize,
926
0
            pafData + (m_gt[5] < 0 ? (m_nYSize - 1) * m_nXSize : 0) +
927
0
                (m_nXSize - m_nSplitAndSwap),
928
0
            m_nSplitAndSwap, m_nYSize, GDT_Float32, sizeof(float),
929
0
            m_gt[5] < 0 ? -static_cast<GSpacing>(m_nXSize * sizeof(float))
930
0
                        : static_cast<GSpacing>(m_nXSize * sizeof(float)),
931
0
            nullptr);
932
0
        if (eErr != CE_None)
933
0
        {
934
0
            VSIFree(pafData);
935
0
            return nullptr;
936
0
        }
937
0
    }
938
939
12.6k
    m_fMin = std::numeric_limits<float>::max();
940
12.6k
    m_fMax = -std::numeric_limits<float>::max();
941
12.6k
    bool bHasNoDataValuePoint = false;
942
12.6k
    bool bHasDataValuePoint = false;
943
19.4M
    for (GUInt32 i = 0; i < m_nDataPoints; i++)
944
19.4M
    {
945
19.4M
        if (m_bHasNoData && pafData[i] == static_cast<float>(m_dfNoData))
946
6.21M
        {
947
6.21M
            if (!bHasNoDataValuePoint)
948
4.34k
                bHasNoDataValuePoint = true;
949
6.21M
            continue;
950
6.21M
        }
951
13.2M
        if (!std::isfinite(pafData[i]))
952
5
        {
953
5
            CPLError(CE_Failure, CPLE_NotSupported,
954
5
                     "Non-finite values not supported for "
955
5
                     "this data encoding");
956
5
            VSIFree(pafData);
957
5
            return nullptr;
958
5
        }
959
13.2M
        if (!bHasDataValuePoint)
960
12.4k
            bHasDataValuePoint = true;
961
13.2M
        pafData[i] += m_fValOffset;
962
13.2M
        if (pafData[i] < m_fMin)
963
41.0k
            m_fMin = pafData[i];
964
13.2M
        if (pafData[i] > m_fMax)
965
33.4k
            m_fMax = pafData[i];
966
13.2M
    }
967
12.6k
    if (m_fMin > m_fMax)
968
148
    {
969
148
        m_fMin = m_fMax = static_cast<float>(m_dfNoData);
970
148
    }
971
972
    // We check that the actual range of values got from the above RasterIO
973
    // request does not go over the expected range of the datatype, as we
974
    // later assume that for computing nMaxBitsPerElt.
975
    // This shouldn't happen for well-behaved drivers, but this can still
976
    // happen in practice, if some drivers don't completely fill buffers etc.
977
12.6k
    if (m_fMax > m_fMin && GDALDataTypeIsInteger(m_eDT) &&
978
12.6k
        ceil(log(m_fMax - m_fMin) / log(2.0)) > GDALGetDataTypeSizeBits(m_eDT))
979
1
    {
980
1
        CPLError(CE_Failure, CPLE_AppDefined,
981
1
                 "Garbage values found when requesting input dataset");
982
1
        VSIFree(pafData);
983
1
        return nullptr;
984
1
    }
985
986
12.6k
    m_dfMinScaled =
987
12.6k
        m_dfDecimalScale == 1.0 ? m_fMin : floor(m_fMin * m_dfDecimalScale);
988
12.6k
    if (!(m_dfMinScaled >= -std::numeric_limits<float>::max() &&
989
12.6k
          m_dfMinScaled < std::numeric_limits<float>::max()))
990
0
    {
991
0
        CPLError(CE_Failure, CPLE_AppDefined,
992
0
                 "Scaled min value not representable on IEEE754 "
993
0
                 "single precision float");
994
0
        VSIFree(pafData);
995
0
        return nullptr;
996
0
    }
997
998
12.6k
    const double dfScaledMaxDiff = (m_fMax - m_fMin) * m_dfDecimalScale;
999
12.6k
    if (GDALDataTypeIsFloating(m_eDT) && m_nBits == 0 && dfScaledMaxDiff > 0 &&
1000
12.6k
        dfScaledMaxDiff <= 256)
1001
0
    {
1002
0
        m_nBits = 8;
1003
0
    }
1004
1005
12.6k
    m_bUseZeroBits =
1006
12.6k
        (m_fMin == m_fMax && !(bHasDataValuePoint && bHasNoDataValuePoint)) ||
1007
12.6k
        (!GDALDataTypeIsFloating(m_eDT) && dfScaledMaxDiff < 1.0);
1008
1009
12.6k
    return pafData;
1010
12.6k
}
1011
1012
/************************************************************************/
1013
/*                        WriteSimplePacking()                          */
1014
/************************************************************************/
1015
1016
// See http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_temp5-0.shtml
1017
bool GRIB2Section567Writer::WriteSimplePacking()
1018
8.30k
{
1019
8.30k
    float *pafData = GetFloatData();
1020
8.30k
    if (pafData == nullptr)
1021
3
        return false;
1022
1023
8.29k
    const int nBitCorrectionForDec =
1024
8.29k
        static_cast<int>(ceil(m_nDecimalScaleFactor * log(10.0) / log(2.0)));
1025
8.29k
    const int nMaxBitsPerElt = std::max(
1026
8.29k
        1, std::min(31, (m_nBits > 0) ? m_nBits
1027
8.29k
                                      : GDALGetDataTypeSizeBits(m_eDT) +
1028
8.29k
                                            nBitCorrectionForDec));
1029
8.29k
    if (nMaxBitsPerElt > 0 &&
1030
8.29k
        m_nDataPoints > static_cast<GUInt32>(INT_MAX) / nMaxBitsPerElt)
1031
0
    {
1032
0
        CPLError(CE_Failure, CPLE_AppDefined,
1033
0
                 "Int overflow while computing maximum number of bits");
1034
0
        VSIFree(pafData);
1035
0
        return false;
1036
0
    }
1037
1038
8.29k
    const int nMaxSize = (m_nDataPoints * nMaxBitsPerElt + 7) / 8;
1039
8.29k
    void *pabyData = VSI_MALLOC_VERBOSE(nMaxSize);
1040
8.29k
    if (pabyData == nullptr)
1041
0
    {
1042
0
        VSIFree(pafData);
1043
0
        VSIFree(pabyData);
1044
0
        return false;
1045
0
    }
1046
1047
    // Indices expected by simpack()
1048
8.29k
    enum
1049
8.29k
    {
1050
8.29k
        TMPL5_R_IDX = 0,      // Reference value (R)
1051
8.29k
        TMPL5_E_IDX = 1,      // Binary scale factor (E)
1052
8.29k
        TMPL5_D_IDX = 2,      // Decimal scale factor (D)
1053
8.29k
        TMPL5_NBITS_IDX = 3,  // Number of bits used for each packed value
1054
8.29k
        TMPL5_TYPE_IDX = 4    // type of original data
1055
8.29k
    };
1056
1057
8.29k
    g2int idrstmpl[TMPL5_TYPE_IDX + 1] = {0};
1058
8.29k
    idrstmpl[TMPL5_R_IDX] = 0;  // reference value, to be filled by simpack
1059
8.29k
    idrstmpl[TMPL5_E_IDX] = 0;  // binary scale factor, to be filled by simpack
1060
8.29k
    idrstmpl[TMPL5_D_IDX] = m_nDecimalScaleFactor;
1061
    // to be filled by simpack if set to 0
1062
8.29k
    idrstmpl[TMPL5_NBITS_IDX] = m_nBits;
1063
    // to be filled by simpack (and we will ignore it)
1064
8.29k
    idrstmpl[TMPL5_TYPE_IDX] = 0;
1065
8.29k
    g2int nLengthPacked = 0;
1066
8.29k
    simpack(pafData, m_nDataPoints, idrstmpl,
1067
8.29k
            static_cast<unsigned char *>(pabyData), &nLengthPacked);
1068
8.29k
    CPLAssert(nLengthPacked <= nMaxSize);
1069
8.29k
    if (nLengthPacked < 0)
1070
1
    {
1071
1
        CPLError(CE_Failure, CPLE_AppDefined, "Error while packing");
1072
1
        VSIFree(pafData);
1073
1
        VSIFree(pabyData);
1074
1
        return false;
1075
1
    }
1076
1077
    // Section 5: Data Representation Section
1078
8.29k
    WriteUInt32(m_fp, 21);  // section size
1079
8.29k
    WriteByte(m_fp, 5);     // section number
1080
8.29k
    WriteUInt32(m_fp, m_nDataPoints);
1081
8.29k
    WriteUInt16(m_fp, GS5_SIMPLE);
1082
8.29k
    float fRefValue;
1083
8.29k
    memcpy(&fRefValue, &idrstmpl[TMPL5_R_IDX], 4);
1084
8.29k
    WriteFloat32(m_fp, fRefValue);
1085
8.29k
    WriteInt16(m_fp, idrstmpl[TMPL5_E_IDX]);
1086
8.29k
    WriteInt16(m_fp, idrstmpl[TMPL5_D_IDX]);
1087
8.29k
    WriteByte(m_fp, idrstmpl[TMPL5_NBITS_IDX]);
1088
    // Type of original data: 0=Floating, 1=Integer
1089
8.29k
    WriteByte(m_fp, GDALDataTypeIsFloating(m_eDT) ? 0 : 1);
1090
1091
    // Section 6: Bitmap section
1092
#ifdef DEBUG
1093
    if (CPLTestBool(CPLGetConfigOption("GRIB_WRITE_BITMAP_TEST", "NO")))
1094
    {
1095
        // Just for the purpose of generating a test product !
1096
        static int counter = 0;
1097
        counter++;
1098
        if (counter == 1)
1099
        {
1100
            WriteUInt32(m_fp, 6 + ((m_nDataPoints + 7) / 8));  // section size
1101
            WriteByte(m_fp, 6);                                // section number
1102
            WriteByte(m_fp, 0);                                // bitmap
1103
            for (GUInt32 i = 0; i < (m_nDataPoints + 7) / 8; i++)
1104
                WriteByte(m_fp, 255);
1105
        }
1106
        else
1107
        {
1108
            WriteUInt32(m_fp, 6);  // section size
1109
            WriteByte(m_fp, 6);    // section number
1110
            WriteByte(m_fp, 254);  // reuse previous bitmap
1111
        }
1112
    }
1113
    else
1114
#endif
1115
8.29k
    {
1116
8.29k
        WriteUInt32(m_fp, 6);              // section size
1117
8.29k
        WriteByte(m_fp, 6);                // section number
1118
8.29k
        WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1119
8.29k
    }
1120
1121
    // Section 7: Data Section
1122
8.29k
    WriteUInt32(m_fp, 5 + nLengthPacked);  // section size
1123
8.29k
    WriteByte(m_fp, 7);                    // section number
1124
8.29k
    if (static_cast<int>(VSIFWriteL(pabyData, 1, nLengthPacked, m_fp)) !=
1125
8.29k
        nLengthPacked)
1126
0
    {
1127
0
        VSIFree(pafData);
1128
0
        VSIFree(pabyData);
1129
0
        return false;
1130
0
    }
1131
1132
8.29k
    VSIFree(pafData);
1133
8.29k
    VSIFree(pabyData);
1134
1135
8.29k
    return true;
1136
8.29k
}
1137
1138
/************************************************************************/
1139
/*                      WriteComplexPackingNoData()                     */
1140
/************************************************************************/
1141
1142
void GRIB2Section567Writer::WriteComplexPackingNoData()
1143
4.31k
{
1144
4.31k
    if (!m_bHasNoData)
1145
0
    {
1146
0
        WriteUInt32(m_fp, GRIB2MISSING_u4);
1147
0
    }
1148
4.31k
    else if (GDALDataTypeIsFloating(m_eDT))
1149
145
    {
1150
145
        WriteFloat32(m_fp, static_cast<float>(m_dfNoData));
1151
145
    }
1152
4.16k
    else
1153
4.16k
    {
1154
4.16k
        if (GDALIsValueInRange<int>(m_dfNoData))
1155
4.16k
        {
1156
4.16k
            WriteInt32(m_fp, static_cast<int>(m_dfNoData));
1157
4.16k
        }
1158
0
        else
1159
0
        {
1160
0
            WriteUInt32(m_fp, GRIB2MISSING_u4);
1161
0
        }
1162
4.16k
    }
1163
4.31k
}
1164
1165
/************************************************************************/
1166
/*                       WriteComplexPacking()                          */
1167
/************************************************************************/
1168
1169
// See http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_temp5-2.shtml
1170
// and http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_temp5-3.shtml
1171
1172
bool GRIB2Section567Writer::WriteComplexPacking(int nSpatialDifferencingOrder)
1173
4.34k
{
1174
4.34k
    if (nSpatialDifferencingOrder < 0 || nSpatialDifferencingOrder > 2)
1175
0
    {
1176
0
        CPLError(CE_Failure, CPLE_AppDefined,
1177
0
                 "Unsupported value for SPATIAL_DIFFERENCING_ORDER");
1178
0
        return false;
1179
0
    }
1180
1181
4.34k
    float *pafData = GetFloatData();
1182
4.34k
    if (pafData == nullptr)
1183
6
        return false;
1184
1185
4.34k
    const float fNoData = static_cast<float>(m_dfNoData);
1186
4.34k
    if (m_bUseZeroBits)
1187
639
    {
1188
        // Case where all values are at nodata or a single value
1189
639
        VSIFree(pafData);
1190
1191
        // Section 5: Data Representation Section
1192
639
        WriteUInt32(m_fp, 47);  // section size
1193
639
        WriteByte(m_fp, 5);     // section number
1194
639
        WriteUInt32(m_fp, m_nDataPoints);
1195
639
        WriteUInt16(m_fp, GS5_CMPLX);
1196
639
        WriteFloat32(m_fp, m_fMin);  // ref value = nodata or single data
1197
639
        WriteInt16(m_fp, 0);         // binary scale factor
1198
639
        WriteInt16(m_fp, 0);         // decimal scale factor
1199
639
        WriteByte(m_fp, 0);          // number of bits
1200
        // Type of original data: 0=Floating, 1=Integer
1201
639
        WriteByte(m_fp, GDALDataTypeIsFloating(m_eDT) ? 0 : 1);
1202
639
        WriteByte(m_fp, 0);
1203
639
        WriteByte(m_fp, m_bHasNoData ? 1 : 0);  // 1 missing value
1204
639
        WriteComplexPackingNoData();
1205
639
        WriteUInt32(m_fp, GRIB2MISSING_u4);
1206
639
        WriteUInt32(m_fp, 0);
1207
639
        WriteByte(m_fp, 0);
1208
639
        WriteByte(m_fp, 0);
1209
639
        WriteUInt32(m_fp, 0);
1210
639
        WriteByte(m_fp, 0);
1211
639
        WriteUInt32(m_fp, 0);
1212
639
        WriteByte(m_fp, 0);
1213
1214
        // Section 6: Bitmap section
1215
639
        WriteUInt32(m_fp, 6);              // section size
1216
639
        WriteByte(m_fp, 6);                // section number
1217
639
        WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1218
1219
        // Section 7: Data Section
1220
639
        WriteUInt32(m_fp, 5);  // section size
1221
639
        WriteByte(m_fp, 7);    // section number
1222
1223
639
        return true;
1224
639
    }
1225
1226
3.70k
    const int nBitCorrectionForDec =
1227
3.70k
        static_cast<int>(ceil(m_nDecimalScaleFactor * log(10.0) / log(2.0)));
1228
3.70k
    const int nMaxBitsPerElt = std::max(
1229
3.70k
        1, std::min(31, (m_nBits > 0) ? m_nBits
1230
3.70k
                                      : GDALGetDataTypeSizeBits(m_eDT) +
1231
3.70k
                                            nBitCorrectionForDec));
1232
3.70k
    if (nMaxBitsPerElt > 0 &&
1233
3.70k
        m_nDataPoints > static_cast<GUInt32>(INT_MAX) / nMaxBitsPerElt)
1234
0
    {
1235
0
        CPLError(CE_Failure, CPLE_AppDefined,
1236
0
                 "Int overflow while computing maximum number of bits");
1237
0
        VSIFree(pafData);
1238
0
        return false;
1239
0
    }
1240
1241
    // No idea what is the exact maximum bound... Take the value of simple
1242
    // packing and multiply by 2, plus some constant...
1243
3.70k
    const int nMaxSize = 10000 + 2 * ((m_nDataPoints * nMaxBitsPerElt + 7) / 8);
1244
3.70k
    void *pabyData = VSI_MALLOC_VERBOSE(nMaxSize);
1245
3.70k
    if (pabyData == nullptr)
1246
0
    {
1247
0
        VSIFree(pafData);
1248
0
        VSIFree(pabyData);
1249
0
        return false;
1250
0
    }
1251
1252
3.70k
    const double dfScaledMaxDiff =
1253
3.70k
        (m_fMax == m_fMin) ? 1 : (m_fMax - m_fMin) * m_dfDecimalScale;
1254
3.70k
    if (m_nBits == 0)
1255
3.70k
    {
1256
3.70k
        double dfTemp = log(ceil(dfScaledMaxDiff)) / log(2.0);
1257
3.70k
        m_nBits = std::max(1, std::min(31, static_cast<int>(ceil(dfTemp))));
1258
3.70k
    }
1259
3.70k
    const int nMaxNum = (m_nBits == 31) ? INT_MAX : ((1 << m_nBits) - 1);
1260
3.70k
    double dfTemp = log(nMaxNum / dfScaledMaxDiff) / log(2.0);
1261
3.70k
    int nBinaryScaleFactor = static_cast<GInt16>(ceil(-dfTemp));
1262
1263
    // Indices expected by cmplxpack()
1264
3.70k
    enum
1265
3.70k
    {
1266
3.70k
        TMPL5_R_IDX = 0,                        // reference value
1267
3.70k
        TMPL5_E_IDX = 1,                        // binary scale factor
1268
3.70k
        TMPL5_D_IDX = 2,                        // decimal scale factor
1269
3.70k
        TMPL5_NBITS_IDX = 3,                    // number of bits
1270
3.70k
        TMPL5_TYPE_IDX = 4,                     // type of original data
1271
3.70k
        TMPL5_GROUP_SPLITTING_IDX = 5,          // Group splitting method used
1272
3.70k
        TMPL5_MISSING_VALUE_MGNT_IDX = 6,       // Missing value management used
1273
3.70k
        TMPL5_PRIMARY_MISSING_VALUE_IDX = 7,    // Primary missing value
1274
3.70k
        TMPL5_SECONDARY_MISSING_VALUE_IDX = 8,  // Secondary missing value
1275
3.70k
        TMPL5_NG_IDX = 9,                 // number of groups of data values
1276
3.70k
        TMPL5_REF_GROUP_WIDTHS_IDX = 10,  // Reference for group widths
1277
        // Number of bits used for the group widths
1278
3.70k
        TMPL5_NBITS_GROUP_WIDTHS_IDX = 11,
1279
3.70k
        TMPL5_REF_GROUP_LENGTHS_IDX = 12,  // Reference for group lengths
1280
        // Length increment for the group lengths
1281
3.70k
        TMPL5_LENGTH_INCR_GROUP_LENGTHS_IDX = 13,
1282
3.70k
        TMPL5_TRUE_LENGTH_LAST_GROUP_IDX = 14,  // True length of last group
1283
        // Number of bits used for the scaled group lengths
1284
3.70k
        TMPL5_NBITS_SCALED_GROUP_LENGTHS_IDX = 15,
1285
        // Order of spatial differencing
1286
3.70k
        TMPL5_ORDER_SPATIAL_DIFFERENCE_IDX = 16,
1287
        // Number of octets required in the data section to specify extra
1288
        // descriptors needed for spatial differencing
1289
3.70k
        TMPL5_NB_OCTETS_EXTRA_DESCR_IDX = 17
1290
3.70k
    };
1291
1292
3.70k
    g2int idrstmpl[TMPL5_NB_OCTETS_EXTRA_DESCR_IDX + 1] = {0};
1293
3.70k
    idrstmpl[TMPL5_E_IDX] = nBinaryScaleFactor;
1294
3.70k
    idrstmpl[TMPL5_D_IDX] = m_nDecimalScaleFactor;
1295
3.70k
    idrstmpl[TMPL5_MISSING_VALUE_MGNT_IDX] = m_bHasNoData ? 1 : 0;
1296
3.70k
    idrstmpl[TMPL5_ORDER_SPATIAL_DIFFERENCE_IDX] = nSpatialDifferencingOrder;
1297
3.70k
    if (m_bHasNoData)
1298
3.70k
    {
1299
3.70k
        memcpy(&idrstmpl[TMPL5_PRIMARY_MISSING_VALUE_IDX], &fNoData, 4);
1300
3.70k
    }
1301
3.70k
    g2int nLengthPacked = 0;
1302
3.70k
    const int nTemplateNumber =
1303
3.70k
        (nSpatialDifferencingOrder > 0) ? GS5_CMPLXSEC : GS5_CMPLX;
1304
3.70k
    cmplxpack(pafData, m_nDataPoints, nTemplateNumber, idrstmpl,
1305
3.70k
              static_cast<unsigned char *>(pabyData), &nLengthPacked);
1306
3.70k
    CPLAssert(nLengthPacked <= nMaxSize);
1307
3.70k
    if (nLengthPacked < 0)
1308
28
    {
1309
28
        CPLError(CE_Failure, CPLE_AppDefined, "Error while packing");
1310
28
        VSIFree(pafData);
1311
28
        VSIFree(pabyData);
1312
28
        return false;
1313
28
    }
1314
1315
    // Section 5: Data Representation Section
1316
3.67k
    WriteUInt32(m_fp, nTemplateNumber == GS5_CMPLX ? 47 : 49);  // section size
1317
3.67k
    WriteByte(m_fp, 5);  // section number
1318
3.67k
    WriteUInt32(m_fp, m_nDataPoints);
1319
3.67k
    WriteUInt16(m_fp, nTemplateNumber);
1320
3.67k
    float fRefValue;
1321
3.67k
    memcpy(&fRefValue, &idrstmpl[TMPL5_R_IDX], 4);
1322
3.67k
    WriteFloat32(m_fp, fRefValue);
1323
3.67k
    WriteInt16(m_fp, idrstmpl[TMPL5_E_IDX]);
1324
3.67k
    WriteInt16(m_fp, idrstmpl[TMPL5_D_IDX]);
1325
3.67k
    WriteByte(m_fp, idrstmpl[TMPL5_NBITS_IDX]);
1326
    // Type of original data: 0=Floating, 1=Integer
1327
3.67k
    WriteByte(m_fp, GDALDataTypeIsFloating(m_eDT) ? 0 : 1);
1328
3.67k
    WriteByte(m_fp, idrstmpl[TMPL5_GROUP_SPLITTING_IDX]);
1329
3.67k
    WriteByte(m_fp, idrstmpl[TMPL5_MISSING_VALUE_MGNT_IDX]);
1330
3.67k
    WriteComplexPackingNoData();
1331
3.67k
    WriteUInt32(m_fp, GRIB2MISSING_u4);
1332
3.67k
    WriteUInt32(m_fp, idrstmpl[TMPL5_NG_IDX]);
1333
3.67k
    WriteByte(m_fp, idrstmpl[TMPL5_REF_GROUP_WIDTHS_IDX]);
1334
3.67k
    WriteByte(m_fp, idrstmpl[TMPL5_NBITS_GROUP_WIDTHS_IDX]);
1335
3.67k
    WriteUInt32(m_fp, idrstmpl[TMPL5_REF_GROUP_LENGTHS_IDX]);
1336
3.67k
    WriteByte(m_fp, idrstmpl[TMPL5_LENGTH_INCR_GROUP_LENGTHS_IDX]);
1337
3.67k
    WriteUInt32(m_fp, idrstmpl[TMPL5_TRUE_LENGTH_LAST_GROUP_IDX]);
1338
3.67k
    WriteByte(m_fp, idrstmpl[TMPL5_NBITS_SCALED_GROUP_LENGTHS_IDX]);
1339
3.67k
    if (nTemplateNumber == GS5_CMPLXSEC)
1340
0
    {
1341
0
        WriteByte(m_fp, nSpatialDifferencingOrder);
1342
0
        WriteByte(m_fp, idrstmpl[TMPL5_NB_OCTETS_EXTRA_DESCR_IDX]);
1343
0
    }
1344
1345
    // Section 6: Bitmap section
1346
3.67k
    WriteUInt32(m_fp, 6);              // section size
1347
3.67k
    WriteByte(m_fp, 6);                // section number
1348
3.67k
    WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1349
1350
    // Section 7: Data Section
1351
3.67k
    WriteUInt32(m_fp, 5 + nLengthPacked);  // section size
1352
3.67k
    WriteByte(m_fp, 7);                    // section number
1353
3.67k
    if (static_cast<int>(VSIFWriteL(pabyData, 1, nLengthPacked, m_fp)) !=
1354
3.67k
        nLengthPacked)
1355
0
    {
1356
0
        VSIFree(pafData);
1357
0
        VSIFree(pabyData);
1358
0
        return false;
1359
0
    }
1360
1361
3.67k
    VSIFree(pafData);
1362
3.67k
    VSIFree(pabyData);
1363
1364
3.67k
    return true;
1365
3.67k
}
1366
1367
/************************************************************************/
1368
/*                             WriteIEEE()                              */
1369
/************************************************************************/
1370
1371
// See http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_temp5-4.shtml
1372
bool GRIB2Section567Writer::WriteIEEE(GDALProgressFunc pfnProgress,
1373
                                      void *pProgressData)
1374
16.3k
{
1375
16.3k
    GDALDataType eReqDT;
1376
16.3k
    if (GDALGetDataTypeSizeBytes(m_eDT) <= 2 || m_eDT == GDT_Float32)
1377
863
        eReqDT = GDT_Float32;
1378
15.4k
    else
1379
15.4k
        eReqDT = GDT_Float64;
1380
1381
    // Section 5: Data Representation Section
1382
16.3k
    WriteUInt32(m_fp, 12);  // section size
1383
16.3k
    WriteByte(m_fp, 5);     // section number
1384
16.3k
    WriteUInt32(m_fp, m_nDataPoints);
1385
16.3k
    WriteUInt16(m_fp, GS5_IEEE);
1386
16.3k
    WriteByte(m_fp, (eReqDT == GDT_Float32) ? 1 : 2);  // Precision
1387
1388
    // Section 6: Bitmap section
1389
16.3k
    WriteUInt32(m_fp, 6);              // section size
1390
16.3k
    WriteByte(m_fp, 6);                // section number
1391
16.3k
    WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1392
1393
    // Section 7: Data Section
1394
16.3k
    const size_t nBufferSize =
1395
16.3k
        static_cast<size_t>(m_nXSize) * GDALGetDataTypeSizeBytes(eReqDT);
1396
    // section size
1397
16.3k
    WriteUInt32(m_fp, static_cast<GUInt32>(5 + nBufferSize * m_nYSize));
1398
16.3k
    WriteByte(m_fp, 7);  // section number
1399
16.3k
    void *pData = CPLMalloc(nBufferSize);
1400
16.3k
    const int nDenominator = std::max(1, m_poSrcDS->GetRasterCount());
1401
16.3k
    void *pScaledProgressData = GDALCreateScaledProgress(
1402
16.3k
        static_cast<double>(m_nBand - 1) / nDenominator,
1403
16.3k
        static_cast<double>(m_nBand) / nDenominator, pfnProgress,
1404
16.3k
        pProgressData);
1405
143k
    for (int i = 0; i < m_nYSize; i++)
1406
127k
    {
1407
127k
        int iSrcLine = m_gt[5] < 0 ? m_nYSize - 1 - i : i;
1408
127k
        CPLErr eErr = m_poSrcDS->GetRasterBand(m_nBand)->RasterIO(
1409
127k
            GF_Read, m_nSplitAndSwap, iSrcLine, m_nXSize - m_nSplitAndSwap, 1,
1410
127k
            pData, m_nXSize - m_nSplitAndSwap, 1, eReqDT, 0, 0, nullptr);
1411
127k
        if (eErr != CE_None)
1412
8
        {
1413
8
            CPLFree(pData);
1414
8
            GDALDestroyScaledProgress(pScaledProgressData);
1415
8
            return false;
1416
8
        }
1417
127k
        if (m_nSplitAndSwap > 0)
1418
0
        {
1419
0
            eErr = m_poSrcDS->GetRasterBand(m_nBand)->RasterIO(
1420
0
                GF_Read, 0, iSrcLine, m_nSplitAndSwap, 1,
1421
0
                reinterpret_cast<void *>(reinterpret_cast<GByte *>(pData) +
1422
0
                                         (m_nXSize - m_nSplitAndSwap) *
1423
0
                                             GDALGetDataTypeSizeBytes(eReqDT)),
1424
0
                m_nSplitAndSwap, 1, eReqDT, 0, 0, nullptr);
1425
0
            if (eErr != CE_None)
1426
0
            {
1427
0
                CPLFree(pData);
1428
0
                GDALDestroyScaledProgress(pScaledProgressData);
1429
0
                return false;
1430
0
            }
1431
0
        }
1432
127k
        if (m_fValOffset != 0.0)
1433
0
        {
1434
0
            if (eReqDT == GDT_Float32)
1435
0
            {
1436
0
                for (int j = 0; j < m_nXSize; j++)
1437
0
                {
1438
0
                    static_cast<float *>(pData)[j] += m_fValOffset;
1439
0
                }
1440
0
            }
1441
0
            else
1442
0
            {
1443
0
                for (int j = 0; j < m_nXSize; j++)
1444
0
                {
1445
0
                    static_cast<double *>(pData)[j] += m_fValOffset;
1446
0
                }
1447
0
            }
1448
0
        }
1449
127k
#ifdef CPL_LSB
1450
127k
        GDALSwapWords(pData, GDALGetDataTypeSizeBytes(eReqDT), m_nXSize,
1451
127k
                      GDALGetDataTypeSizeBytes(eReqDT));
1452
127k
#endif
1453
127k
        if (VSIFWriteL(pData, 1, nBufferSize, m_fp) != nBufferSize)
1454
0
        {
1455
0
            CPLFree(pData);
1456
0
            GDALDestroyScaledProgress(pScaledProgressData);
1457
0
            return false;
1458
0
        }
1459
127k
        if (!GDALScaledProgress(static_cast<double>(i + 1) / m_nYSize, nullptr,
1460
127k
                                pScaledProgressData))
1461
0
        {
1462
0
            CPLFree(pData);
1463
0
            GDALDestroyScaledProgress(pScaledProgressData);
1464
0
            return false;
1465
0
        }
1466
127k
    }
1467
16.3k
    GDALDestroyScaledProgress(pScaledProgressData);
1468
16.3k
    CPLFree(pData);
1469
1470
16.3k
    return true;
1471
16.3k
}
1472
1473
/************************************************************************/
1474
/*                        WrapArrayAsMemDataset()                       */
1475
/************************************************************************/
1476
1477
static GDALDataset *WrapArrayAsMemDataset(int nXSize, int nYSize,
1478
                                          GDALDataType eReducedDT, void *pData)
1479
0
{
1480
0
    CPLAssert(eReducedDT == GDT_Byte || eReducedDT == GDT_UInt16);
1481
0
    auto poMEMDS =
1482
0
        MEMDataset::Create("", nXSize, nYSize, 0, eReducedDT, nullptr);
1483
0
    GByte *pabyData = static_cast<GByte *>(pData);
1484
#ifdef CPL_MSB
1485
    if (eReducedDT == GDT_Byte)
1486
        pabyData++;
1487
#endif
1488
0
    auto hBand =
1489
0
        MEMCreateRasterBandEx(poMEMDS, 1, pabyData, eReducedDT, 2, 0, false);
1490
0
    poMEMDS->AddMEMBand(hBand);
1491
0
    return poMEMDS;
1492
0
}
1493
1494
/************************************************************************/
1495
/*                      GetRoundedToUpperPowerOfTwo()                   */
1496
/************************************************************************/
1497
1498
static int GetRoundedToUpperPowerOfTwo(int nBits)
1499
0
{
1500
0
    if (nBits == 3)
1501
0
        nBits = 4;
1502
0
    else if (nBits > 4 && nBits < 8)
1503
0
        nBits = 8;
1504
0
    else if (nBits > 8 && nBits < 15)
1505
0
        nBits = 16;
1506
0
    return nBits;
1507
0
}
1508
1509
/************************************************************************/
1510
/*                             GetScaledData()                          */
1511
/************************************************************************/
1512
1513
static GUInt16 *GetScaledData(GUInt32 nDataPoints, const float *pafData,
1514
                              float fMin, float fMax, double dfDecimalScale,
1515
                              double dfMinScaled,
1516
                              bool bOnlyPowerOfTwoDepthAllowed, int &nBits,
1517
                              GInt16 &nBinaryScaleFactor)
1518
0
{
1519
0
    bool bDone = false;
1520
0
    nBinaryScaleFactor = 0;
1521
0
    GUInt16 *panData = static_cast<GUInt16 *>(
1522
0
        VSI_MALLOC2_VERBOSE(nDataPoints, sizeof(GUInt16)));
1523
0
    if (panData == nullptr)
1524
0
    {
1525
0
        return nullptr;
1526
0
    }
1527
1528
0
    const double dfScaledMaxDiff = (fMax - fMin) * dfDecimalScale;
1529
0
    if (nBits == 0)
1530
0
    {
1531
0
        nBits = (g2int)ceil(log(ceil(dfScaledMaxDiff)) / log(2.0));
1532
0
        if (nBits > 16)
1533
0
        {
1534
0
            CPLError(CE_Warning, CPLE_AppDefined,
1535
0
                     "More than 16 bits of integer precision would be "
1536
0
                     "required. Dropping precision to fit on 16 bits");
1537
0
            nBits = 16;
1538
0
        }
1539
0
        else
1540
0
        {
1541
0
            bDone = true;
1542
0
            for (GUInt32 i = 0; i < nDataPoints; i++)
1543
0
            {
1544
0
                panData[i] = static_cast<GUInt16>(
1545
0
                    0.5 + (pafData[i] * dfDecimalScale - dfMinScaled));
1546
0
            }
1547
0
        }
1548
0
    }
1549
1550
0
    if (bOnlyPowerOfTwoDepthAllowed)
1551
0
        nBits = GetRoundedToUpperPowerOfTwo(nBits);
1552
1553
0
    if (!bDone && nBits != 0)
1554
0
    {
1555
0
        if (nBits > 16)
1556
0
        {
1557
0
            CPLError(CE_Warning, CPLE_AppDefined,
1558
0
                     "Maximum bit depth supported is 16. Using that");
1559
0
            nBits = 16;
1560
0
        }
1561
0
        const int nMaxNum = (1 << nBits) - 1;
1562
0
        double dfTemp = log(nMaxNum / dfScaledMaxDiff) / log(2.0);
1563
0
        nBinaryScaleFactor = static_cast<GInt16>(ceil(-dfTemp));
1564
0
        double dfBinaryScale = pow(2.0, -1.0 * nBinaryScaleFactor);
1565
0
        for (GUInt32 i = 0; i < nDataPoints; i++)
1566
0
        {
1567
0
            panData[i] = static_cast<GUInt16>(
1568
0
                0.5 +
1569
0
                (pafData[i] * dfDecimalScale - dfMinScaled) * dfBinaryScale);
1570
0
        }
1571
0
    }
1572
1573
0
    return panData;
1574
0
}
1575
1576
/************************************************************************/
1577
/*                              WritePNG()                              */
1578
/************************************************************************/
1579
1580
// See http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_temp5-41.shtml
1581
bool GRIB2Section567Writer::WritePNG()
1582
0
{
1583
0
    float *pafData = GetFloatData();
1584
0
    if (pafData == nullptr)
1585
0
        return false;
1586
1587
0
    if (m_bUseZeroBits)
1588
0
    {
1589
        // Section 5: Data Representation Section
1590
0
        WriteUInt32(m_fp, 21);  // section size
1591
0
        WriteByte(m_fp, 5);     // section number
1592
0
        WriteUInt32(m_fp, m_nDataPoints);
1593
0
        WriteUInt16(m_fp, GS5_PNG);
1594
0
        WriteFloat32(
1595
0
            m_fp,
1596
0
            static_cast<float>(m_dfMinScaled / m_dfDecimalScale));  // ref value
1597
0
        WriteInt16(m_fp, 0);  // Binary scale factor (E)
1598
0
        WriteInt16(m_fp, 0);  // Decimal scale factor (D)
1599
0
        WriteByte(m_fp, 0);   // Number of bits
1600
        // Type of original data: 0=Floating, 1=Integer
1601
0
        WriteByte(m_fp, GDALDataTypeIsFloating(m_eDT) ? 0 : 1);
1602
1603
        // Section 6: Bitmap section
1604
0
        WriteUInt32(m_fp, 6);              // section size
1605
0
        WriteByte(m_fp, 6);                // section number
1606
0
        WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1607
1608
        // Section 7: Data Section
1609
0
        WriteUInt32(m_fp, 5);  // section size
1610
0
        WriteByte(m_fp, 7);    // section number
1611
1612
0
        CPLFree(pafData);
1613
1614
0
        return true;
1615
0
    }
1616
1617
0
    GDALDriver *poPNGDriver =
1618
0
        reinterpret_cast<GDALDriver *>(GDALGetDriverByName("PNG"));
1619
0
    if (poPNGDriver == nullptr)
1620
0
    {
1621
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find PNG driver");
1622
0
        return false;
1623
0
    }
1624
1625
0
    GInt16 nBinaryScaleFactor = 0;
1626
0
    GUInt16 *panData =
1627
0
        GetScaledData(m_nDataPoints, pafData, m_fMin, m_fMax, m_dfDecimalScale,
1628
0
                      m_dfMinScaled, true, m_nBits, nBinaryScaleFactor);
1629
0
    if (panData == nullptr)
1630
0
    {
1631
0
        VSIFree(pafData);
1632
0
        return false;
1633
0
    }
1634
1635
0
    CPLFree(pafData);
1636
1637
0
    CPLStringList aosPNGOptions;
1638
0
    aosPNGOptions.SetNameValue("NBITS", CPLSPrintf("%d", m_nBits));
1639
1640
0
    const GDALDataType eReducedDT = (m_nBits <= 8) ? GDT_Byte : GDT_UInt16;
1641
0
    GDALDataset *poMEMDS =
1642
0
        WrapArrayAsMemDataset(m_nXSize, m_nYSize, eReducedDT, panData);
1643
1644
0
    const CPLString osTmpFile(VSIMemGenerateHiddenFilename("grib_driver.png"));
1645
0
    GDALDataset *poPNGDS = poPNGDriver->CreateCopy(
1646
0
        osTmpFile, poMEMDS, FALSE, aosPNGOptions.List(), nullptr, nullptr);
1647
0
    if (poPNGDS == nullptr)
1648
0
    {
1649
0
        CPLError(CE_Failure, CPLE_AppDefined, "PNG compression failed");
1650
0
        VSIUnlink(osTmpFile);
1651
0
        delete poMEMDS;
1652
0
        CPLFree(panData);
1653
0
        return false;
1654
0
    }
1655
0
    delete poPNGDS;
1656
0
    delete poMEMDS;
1657
0
    CPLFree(panData);
1658
1659
    // Section 5: Data Representation Section
1660
0
    WriteUInt32(m_fp, 21);  // section size
1661
0
    WriteByte(m_fp, 5);     // section number
1662
0
    WriteUInt32(m_fp, m_nDataPoints);
1663
0
    WriteUInt16(m_fp, GS5_PNG);
1664
0
    WriteFloat32(m_fp, static_cast<float>(m_dfMinScaled));
1665
0
    WriteInt16(m_fp, nBinaryScaleFactor);     // Binary scale factor (E)
1666
0
    WriteInt16(m_fp, m_nDecimalScaleFactor);  // Decimal scale factor (D)
1667
0
    WriteByte(m_fp, m_nBits);                 // Number of bits
1668
    // Type of original data: 0=Floating, 1=Integer
1669
0
    WriteByte(m_fp, GDALDataTypeIsFloating(m_eDT) ? 0 : 1);
1670
1671
    // Section 6: Bitmap section
1672
0
    WriteUInt32(m_fp, 6);              // section size
1673
0
    WriteByte(m_fp, 6);                // section number
1674
0
    WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1675
1676
    // Section 7: Data Section
1677
0
    vsi_l_offset nDataLength = 0;
1678
0
    GByte *pabyData = VSIGetMemFileBuffer(osTmpFile, &nDataLength, FALSE);
1679
0
    WriteUInt32(m_fp, static_cast<GUInt32>(5 + nDataLength));  // section size
1680
0
    WriteByte(m_fp, 7);                                        // section number
1681
0
    const size_t nDataLengthSize = static_cast<size_t>(nDataLength);
1682
0
    const bool bOK =
1683
0
        VSIFWriteL(pabyData, 1, nDataLengthSize, m_fp) == nDataLengthSize;
1684
1685
0
    VSIUnlink(osTmpFile);
1686
0
    VSIUnlink((osTmpFile + ".aux.xml").c_str());
1687
1688
0
    return bOK;
1689
0
}
1690
1691
/************************************************************************/
1692
/*                             WriteJPEG2000()                          */
1693
/************************************************************************/
1694
1695
// See http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_temp5-40.shtml
1696
bool GRIB2Section567Writer::WriteJPEG2000(char **papszOptions)
1697
0
{
1698
0
    float *pafData = GetFloatData();
1699
0
    if (pafData == nullptr)
1700
0
        return false;
1701
1702
0
    if (m_bUseZeroBits)
1703
0
    {
1704
        // Section 5: Data Representation Section
1705
0
        WriteUInt32(m_fp, 23);  // section size
1706
0
        WriteByte(m_fp, 5);     // section number
1707
0
        WriteUInt32(m_fp, m_nDataPoints);
1708
0
        WriteUInt16(m_fp, GS5_JPEG2000);
1709
0
        WriteFloat32(
1710
0
            m_fp,
1711
0
            static_cast<float>(m_dfMinScaled / m_dfDecimalScale));  // ref val
1712
0
        WriteInt16(m_fp, 0);  // Binary scale factor (E)
1713
0
        WriteInt16(m_fp, 0);  // Decimal scale factor (D)
1714
0
        WriteByte(m_fp, 0);   // Number of bits
1715
        // Type of original data: 0=Floating, 1=Integer
1716
0
        WriteByte(m_fp, GDALDataTypeIsFloating(m_eDT) ? 0 : 1);
1717
0
        WriteByte(m_fp, 0);                // compression type: lossless
1718
0
        WriteByte(m_fp, GRIB2MISSING_u1);  // compression ratio
1719
1720
        // Section 6: Bitmap section
1721
0
        WriteUInt32(m_fp, 6);              // section size
1722
0
        WriteByte(m_fp, 6);                // section number
1723
0
        WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1724
1725
        // Section 7: Data Section
1726
0
        WriteUInt32(m_fp, 5);  // section size
1727
0
        WriteByte(m_fp, 7);    // section number
1728
1729
0
        CPLFree(pafData);
1730
1731
0
        return true;
1732
0
    }
1733
1734
0
    GDALDriver *poJ2KDriver = nullptr;
1735
0
    const char *pszJ2KDriver = GetBandOption(papszOptions, nullptr, m_nBand,
1736
0
                                             "JPEG2000_DRIVER", nullptr);
1737
0
    if (pszJ2KDriver)
1738
0
    {
1739
0
        poJ2KDriver =
1740
0
            reinterpret_cast<GDALDriver *>(GDALGetDriverByName(pszJ2KDriver));
1741
0
    }
1742
0
    else
1743
0
    {
1744
0
        for (size_t i = 0; i < CPL_ARRAYSIZE(apszJ2KDrivers); i++)
1745
0
        {
1746
0
            poJ2KDriver = reinterpret_cast<GDALDriver *>(
1747
0
                GDALGetDriverByName(apszJ2KDrivers[i]));
1748
0
            if (poJ2KDriver)
1749
0
            {
1750
0
                CPLDebug("GRIB", "Using %s", poJ2KDriver->GetDescription());
1751
0
                break;
1752
0
            }
1753
0
        }
1754
0
    }
1755
0
    if (poJ2KDriver == nullptr)
1756
0
    {
1757
0
        CPLError(CE_Failure, CPLE_AppDefined, "Cannot find JPEG2000 driver");
1758
0
        VSIFree(pafData);
1759
0
        return false;
1760
0
    }
1761
1762
0
    GInt16 nBinaryScaleFactor = 0;
1763
0
    GUInt16 *panData =
1764
0
        GetScaledData(m_nDataPoints, pafData, m_fMin, m_fMax, m_dfDecimalScale,
1765
0
                      m_dfMinScaled, false, m_nBits, nBinaryScaleFactor);
1766
0
    if (panData == nullptr)
1767
0
    {
1768
0
        VSIFree(pafData);
1769
0
        return false;
1770
0
    }
1771
1772
0
    CPLFree(pafData);
1773
1774
0
    CPLStringList aosJ2KOptions;
1775
0
    int nCompressionRatio = atoi(GetBandOption(papszOptions, nullptr, m_nBand,
1776
0
                                               "COMPRESSION_RATIO", "1"));
1777
0
    if (m_nDataPoints < 10000 && nCompressionRatio > 1)
1778
0
    {
1779
        // Lossy compression with too few pixels is really lossy due to how
1780
        // codec work
1781
0
        CPLDebug("GRIB", "Forcing JPEG2000 lossless mode given "
1782
0
                         "the low number of pixels");
1783
0
        nCompressionRatio = 1;
1784
0
    }
1785
0
    const bool bLossLess = nCompressionRatio <= 1;
1786
0
    if (EQUAL(poJ2KDriver->GetDescription(), "JP2KAK"))
1787
0
    {
1788
0
        if (bLossLess)
1789
0
        {
1790
0
            aosJ2KOptions.SetNameValue("QUALITY", "100");
1791
0
        }
1792
0
        else
1793
0
        {
1794
0
            aosJ2KOptions.SetNameValue(
1795
0
                "QUALITY",
1796
0
                CPLSPrintf("%d", std::max(1, 100 / nCompressionRatio)));
1797
0
        }
1798
0
    }
1799
0
    else if (EQUAL(poJ2KDriver->GetDescription(), "JP2OPENJPEG"))
1800
0
    {
1801
0
        if (bLossLess)
1802
0
        {
1803
0
            aosJ2KOptions.SetNameValue("QUALITY", "100");
1804
0
            aosJ2KOptions.SetNameValue("REVERSIBLE", "YES");
1805
0
        }
1806
0
        else
1807
0
        {
1808
0
            aosJ2KOptions.SetNameValue(
1809
0
                "QUALITY", CPLSPrintf("%f", 100.0 / nCompressionRatio));
1810
0
        }
1811
0
    }
1812
0
    else if (EQUAL(poJ2KDriver->GetDescription(), "JP2ECW"))
1813
0
    {
1814
0
        if (bLossLess)
1815
0
        {
1816
0
            aosJ2KOptions.SetNameValue("TARGET", "0");
1817
0
        }
1818
0
        else
1819
0
        {
1820
0
            aosJ2KOptions.SetNameValue(
1821
0
                "TARGET", CPLSPrintf("%f", 100.0 - 100.0 / nCompressionRatio));
1822
0
        }
1823
0
    }
1824
0
    aosJ2KOptions.SetNameValue("NBITS", CPLSPrintf("%d", m_nBits));
1825
1826
0
    const GDALDataType eReducedDT = (m_nBits <= 8) ? GDT_Byte : GDT_UInt16;
1827
0
    GDALDataset *poMEMDS =
1828
0
        WrapArrayAsMemDataset(m_nXSize, m_nYSize, eReducedDT, panData);
1829
1830
0
    const CPLString osTmpFile(VSIMemGenerateHiddenFilename("grib_driver.j2k"));
1831
0
    GDALDataset *poJ2KDS = poJ2KDriver->CreateCopy(
1832
0
        osTmpFile, poMEMDS, FALSE, aosJ2KOptions.List(), nullptr, nullptr);
1833
0
    if (poJ2KDS == nullptr)
1834
0
    {
1835
0
        CPLError(CE_Failure, CPLE_AppDefined, "JPEG2000 compression failed");
1836
0
        VSIUnlink(osTmpFile);
1837
0
        delete poMEMDS;
1838
0
        CPLFree(panData);
1839
0
        return false;
1840
0
    }
1841
0
    delete poJ2KDS;
1842
0
    delete poMEMDS;
1843
0
    CPLFree(panData);
1844
1845
    // Section 5: Data Representation Section
1846
0
    WriteUInt32(m_fp, 23);  // section size
1847
0
    WriteByte(m_fp, 5);     // section number
1848
0
    WriteUInt32(m_fp, m_nDataPoints);
1849
0
    WriteUInt16(m_fp, GS5_JPEG2000);
1850
0
    WriteFloat32(m_fp, static_cast<float>(m_dfMinScaled));
1851
0
    WriteInt16(m_fp, nBinaryScaleFactor);     // Binary scale factor (E)
1852
0
    WriteInt16(m_fp, m_nDecimalScaleFactor);  // Decimal scale factor (D)
1853
0
    WriteByte(m_fp, m_nBits);                 // Number of bits
1854
    // Type of original data: 0=Floating, 1=Integer
1855
0
    WriteByte(m_fp, GDALDataTypeIsFloating(m_eDT) ? 0 : 1);
1856
    // compression type: lossless(0) or lossy(1)
1857
0
    WriteByte(m_fp, bLossLess ? 0 : 1);
1858
0
    WriteByte(m_fp, bLossLess ? GRIB2MISSING_u1
1859
0
                              : nCompressionRatio);  // compression ratio
1860
1861
    // Section 6: Bitmap section
1862
0
    WriteUInt32(m_fp, 6);              // section size
1863
0
    WriteByte(m_fp, 6);                // section number
1864
0
    WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1865
1866
    // Section 7: Data Section
1867
0
    vsi_l_offset nDataLength = 0;
1868
0
    GByte *pabyData = VSIGetMemFileBuffer(osTmpFile, &nDataLength, FALSE);
1869
0
    WriteUInt32(m_fp, static_cast<GUInt32>(5 + nDataLength));  // section size
1870
0
    WriteByte(m_fp, 7);                                        // section number
1871
0
    const size_t nDataLengthSize = static_cast<size_t>(nDataLength);
1872
0
    const bool bOK =
1873
0
        VSIFWriteL(pabyData, 1, nDataLengthSize, m_fp) == nDataLengthSize;
1874
1875
0
    VSIUnlink(osTmpFile);
1876
0
    VSIUnlink((osTmpFile + ".aux.xml").c_str());
1877
1878
0
    return bOK;
1879
0
}
1880
1881
/************************************************************************/
1882
/*                               Write()                                */
1883
/************************************************************************/
1884
1885
bool GRIB2Section567Writer::Write(float fValOffset, char **papszOptions,
1886
                                  GDALProgressFunc pfnProgress,
1887
                                  void *pProgressData)
1888
28.9k
{
1889
28.9k
    m_fValOffset = fValOffset;
1890
1891
28.9k
    typedef enum
1892
28.9k
    {
1893
28.9k
        SIMPLE_PACKING,
1894
28.9k
        COMPLEX_PACKING,
1895
28.9k
        IEEE_FLOATING_POINT,
1896
28.9k
        PNG,
1897
28.9k
        JPEG2000
1898
28.9k
    } GRIBDataEncoding;
1899
1900
28.9k
    if (m_eDT != GDT_Byte && m_eDT != GDT_UInt16 && m_eDT != GDT_Int16 &&
1901
28.9k
        m_eDT != GDT_UInt32 && m_eDT != GDT_Int32 && m_eDT != GDT_Float32 &&
1902
28.9k
        m_eDT != GDT_Float64)
1903
0
    {
1904
0
        CPLError(CE_Failure, CPLE_NotSupported, "Unsupported data type: %s",
1905
0
                 GDALGetDataTypeName(m_eDT));
1906
0
        return false;
1907
0
    }
1908
28.9k
    const char *pszDataEncoding =
1909
28.9k
        GetBandOption(papszOptions, nullptr, m_nBand, "DATA_ENCODING", "AUTO");
1910
28.9k
    GRIBDataEncoding eDataEncoding(SIMPLE_PACKING);
1911
28.9k
    const char *pszJ2KDriver = GetBandOption(papszOptions, nullptr, m_nBand,
1912
28.9k
                                             "JPEG2000_DRIVER", nullptr);
1913
28.9k
    const char *pszSpatialDifferencingOrder = GetBandOption(
1914
28.9k
        papszOptions, nullptr, m_nBand, "SPATIAL_DIFFERENCING_ORDER", nullptr);
1915
28.9k
    if (pszJ2KDriver && pszSpatialDifferencingOrder)
1916
0
    {
1917
0
        CPLError(CE_Failure, CPLE_NotSupported,
1918
0
                 "JPEG2000_DRIVER and SPATIAL_DIFFERENCING_ORDER are not "
1919
0
                 "compatible");
1920
0
        return false;
1921
0
    }
1922
1923
28.9k
    if (m_bHasNoData && !EQUAL(pszDataEncoding, "COMPLEX_PACKING") &&
1924
28.9k
        pszSpatialDifferencingOrder == nullptr)
1925
7.61k
    {
1926
7.61k
        double *padfVals = static_cast<double *>(
1927
7.61k
            VSI_MALLOC2_VERBOSE(m_nXSize, sizeof(double)));
1928
7.61k
        if (padfVals == nullptr)
1929
0
            return false;
1930
7.61k
        bool bFoundNoData = false;
1931
393k
        for (int j = 0; j < m_nYSize; j++)
1932
390k
        {
1933
390k
            CPLErr eErr = m_poSrcDS->GetRasterBand(m_nBand)->RasterIO(
1934
390k
                GF_Read, 0, j, m_nXSize, 1, padfVals, m_nXSize, 1, GDT_Float64,
1935
390k
                0, 0, nullptr);
1936
390k
            if (eErr != CE_None)
1937
6
            {
1938
6
                VSIFree(padfVals);
1939
6
                return false;
1940
6
            }
1941
1.67M
            for (int i = 0; i < m_nXSize; i++)
1942
1.28M
            {
1943
1.28M
                if (padfVals[i] == m_dfNoData)
1944
4.34k
                {
1945
4.34k
                    bFoundNoData = true;
1946
4.34k
                    break;
1947
4.34k
                }
1948
1.28M
            }
1949
390k
            if (bFoundNoData)
1950
4.34k
                break;
1951
390k
        }
1952
7.61k
        VSIFree(padfVals);
1953
1954
7.61k
        if (!bFoundNoData)
1955
3.26k
        {
1956
3.26k
            m_bHasNoData = false;
1957
3.26k
        }
1958
7.61k
    }
1959
1960
28.9k
    if (EQUAL(pszDataEncoding, "AUTO"))
1961
28.9k
    {
1962
28.9k
        if (m_bHasNoData || pszSpatialDifferencingOrder != nullptr)
1963
4.34k
        {
1964
4.34k
            eDataEncoding = COMPLEX_PACKING;
1965
4.34k
            CPLDebug("GRIB", "Using COMPLEX_PACKING");
1966
4.34k
        }
1967
24.6k
        else if (pszJ2KDriver != nullptr)
1968
0
        {
1969
0
            eDataEncoding = JPEG2000;
1970
0
            CPLDebug("GRIB", "Using JPEG2000");
1971
0
        }
1972
24.6k
        else if (m_eDT == GDT_Float32 || m_eDT == GDT_Float64)
1973
16.3k
        {
1974
16.3k
            eDataEncoding = IEEE_FLOATING_POINT;
1975
16.3k
            CPLDebug("GRIB", "Using IEEE_FLOATING_POINT");
1976
16.3k
        }
1977
8.30k
        else
1978
8.30k
        {
1979
8.30k
            CPLDebug("GRIB", "Using SIMPLE_PACKING");
1980
8.30k
        }
1981
28.9k
    }
1982
0
    else if (EQUAL(pszDataEncoding, "SIMPLE_PACKING"))
1983
0
    {
1984
0
        eDataEncoding = SIMPLE_PACKING;
1985
0
    }
1986
0
    else if (EQUAL(pszDataEncoding, "COMPLEX_PACKING"))
1987
0
    {
1988
0
        eDataEncoding = COMPLEX_PACKING;
1989
0
    }
1990
0
    else if (EQUAL(pszDataEncoding, "IEEE_FLOATING_POINT"))
1991
0
    {
1992
0
        eDataEncoding = IEEE_FLOATING_POINT;
1993
0
    }
1994
0
    else if (EQUAL(pszDataEncoding, "PNG"))
1995
0
    {
1996
0
        eDataEncoding = PNG;
1997
0
    }
1998
0
    else if (EQUAL(pszDataEncoding, "JPEG2000"))
1999
0
    {
2000
0
        eDataEncoding = JPEG2000;
2001
0
    }
2002
0
    else
2003
0
    {
2004
0
        CPLError(CE_Failure, CPLE_NotSupported, "Unsupported DATA_ENCODING=%s",
2005
0
                 pszDataEncoding);
2006
0
        return false;
2007
0
    }
2008
2009
28.9k
    const char *pszBits =
2010
28.9k
        GetBandOption(papszOptions, nullptr, m_nBand, "NBITS", nullptr);
2011
28.9k
    if (pszBits == nullptr && eDataEncoding != IEEE_FLOATING_POINT)
2012
12.6k
    {
2013
12.6k
        pszBits = m_poSrcDS->GetRasterBand(m_nBand)->GetMetadataItem(
2014
12.6k
            "DRS_NBITS", "GRIB");
2015
12.6k
    }
2016
16.3k
    else if (pszBits != nullptr && eDataEncoding == IEEE_FLOATING_POINT)
2017
0
    {
2018
0
        CPLError(CE_Warning, CPLE_NotSupported,
2019
0
                 "NBITS ignored for DATA_ENCODING = IEEE_FLOATING_POINT");
2020
0
    }
2021
28.9k
    if (pszBits == nullptr)
2022
28.9k
    {
2023
28.9k
        pszBits = "0";
2024
28.9k
    }
2025
28.9k
    m_nBits = std::max(0, atoi(pszBits));
2026
28.9k
    if (m_nBits > 31)
2027
0
    {
2028
0
        CPLError(CE_Warning, CPLE_NotSupported, "NBITS clamped to 31");
2029
0
        m_nBits = 31;
2030
0
    }
2031
2032
28.9k
    const char *pszDecimalScaleFactor = GetBandOption(
2033
28.9k
        papszOptions, nullptr, m_nBand, "DECIMAL_SCALE_FACTOR", nullptr);
2034
28.9k
    if (pszDecimalScaleFactor != nullptr)
2035
1
    {
2036
1
        m_nDecimalScaleFactor = atoi(pszDecimalScaleFactor);
2037
1
        if (m_nDecimalScaleFactor != 0 && eDataEncoding == IEEE_FLOATING_POINT)
2038
0
        {
2039
0
            CPLError(CE_Warning, CPLE_NotSupported,
2040
0
                     "DECIMAL_SCALE_FACTOR ignored for "
2041
0
                     "DATA_ENCODING = IEEE_FLOATING_POINT");
2042
0
        }
2043
1
        else if (m_nDecimalScaleFactor > 0 && !GDALDataTypeIsFloating(m_eDT))
2044
0
        {
2045
0
            CPLError(CE_Warning, CPLE_AppDefined,
2046
0
                     "DECIMAL_SCALE_FACTOR > 0 makes no sense for integer "
2047
0
                     "data types. Ignored");
2048
0
            m_nDecimalScaleFactor = 0;
2049
0
        }
2050
1
    }
2051
28.9k
    else if (eDataEncoding != IEEE_FLOATING_POINT)
2052
12.6k
    {
2053
12.6k
        pszDecimalScaleFactor =
2054
12.6k
            m_poSrcDS->GetRasterBand(m_nBand)->GetMetadataItem(
2055
12.6k
                "DRS_DECIMAL_SCALE_FACTOR", "GRIB");
2056
12.6k
        if (pszDecimalScaleFactor != nullptr)
2057
0
        {
2058
0
            m_nDecimalScaleFactor = atoi(pszDecimalScaleFactor);
2059
0
        }
2060
12.6k
    }
2061
28.9k
    m_dfDecimalScale = pow(10.0, static_cast<double>(m_nDecimalScaleFactor));
2062
2063
28.9k
    if (pszJ2KDriver != nullptr && eDataEncoding != JPEG2000)
2064
0
    {
2065
0
        CPLError(CE_Warning, CPLE_AppDefined,
2066
0
                 "JPEG2000_DRIVER option ignored for "
2067
0
                 "non-JPEG2000 DATA_ENCODING");
2068
0
    }
2069
28.9k
    if (pszSpatialDifferencingOrder && eDataEncoding != COMPLEX_PACKING)
2070
0
    {
2071
0
        CPLError(CE_Warning, CPLE_AppDefined,
2072
0
                 "SPATIAL_DIFFERENCING_ORDER option ignored for "
2073
0
                 "non-COMPLEX_PACKING DATA_ENCODING");
2074
0
    }
2075
28.9k
    if (m_bHasNoData && eDataEncoding != COMPLEX_PACKING)
2076
0
    {
2077
0
        CPLError(CE_Warning, CPLE_AppDefined,
2078
0
                 "non-COMPLEX_PACKING DATA_ENCODING cannot preserve nodata");
2079
0
    }
2080
2081
28.9k
    if (eDataEncoding == SIMPLE_PACKING)
2082
8.30k
    {
2083
8.30k
        return WriteSimplePacking();
2084
8.30k
    }
2085
20.6k
    else if (eDataEncoding == COMPLEX_PACKING)
2086
4.34k
    {
2087
4.34k
        const int nSpatialDifferencingOrder =
2088
4.34k
            pszSpatialDifferencingOrder ? atoi(pszSpatialDifferencingOrder) : 0;
2089
4.34k
        return WriteComplexPacking(nSpatialDifferencingOrder);
2090
4.34k
    }
2091
16.3k
    else if (eDataEncoding == IEEE_FLOATING_POINT)
2092
16.3k
    {
2093
16.3k
        return WriteIEEE(pfnProgress, pProgressData);
2094
16.3k
    }
2095
0
    else if (eDataEncoding == PNG)
2096
0
    {
2097
0
        return WritePNG();
2098
0
    }
2099
0
    else /* if( eDataEncoding == JPEG2000 ) */
2100
0
    {
2101
0
        return WriteJPEG2000(papszOptions);
2102
0
    }
2103
28.9k
}
2104
2105
/************************************************************************/
2106
/*                           GetIDSOption()                             */
2107
/************************************************************************/
2108
2109
static const char *GetIDSOption(char **papszOptions, GDALDataset *poSrcDS,
2110
                                int nBand, const char *pszKey,
2111
                                const char *pszDefault)
2112
202k
{
2113
202k
    const char *pszValue =
2114
202k
        GetBandOption(papszOptions, nullptr, nBand,
2115
202k
                      (CPLString("IDS_") + pszKey).c_str(), nullptr);
2116
202k
    if (pszValue == nullptr)
2117
202k
    {
2118
202k
        const char *pszIDS =
2119
202k
            GetBandOption(papszOptions, poSrcDS, nBand, "IDS", nullptr);
2120
202k
        if (pszIDS != nullptr)
2121
0
        {
2122
0
            char **papszTokens = CSLTokenizeString2(pszIDS, " ", 0);
2123
0
            pszValue = CSLFetchNameValue(papszTokens, pszKey);
2124
0
            if (pszValue)
2125
0
                pszValue = CPLSPrintf("%s", pszValue);
2126
0
            CSLDestroy(papszTokens);
2127
0
        }
2128
202k
    }
2129
202k
    if (pszValue == nullptr)
2130
202k
        pszValue = pszDefault;
2131
202k
    return pszValue;
2132
202k
}
2133
2134
/************************************************************************/
2135
/*                           WriteSection1()                            */
2136
/************************************************************************/
2137
2138
static void WriteSection1(VSILFILE *fp, GDALDataset *poSrcDS, int nBand,
2139
                          char **papszOptions)
2140
28.9k
{
2141
    // Section 1: Identification Section
2142
28.9k
    WriteUInt32(fp, 21);  // section size
2143
28.9k
    WriteByte(fp, 1);     // section number
2144
2145
28.9k
    GUInt16 nCenter = static_cast<GUInt16>(
2146
28.9k
        atoi(GetIDSOption(papszOptions, poSrcDS, nBand, "CENTER",
2147
28.9k
                          CPLSPrintf("%d", GRIB2MISSING_u1))));
2148
28.9k
    WriteUInt16(fp, nCenter);
2149
2150
28.9k
    GUInt16 nSubCenter = static_cast<GUInt16>(
2151
28.9k
        atoi(GetIDSOption(papszOptions, poSrcDS, nBand, "SUBCENTER",
2152
28.9k
                          CPLSPrintf("%d", GRIB2MISSING_u2))));
2153
28.9k
    WriteUInt16(fp, nSubCenter);
2154
2155
28.9k
    GByte nMasterTable = static_cast<GByte>(
2156
28.9k
        atoi(GetIDSOption(papszOptions, poSrcDS, nBand, "MASTER_TABLE", "2")));
2157
28.9k
    WriteByte(fp, nMasterTable);
2158
2159
28.9k
    WriteByte(fp, 0);  // local table
2160
2161
28.9k
    GByte nSignfRefTime = static_cast<GByte>(atoi(
2162
28.9k
        GetIDSOption(papszOptions, poSrcDS, nBand, "SIGNF_REF_TIME", "0")));
2163
28.9k
    WriteByte(fp, nSignfRefTime);  // Significance of reference time
2164
2165
28.9k
    const char *pszRefTime =
2166
28.9k
        GetIDSOption(papszOptions, poSrcDS, nBand, "REF_TIME", "");
2167
28.9k
    int nYear = 1970, nMonth = 1, nDay = 1, nHour = 0, nMinute = 0, nSecond = 0;
2168
28.9k
    sscanf(pszRefTime, "%04d-%02d-%02dT%02d:%02d:%02dZ", &nYear, &nMonth, &nDay,
2169
28.9k
           &nHour, &nMinute, &nSecond);
2170
28.9k
    WriteUInt16(fp, nYear);
2171
28.9k
    WriteByte(fp, nMonth);
2172
28.9k
    WriteByte(fp, nDay);
2173
28.9k
    WriteByte(fp, nHour);
2174
28.9k
    WriteByte(fp, nMinute);
2175
28.9k
    WriteByte(fp, nSecond);
2176
2177
28.9k
    GByte nProdStatus = static_cast<GByte>(
2178
28.9k
        atoi(GetIDSOption(papszOptions, poSrcDS, nBand, "PROD_STATUS",
2179
28.9k
                          CPLSPrintf("%d", GRIB2MISSING_u1))));
2180
28.9k
    WriteByte(fp, nProdStatus);
2181
2182
28.9k
    GByte nType = static_cast<GByte>(
2183
28.9k
        atoi(GetIDSOption(papszOptions, poSrcDS, nBand, "TYPE",
2184
28.9k
                          CPLSPrintf("%d", GRIB2MISSING_u1))));
2185
28.9k
    WriteByte(fp, nType);
2186
28.9k
}
2187
2188
/************************************************************************/
2189
/*                        WriteAssembledPDS()                           */
2190
/************************************************************************/
2191
2192
static void WriteAssembledPDS(VSILFILE *fp, const gtemplate *mappds,
2193
                              bool bWriteExt, char **papszTokens,
2194
                              std::vector<int> &anVals)
2195
0
{
2196
0
    const int iStart = bWriteExt ? mappds->maplen : 0;
2197
0
    const int iEnd =
2198
0
        bWriteExt ? mappds->maplen + mappds->extlen : mappds->maplen;
2199
0
    for (int i = iStart; i < iEnd; i++)
2200
0
    {
2201
0
        const int nVal = atoi(papszTokens[i]);
2202
0
        anVals.push_back(nVal);
2203
0
        const int nEltSize =
2204
0
            bWriteExt ? mappds->ext[i - mappds->maplen] : mappds->map[i];
2205
0
        if (nEltSize == 1)
2206
0
        {
2207
0
            if (nVal < 0 || nVal > 255)
2208
0
            {
2209
0
                CPLError(CE_Warning, CPLE_AppDefined,
2210
0
                         "Value %d of index %d in PDS should be in [0,255] "
2211
0
                         "range",
2212
0
                         nVal, i);
2213
0
            }
2214
0
            WriteByte(fp, nVal);
2215
0
        }
2216
0
        else if (nEltSize == 2)
2217
0
        {
2218
0
            if (nVal < 0 || nVal > 65535)
2219
0
            {
2220
0
                CPLError(CE_Warning, CPLE_AppDefined,
2221
0
                         "Value %d of index %d in PDS should be in [0,65535] "
2222
0
                         "range",
2223
0
                         nVal, i);
2224
0
            }
2225
0
            WriteUInt16(fp, nVal);
2226
0
        }
2227
0
        else if (nEltSize == 4)
2228
0
        {
2229
0
            GIntBig nBigVal = CPLAtoGIntBig(papszTokens[i]);
2230
0
            anVals.back() = static_cast<int>(nBigVal);
2231
0
            if (nBigVal < 0 || nBigVal > static_cast<GIntBig>(UINT_MAX))
2232
0
            {
2233
0
                CPLError(CE_Warning, CPLE_AppDefined,
2234
0
                         "Value " CPL_FRMT_GIB " of index %d in PDS should be "
2235
0
                         "in [0,%d] range",
2236
0
                         nBigVal, i, INT_MAX);
2237
0
            }
2238
0
            WriteUInt32(fp, static_cast<GUInt32>(nBigVal));
2239
0
        }
2240
0
        else if (nEltSize == -1)
2241
0
        {
2242
0
            if (nVal < -128 || nVal > 127)
2243
0
            {
2244
0
                CPLError(CE_Warning, CPLE_AppDefined,
2245
0
                         "Value %d of index %d in PDS should be in [-128,127] "
2246
0
                         "range",
2247
0
                         nVal, i);
2248
0
            }
2249
0
            WriteSByte(fp, nVal);
2250
0
        }
2251
0
        else if (nEltSize == -2)
2252
0
        {
2253
0
            if (nVal < -32768 || nVal > 32767)
2254
0
            {
2255
0
                CPLError(CE_Warning, CPLE_AppDefined,
2256
0
                         "Value %d of index %d in PDS should be in "
2257
0
                         "[-32768,32767] range",
2258
0
                         nVal, i);
2259
0
            }
2260
0
            WriteInt16(fp, nVal);
2261
0
        }
2262
0
        else if (nEltSize == -4)
2263
0
        {
2264
0
            GIntBig nBigVal = CPLAtoGIntBig(papszTokens[i]);
2265
0
            if (nBigVal < INT_MIN || nBigVal > INT_MAX)
2266
0
            {
2267
0
                CPLError(CE_Warning, CPLE_AppDefined,
2268
0
                         "Value " CPL_FRMT_GIB " of index %d in PDS should be "
2269
0
                         "in [%d,%d] range",
2270
0
                         nBigVal, i, INT_MIN, INT_MAX);
2271
0
            }
2272
0
            WriteInt32(fp, atoi(papszTokens[i]));
2273
0
        }
2274
0
        else
2275
0
        {
2276
0
            CPLAssert(false);
2277
0
        }
2278
0
    }
2279
0
}
2280
2281
/************************************************************************/
2282
/*                         ComputeValOffset()                           */
2283
/************************************************************************/
2284
2285
static float ComputeValOffset(int nTokens, char **papszTokens,
2286
                              const char *pszInputUnit)
2287
0
{
2288
0
    float fValOffset = 0.0f;
2289
2290
    // Parameter category 0 = Temperature
2291
0
    if (nTokens >= 2 && atoi(papszTokens[0]) == 0)
2292
0
    {
2293
        // Cf
2294
        // https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-2-0-0.shtml
2295
        // PARAMETERS FOR DISCIPLINE 0 CATEGORY 0
2296
0
        int nParamNumber = atoi(papszTokens[1]);
2297
0
        if ((nParamNumber >= 0 && nParamNumber <= 18 && nParamNumber != 8 &&
2298
0
             nParamNumber != 10 && nParamNumber != 11 && nParamNumber != 16) ||
2299
0
            nParamNumber == 21 || nParamNumber == 27)
2300
0
        {
2301
0
            if (pszInputUnit == nullptr || EQUAL(pszInputUnit, "C") ||
2302
0
                EQUAL(pszInputUnit, "[C]"))
2303
0
            {
2304
0
                fValOffset = 273.15f;
2305
0
                CPLDebug("GRIB",
2306
0
                         "Applying a %f offset to convert from "
2307
0
                         "Celsius to Kelvin",
2308
0
                         fValOffset);
2309
0
            }
2310
0
        }
2311
0
    }
2312
2313
0
    return fValOffset;
2314
0
}
2315
2316
/************************************************************************/
2317
/*                           WriteSection4()                            */
2318
/************************************************************************/
2319
2320
static bool WriteSection4(VSILFILE *fp, GDALDataset *poSrcDS, int nBand,
2321
                          char **papszOptions, float &fValOffset)
2322
28.9k
{
2323
    // Section 4: Product Definition Section
2324
28.9k
    vsi_l_offset nStartSection4 = VSIFTellL(fp);
2325
28.9k
    WriteUInt32(fp, GRIB2MISSING_u4);  // section size
2326
28.9k
    WriteByte(fp, 4);                  // section number
2327
28.9k
    WriteUInt16(fp, 0);  // Number of coordinate values after template
2328
2329
    // 0 = Analysis or forecast at a horizontal level or in a horizontal
2330
    // layer at a point in time
2331
28.9k
    int nPDTN =
2332
28.9k
        atoi(GetBandOption(papszOptions, poSrcDS, nBand, "PDS_PDTN", "0"));
2333
28.9k
    const char *pszPDSTemplateNumbers = GetBandOption(
2334
28.9k
        papszOptions, nullptr, nBand, "PDS_TEMPLATE_NUMBERS", nullptr);
2335
28.9k
    const char *pszPDSTemplateAssembledValues = GetBandOption(
2336
28.9k
        papszOptions, nullptr, nBand, "PDS_TEMPLATE_ASSEMBLED_VALUES", nullptr);
2337
28.9k
    if (pszPDSTemplateNumbers == nullptr &&
2338
28.9k
        pszPDSTemplateAssembledValues == nullptr)
2339
28.9k
    {
2340
28.9k
        pszPDSTemplateNumbers = GetBandOption(papszOptions, poSrcDS, nBand,
2341
28.9k
                                              "PDS_TEMPLATE_NUMBERS", nullptr);
2342
28.9k
    }
2343
28.9k
    CPLString osInputUnit;
2344
28.9k
    const char *pszInputUnit =
2345
28.9k
        GetBandOption(papszOptions, nullptr, nBand, "INPUT_UNIT", nullptr);
2346
28.9k
    if (pszInputUnit == nullptr)
2347
28.9k
    {
2348
28.9k
        const char *pszGribUnit =
2349
28.9k
            poSrcDS->GetRasterBand(nBand)->GetMetadataItem("GRIB_UNIT");
2350
28.9k
        if (pszGribUnit != nullptr)
2351
15.5k
        {
2352
15.5k
            osInputUnit = pszGribUnit;
2353
15.5k
            pszInputUnit = osInputUnit.c_str();
2354
15.5k
        }
2355
28.9k
    }
2356
28.9k
    WriteUInt16(fp, nPDTN);  // PDTN
2357
28.9k
    if (nPDTN == 0 && pszPDSTemplateNumbers == nullptr &&
2358
28.9k
        pszPDSTemplateAssembledValues == nullptr)
2359
28.9k
    {
2360
        // See http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_temp4-0.shtml
2361
28.9k
        WriteByte(fp, GRIB2MISSING_u1);  // Parameter category = Missing
2362
28.9k
        WriteByte(fp, GRIB2MISSING_u1);  // Parameter number = Missing
2363
28.9k
        WriteByte(fp, GRIB2MISSING_u1);  // Type of generating process = Missing
2364
28.9k
        WriteByte(fp, 0);  // Background generating process identifier
2365
        // Analysis or forecast generating process identified
2366
28.9k
        WriteByte(fp, GRIB2MISSING_u1);
2367
28.9k
        WriteUInt16(fp, 0);  // Hours
2368
28.9k
        WriteByte(fp, 0);    // Minutes
2369
28.9k
        WriteByte(fp, 0);    // Indicator of unit of time range: 0=Minute
2370
28.9k
        WriteUInt32(fp, 0);  // Forecast time in units
2371
28.9k
        WriteByte(fp, 0);    // Type of first fixed surface
2372
28.9k
        WriteByte(fp, 0);    // Scale factor of first fixed surface
2373
28.9k
        WriteUInt32(fp, 0);  // Type of second fixed surface
2374
28.9k
        WriteByte(fp, GRIB2MISSING_u1);  // Type of second fixed surface
2375
28.9k
        WriteByte(fp, GRIB2MISSING_u1);  // Scale factor of second fixed surface
2376
        // Scaled value of second fixed surface
2377
28.9k
        WriteUInt32(fp, GRIB2MISSING_u4);
2378
28.9k
    }
2379
0
    else if (pszPDSTemplateNumbers == nullptr &&
2380
0
             pszPDSTemplateAssembledValues == nullptr)
2381
0
    {
2382
0
        CPLError(CE_Failure, CPLE_NotSupported,
2383
0
                 "PDS_PDTN != 0 specified but both PDS_TEMPLATE_NUMBERS and "
2384
0
                 "PDS_TEMPLATE_ASSEMBLED_VALUES missing");
2385
0
        return false;
2386
0
    }
2387
0
    else if (pszPDSTemplateNumbers != nullptr &&
2388
0
             pszPDSTemplateAssembledValues != nullptr)
2389
0
    {
2390
0
        CPLError(CE_Failure, CPLE_NotSupported,
2391
0
                 "PDS_TEMPLATE_NUMBERS and "
2392
0
                 "PDS_TEMPLATE_ASSEMBLED_VALUES are exclusive");
2393
0
        return false;
2394
0
    }
2395
0
    else if (pszPDSTemplateNumbers != nullptr)
2396
0
    {
2397
0
        char **papszTokens = CSLTokenizeString2(pszPDSTemplateNumbers, " ", 0);
2398
0
        const int nTokens = CSLCount(papszTokens);
2399
2400
0
        fValOffset = ComputeValOffset(nTokens, papszTokens, pszInputUnit);
2401
2402
0
        for (int i = 0; papszTokens[i] != nullptr; i++)
2403
0
        {
2404
0
            int nVal = atoi(papszTokens[i]);
2405
0
            if (nVal < 0 || nVal > 255)
2406
0
            {
2407
0
                CPLError(CE_Warning, CPLE_AppDefined,
2408
0
                         "Value %d of index %d in PDS should be in [0,255] "
2409
0
                         "range",
2410
0
                         nVal, i);
2411
0
            }
2412
0
            WriteByte(fp, nVal);
2413
0
        }
2414
0
        CSLDestroy(papszTokens);
2415
2416
        // Read back section
2417
0
        PatchSectionSize(fp, nStartSection4);
2418
2419
0
        vsi_l_offset nCurOffset = VSIFTellL(fp);
2420
0
        VSIFSeekL(fp, nStartSection4, SEEK_SET);
2421
0
        size_t nSizeSect4 = static_cast<size_t>(nCurOffset - nStartSection4);
2422
0
        GByte *pabySect4 = static_cast<GByte *>(CPLMalloc(nSizeSect4));
2423
0
        VSIFReadL(pabySect4, 1, nSizeSect4, fp);
2424
0
        VSIFSeekL(fp, nCurOffset, SEEK_SET);
2425
2426
        // Check consistency with template definition
2427
0
        g2int iofst = 0;
2428
0
        g2int pdsnum = 0;
2429
0
        g2int *pdstempl = nullptr;
2430
0
        g2int mappdslen = 0;
2431
0
        g2float *coordlist = nullptr;
2432
0
        g2int numcoord = 0;
2433
0
        int ret =
2434
0
            g2_unpack4(pabySect4, static_cast<g2int>(nSizeSect4), &iofst,
2435
0
                       &pdsnum, &pdstempl, &mappdslen, &coordlist, &numcoord);
2436
0
        CPLFree(pabySect4);
2437
0
        if (ret == 0)
2438
0
        {
2439
0
            gtemplate *mappds = extpdstemplate(pdsnum, pdstempl);
2440
0
            free(pdstempl);
2441
0
            free(coordlist);
2442
0
            if (mappds)
2443
0
            {
2444
0
                int nTemplateByteCount = 0;
2445
0
                for (int i = 0; i < mappds->maplen; i++)
2446
0
                    nTemplateByteCount += abs(mappds->map[i]);
2447
0
                for (int i = 0; i < mappds->extlen; i++)
2448
0
                    nTemplateByteCount += abs(mappds->ext[i]);
2449
0
                if (nTokens < nTemplateByteCount)
2450
0
                {
2451
0
                    CPLError(CE_Failure, CPLE_AppDefined,
2452
0
                             "PDS_PDTN = %d (with provided elements) requires "
2453
0
                             "%d bytes in PDS_TEMPLATE_NUMBERS. "
2454
0
                             "Only %d provided",
2455
0
                             nPDTN, nTemplateByteCount, nTokens);
2456
0
                    free(mappds->ext);
2457
0
                    free(mappds);
2458
0
                    return false;
2459
0
                }
2460
0
                else if (nTokens > nTemplateByteCount)
2461
0
                {
2462
0
                    CPLError(CE_Warning, CPLE_AppDefined,
2463
0
                             "PDS_PDTN = %d (with provided elements) requires "
2464
0
                             "%d bytes in PDS_TEMPLATE_NUMBERS. "
2465
0
                             "But %d provided. Extra bytes will be ignored "
2466
0
                             "by readers",
2467
0
                             nPDTN, nTemplateByteCount, nTokens);
2468
0
                }
2469
2470
0
                free(mappds->ext);
2471
0
                free(mappds);
2472
0
            }
2473
0
        }
2474
0
        else
2475
0
        {
2476
0
            free(pdstempl);
2477
0
            free(coordlist);
2478
0
            CPLError(CE_Warning, CPLE_AppDefined,
2479
0
                     "PDS_PDTN = %d is unknown. Product will not be "
2480
0
                     "correctly read by this driver (but potentially valid "
2481
0
                     "for other readers)",
2482
0
                     nPDTN);
2483
0
        }
2484
0
    }
2485
0
    else
2486
0
    {
2487
0
        gtemplate *mappds = getpdstemplate(nPDTN);
2488
0
        if (mappds == nullptr)
2489
0
        {
2490
0
            CPLError(CE_Failure, CPLE_NotSupported,
2491
0
                     "PDS_PDTN = %d is unknown, so it is not possible to use "
2492
0
                     "PDS_TEMPLATE_ASSEMBLED_VALUES. Use PDS_TEMPLATE_NUMBERS "
2493
0
                     "instead",
2494
0
                     nPDTN);
2495
0
            return false;
2496
0
        }
2497
0
        char **papszTokens =
2498
0
            CSLTokenizeString2(pszPDSTemplateAssembledValues, " ", 0);
2499
0
        const int nTokens = CSLCount(papszTokens);
2500
0
        if (nTokens < mappds->maplen)
2501
0
        {
2502
0
            CPLError(CE_Failure, CPLE_AppDefined,
2503
0
                     "PDS_PDTN = %d requires at least %d elements in "
2504
0
                     "PDS_TEMPLATE_ASSEMBLED_VALUES. Only %d provided",
2505
0
                     nPDTN, mappds->maplen, nTokens);
2506
0
            free(mappds);
2507
0
            CSLDestroy(papszTokens);
2508
0
            return false;
2509
0
        }
2510
2511
0
        fValOffset = ComputeValOffset(nTokens, papszTokens, pszInputUnit);
2512
2513
0
        std::vector<int> anVals;
2514
0
        WriteAssembledPDS(fp, mappds, false, papszTokens, anVals);
2515
2516
0
        if (mappds->needext && !anVals.empty())
2517
0
        {
2518
0
            free(mappds);
2519
0
            mappds = extpdstemplate(nPDTN, &anVals[0]);
2520
0
            if (mappds == nullptr)
2521
0
            {
2522
0
                CPLError(CE_Failure, CPLE_AppDefined,
2523
0
                         "Could not get extended template definition");
2524
0
                CSLDestroy(papszTokens);
2525
0
                return false;
2526
0
            }
2527
0
            if (nTokens < mappds->maplen + mappds->extlen)
2528
0
            {
2529
0
                CPLError(CE_Failure, CPLE_AppDefined,
2530
0
                         "PDS_PDTN = %d (with provided elements) requires "
2531
0
                         "%d elements in PDS_TEMPLATE_ASSEMBLED_VALUES. "
2532
0
                         "Only %d provided",
2533
0
                         nPDTN, mappds->maplen + mappds->extlen, nTokens);
2534
0
                free(mappds->ext);
2535
0
                free(mappds);
2536
0
                CSLDestroy(papszTokens);
2537
0
                return false;
2538
0
            }
2539
0
            else if (nTokens > mappds->maplen + mappds->extlen)
2540
0
            {
2541
0
                CPLError(CE_Warning, CPLE_AppDefined,
2542
0
                         "PDS_PDTN = %d (with provided elements) requires"
2543
0
                         "%d elements in PDS_TEMPLATE_ASSEMBLED_VALUES. "
2544
0
                         "But %d provided. Extra elements will be ignored",
2545
0
                         nPDTN, mappds->maplen + mappds->extlen, nTokens);
2546
0
            }
2547
2548
0
            WriteAssembledPDS(fp, mappds, true, papszTokens, anVals);
2549
0
        }
2550
2551
0
        free(mappds->ext);
2552
0
        free(mappds);
2553
0
        CSLDestroy(papszTokens);
2554
0
    }
2555
28.9k
    PatchSectionSize(fp, nStartSection4);
2556
28.9k
    return true;
2557
28.9k
}
2558
2559
/************************************************************************/
2560
/*                             CreateCopy()                             */
2561
/************************************************************************/
2562
2563
GDALDataset *GRIBDataset::CreateCopy(const char *pszFilename,
2564
                                     GDALDataset *poSrcDS, int /* bStrict */,
2565
                                     char **papszOptions,
2566
                                     GDALProgressFunc pfnProgress,
2567
                                     void *pProgressData)
2568
2569
696
{
2570
696
    if (poSrcDS->GetRasterYSize() == 0 ||
2571
696
        poSrcDS->GetRasterXSize() > INT_MAX / poSrcDS->GetRasterXSize())
2572
0
    {
2573
0
        CPLError(CE_Failure, CPLE_NotSupported,
2574
0
                 "Cannot create GRIB2 rasters with more than 2 billion pixels");
2575
0
        return nullptr;
2576
0
    }
2577
2578
696
    GDALGeoTransform gt;
2579
696
    if (poSrcDS->GetGeoTransform(gt) != CE_None)
2580
64
    {
2581
64
        CPLError(CE_Failure, CPLE_NotSupported,
2582
64
                 "Source dataset must have a geotransform");
2583
64
        return nullptr;
2584
64
    }
2585
632
    if (gt[2] != 0.0 || gt[4] != 0.0)
2586
0
    {
2587
0
        CPLError(CE_Failure, CPLE_NotSupported,
2588
0
                 "Geotransform with rotation terms not supported");
2589
0
        return nullptr;
2590
0
    }
2591
2592
632
    OGRSpatialReference oSRS;
2593
632
    oSRS.importFromWkt(poSrcDS->GetProjectionRef());
2594
632
    if (oSRS.IsProjected())
2595
335
    {
2596
335
        const char *pszProjection = oSRS.GetAttrValue("PROJECTION");
2597
335
        if (pszProjection == nullptr ||
2598
335
            !(EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) ||
2599
335
              EQUAL(pszProjection, SRS_PT_MERCATOR_1SP) ||
2600
335
              EQUAL(pszProjection, SRS_PT_MERCATOR_2SP) ||
2601
335
              EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC) ||
2602
335
              EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ||
2603
335
              EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ||
2604
335
              EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA) ||
2605
335
              EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA)))
2606
2
        {
2607
2
            CPLError(CE_Failure, CPLE_NotSupported,
2608
2
                     "Unsupported projection: %s",
2609
2
                     pszProjection ? pszProjection : "");
2610
2
            return nullptr;
2611
2
        }
2612
335
    }
2613
297
    else if (!oSRS.IsGeographic())
2614
16
    {
2615
16
        CPLError(CE_Failure, CPLE_NotSupported,
2616
16
                 "Unsupported or missing spatial reference system");
2617
16
        return nullptr;
2618
16
    }
2619
2620
614
    const bool bAppendSubdataset = CPLTestBool(
2621
614
        CSLFetchNameValueDef(papszOptions, "APPEND_SUBDATASET", "NO"));
2622
614
    VSILFILE *fp = VSIFOpenL(pszFilename, bAppendSubdataset ? "rb+" : "wb+");
2623
614
    if (fp == nullptr)
2624
0
    {
2625
0
        CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
2626
0
        return nullptr;
2627
0
    }
2628
614
    VSIFSeekL(fp, 0, SEEK_END);
2629
2630
614
    vsi_l_offset nStartOffset = 0;
2631
614
    vsi_l_offset nTotalSizeOffset = 0;
2632
614
    int nSplitAndSwapColumn = 0;
2633
    // Note: WRITE_SUBGRIDS=YES should not be used blindly currently, as it
2634
    // does not check that the content of the DISCIPLINE and IDS are the same.
2635
    // A smarter behavior would be to break into separate messages if needed
2636
614
    const bool bWriteSubGrids =
2637
614
        CPLTestBool(CSLFetchNameValueDef(papszOptions, "WRITE_SUBGRIDS", "NO"));
2638
29.5k
    for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); nBand++)
2639
28.9k
    {
2640
28.9k
        if (nBand == 1 || !bWriteSubGrids)
2641
28.9k
        {
2642
            // Section 0: Indicator section
2643
28.9k
            nStartOffset = VSIFTellL(fp);
2644
28.9k
            VSIFWriteL("GRIB", 4, 1, fp);
2645
28.9k
            WriteByte(fp, 0);  // reserved
2646
28.9k
            WriteByte(fp, 0);  // reserved
2647
28.9k
            int nDiscipline =
2648
28.9k
                atoi(GetBandOption(papszOptions, poSrcDS, nBand, "DISCIPLINE",
2649
28.9k
                                   "0"));  // 0 = Meteorological
2650
28.9k
            WriteByte(fp, nDiscipline);    // discipline
2651
28.9k
            WriteByte(fp, 2);              // GRIB edition number
2652
28.9k
            nTotalSizeOffset = VSIFTellL(fp);
2653
28.9k
            WriteUInt32(fp, GRIB2MISSING_u4);  // dummy file size (high 32 bits)
2654
28.9k
            WriteUInt32(fp, GRIB2MISSING_u4);  // dummy file size (low 32 bits)
2655
2656
            // Section 1: Identification Section
2657
28.9k
            WriteSection1(fp, poSrcDS, nBand, papszOptions);
2658
2659
            // Section 2: Local use section
2660
28.9k
            WriteUInt32(fp, 5);  // section size
2661
28.9k
            WriteByte(fp, 2);    // section number
2662
2663
            // Section 3: Grid Definition Section
2664
28.9k
            GRIB2Section3Writer oSection3(fp, poSrcDS);
2665
28.9k
            if (!oSection3.Write())
2666
0
            {
2667
0
                VSIFCloseL(fp);
2668
0
                return nullptr;
2669
0
            }
2670
28.9k
            nSplitAndSwapColumn = oSection3.SplitAndSwap();
2671
28.9k
        }
2672
2673
        // Section 4: Product Definition Section
2674
28.9k
        float fValOffset = 0.0f;
2675
28.9k
        if (!WriteSection4(fp, poSrcDS, nBand, papszOptions, fValOffset))
2676
0
        {
2677
0
            VSIFCloseL(fp);
2678
0
            return nullptr;
2679
0
        }
2680
2681
        // Section 5, 6 and 7
2682
28.9k
        if (!GRIB2Section567Writer(fp, poSrcDS, nBand, nSplitAndSwapColumn)
2683
28.9k
                 .Write(fValOffset, papszOptions, pfnProgress, pProgressData))
2684
52
        {
2685
52
            VSIFCloseL(fp);
2686
52
            return nullptr;
2687
52
        }
2688
2689
28.9k
        if (nBand == poSrcDS->GetRasterCount() || !bWriteSubGrids)
2690
28.9k
        {
2691
            // Section 8: End section
2692
28.9k
            VSIFWriteL("7777", 4, 1, fp);
2693
2694
            // Patch total message size at end of section 0
2695
28.9k
            vsi_l_offset nCurOffset = VSIFTellL(fp);
2696
28.9k
            if (nCurOffset - nStartOffset > INT_MAX)
2697
0
            {
2698
0
                CPLError(CE_Failure, CPLE_NotSupported,
2699
0
                         "GRIB message larger than 2 GB");
2700
0
                VSIFCloseL(fp);
2701
0
                return nullptr;
2702
0
            }
2703
28.9k
            GUInt32 nTotalSize =
2704
28.9k
                static_cast<GUInt32>(nCurOffset - nStartOffset);
2705
28.9k
            VSIFSeekL(fp, nTotalSizeOffset, SEEK_SET);
2706
28.9k
            WriteUInt32(fp, 0);           // file size (high 32 bits)
2707
28.9k
            WriteUInt32(fp, nTotalSize);  // file size (low 32 bits)
2708
2709
28.9k
            VSIFSeekL(fp, nCurOffset, SEEK_SET);
2710
28.9k
        }
2711
2712
28.9k
        if (pfnProgress &&
2713
28.9k
            !pfnProgress(static_cast<double>(nBand) / poSrcDS->GetRasterCount(),
2714
28.9k
                         nullptr, pProgressData))
2715
0
        {
2716
0
            VSIFCloseL(fp);
2717
0
            return nullptr;
2718
0
        }
2719
28.9k
    }
2720
2721
562
    VSIFCloseL(fp);
2722
2723
562
    GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
2724
562
    return Open(&oOpenInfo);
2725
614
}