Coverage Report

Created: 2025-12-03 08:24

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/gdal/frmts/grib/gribcreatecopy.cpp
Line
Count
Source
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
42
{
38
42
    if (lon == 180)
39
0
        return 180;
40
42
    return fmod(fmod(lon, 360) + 360, 360);
41
42
}
42
43
/************************************************************************/
44
/*                             WriteByte()                              */
45
/************************************************************************/
46
47
static bool WriteByte(VSILFILE *fp, int nVal)
48
55.3k
{
49
55.3k
    GByte byVal = static_cast<GByte>(nVal);
50
55.3k
    return VSIFWriteL(&byVal, 1, sizeof(byVal), fp) == sizeof(byVal);
51
55.3k
}
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
10.2k
{
74
10.2k
    GUInt16 usVal = static_cast<GUInt16>(nVal);
75
10.2k
    CPL_MSBPTR16(&usVal);
76
10.2k
    return VSIFWriteL(&usVal, 1, sizeof(usVal), fp) == sizeof(usVal);
77
10.2k
}
78
79
/************************************************************************/
80
/*                             WriteInt16()                             */
81
/************************************************************************/
82
83
static bool WriteInt16(VSILFILE *fp, int nVal)
84
60
{
85
60
    GInt16 sVal = static_cast<GInt16>(nVal);
86
60
    if (sVal == std::numeric_limits<GInt16>::min())
87
0
        sVal = std::numeric_limits<GInt16>::min() + 1;
88
60
    GUInt16 nUnsignedVal = (sVal < 0) ? static_cast<GUInt16>(-sVal) | 0x8000U
89
60
                                      : static_cast<GUInt16>(sVal);
90
60
    CPL_MSBPTR16(&nUnsignedVal);
91
60
    return VSIFWriteL(&nUnsignedVal, 1, sizeof(nUnsignedVal), fp) ==
92
60
           sizeof(nUnsignedVal);
93
60
}
94
95
/************************************************************************/
96
/*                             WriteUInt32()                            */
97
/************************************************************************/
98
99
static bool WriteUInt32(VSILFILE *fp, GUInt32 nVal)
100
32.1k
{
101
32.1k
    CPL_MSBPTR32(&nVal);
102
32.1k
    return VSIFWriteL(&nVal, 1, sizeof(nVal), fp) == sizeof(nVal);
103
32.1k
}
104
105
/************************************************************************/
106
/*                             WriteInt32()                             */
107
/************************************************************************/
108
109
static bool WriteInt32(VSILFILE *fp, GInt32 nVal)
110
7.84k
{
111
7.84k
    if (nVal == std::numeric_limits<GInt32>::min())
112
16
        nVal = std::numeric_limits<GInt32>::min() + 1;
113
7.84k
    GUInt32 nUnsignedVal = (nVal < 0)
114
7.84k
                               ? static_cast<GUInt32>(-nVal) | 0x80000000U
115
7.84k
                               : static_cast<GUInt32>(nVal);
116
7.84k
    CPL_MSBPTR32(&nUnsignedVal);
117
7.84k
    return VSIFWriteL(&nUnsignedVal, 1, sizeof(nUnsignedVal), fp) ==
118
7.84k
           sizeof(nUnsignedVal);
119
7.84k
}
120
121
/************************************************************************/
122
/*                            WriteFloat32()                            */
123
/************************************************************************/
124
125
static bool WriteFloat32(VSILFILE *fp, float fVal)
126
59
{
127
59
    CPL_MSBPTR32(&fVal);
128
59
    return VSIFWriteL(&fVal, 1, sizeof(fVal), fp) == sizeof(fVal);
129
59
}
130
131
/************************************************************************/
132
/*                         PatchSectionSize()                           */
133
/************************************************************************/
134
135
static void PatchSectionSize(VSILFILE *fp, vsi_l_offset nStartSection)
136
2.57k
{
137
2.57k
    vsi_l_offset nCurOffset = VSIFTellL(fp);
138
2.57k
    VSIFSeekL(fp, nStartSection, SEEK_SET);
139
2.57k
    GUInt32 nSect3Size = static_cast<GUInt32>(nCurOffset - nStartSection);
140
2.57k
    WriteUInt32(fp, nSect3Size);
141
2.57k
    VSIFSeekL(fp, nCurOffset, SEEK_SET);
142
2.57k
}
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
1.28k
    {
178
1.28k
        return nSplitAndSwapColumn;
179
1.28k
    }
180
181
    bool Write();
182
};
183
184
/************************************************************************/
185
/*                        GRIB2Section3Writer()                         */
186
/************************************************************************/
187
188
GRIB2Section3Writer::GRIB2Section3Writer(VSILFILE *fpIn, GDALDataset *poSrcDSIn)
189
1.28k
    : fp(fpIn), poSrcDS(poSrcDSIn)
190
1.28k
{
191
1.28k
    oSRS.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
192
1.28k
    oSRS.importFromWkt(poSrcDS->GetProjectionRef());
193
1.28k
    pszProjection = oSRS.GetAttrValue("PROJECTION");
194
195
1.28k
    poSrcDS->GetGeoTransform(m_gt);
196
197
1.28k
    dfLLX = m_gt[0] + m_gt[1] / 2;
198
1.28k
    dfLLY = m_gt[3] + m_gt[5] / 2 + (poSrcDS->GetRasterYSize() - 1) * m_gt[5];
199
1.28k
    dfURX = m_gt[0] + m_gt[1] / 2 + (poSrcDS->GetRasterXSize() - 1) * m_gt[1];
200
1.28k
    dfURY = m_gt[3] + m_gt[5] / 2;
201
1.28k
    if (dfURY < dfLLY)
202
0
    {
203
0
        double dfTemp = dfURY;
204
0
        dfURY = dfLLY;
205
0
        dfLLY = dfTemp;
206
0
    }
207
1.28k
}
208
209
/************************************************************************/
210
/*                     WriteEllipsoidAndRasterSize()                    */
211
/************************************************************************/
212
213
bool GRIB2Section3Writer::WriteEllipsoidAndRasterSize()
214
1.28k
{
215
1.28k
    const double dfSemiMajor = oSRS.GetSemiMajor();
216
1.28k
    const double dfSemiMinor = oSRS.GetSemiMinor();
217
1.28k
    const double dfInvFlattening = oSRS.GetInvFlattening();
218
1.28k
    if (std::abs(dfSemiMajor - 6378137.0) < 0.01 &&
219
1.24k
        std::abs(dfInvFlattening - 298.257223563) < 1e-9)  // WGS84
220
0
    {
221
0
        WriteByte(fp, 5);  // WGS84
222
0
        WriteByte(fp, GRIB2MISSING_u1);
223
0
        WriteUInt32(fp, GRIB2MISSING_u4);
224
0
        WriteByte(fp, GRIB2MISSING_u1);
225
0
        WriteUInt32(fp, GRIB2MISSING_u4);
226
0
        WriteByte(fp, GRIB2MISSING_u1);
227
0
        WriteUInt32(fp, GRIB2MISSING_u4);
228
0
    }
229
1.28k
    else if (std::abs(dfSemiMajor - 6378137.0) < 0.01 &&
230
1.24k
             std::abs(dfInvFlattening - 298.257222101) < 1e-9)  // GRS80
231
1.24k
    {
232
1.24k
        WriteByte(fp, 4);  // GRS80
233
1.24k
        WriteByte(fp, GRIB2MISSING_u1);
234
1.24k
        WriteUInt32(fp, GRIB2MISSING_u4);
235
1.24k
        WriteByte(fp, GRIB2MISSING_u1);
236
1.24k
        WriteUInt32(fp, GRIB2MISSING_u4);
237
1.24k
        WriteByte(fp, GRIB2MISSING_u1);
238
1.24k
        WriteUInt32(fp, GRIB2MISSING_u4);
239
1.24k
    }
240
42
    else if (dfInvFlattening == 0)
241
0
    {
242
        // Earth assumed spherical with radius specified (in m)
243
        // by data producer
244
0
        WriteByte(fp, 1);
245
0
        WriteByte(fp, 2);  // scale = * 100
246
0
        WriteUInt32(fp, static_cast<GUInt32>(dfSemiMajor * 100 + 0.5));
247
0
        WriteByte(fp, GRIB2MISSING_u1);
248
0
        WriteUInt32(fp, GRIB2MISSING_u4);
249
0
        WriteByte(fp, GRIB2MISSING_u1);
250
0
        WriteUInt32(fp, GRIB2MISSING_u4);
251
0
    }
252
42
    else
253
42
    {
254
        // Earth assumed oblate spheroid with major and minor axes
255
        // specified (in m) by data producer
256
42
        WriteByte(fp, 7);
257
42
        WriteByte(fp, GRIB2MISSING_u1);
258
42
        WriteUInt32(fp, GRIB2MISSING_u4);
259
42
        WriteByte(fp, 2);  // scale = * 100
260
42
        WriteUInt32(fp, static_cast<GUInt32>(dfSemiMajor * 100 + 0.5));
261
42
        WriteByte(fp, 2);  // scale = * 100
262
42
        WriteUInt32(fp, static_cast<GUInt32>(dfSemiMinor * 100 + 0.5));
263
42
    }
264
1.28k
    WriteUInt32(fp, poSrcDS->GetRasterXSize());
265
1.28k
    WriteUInt32(fp, poSrcDS->GetRasterYSize());
266
267
1.28k
    return true;
268
1.28k
}
269
270
/************************************************************************/
271
/*                            WriteScaled()                             */
272
/************************************************************************/
273
274
bool GRIB2Section3Writer::WriteScaled(double dfVal, double dfUnit)
275
7.83k
{
276
7.83k
    return WriteInt32(fp, static_cast<GInt32>(floor(dfVal / dfUnit + 0.5)));
277
7.83k
}
278
279
/************************************************************************/
280
/*                          WriteGeographic()                           */
281
/************************************************************************/
282
283
bool GRIB2Section3Writer::WriteGeographic()
284
1.24k
{
285
1.24k
    WriteUInt16(fp, GS3_LATLON);  // Grid template number
286
287
1.24k
    WriteEllipsoidAndRasterSize();
288
289
1.24k
    if (dfLLX < 0 &&
290
0
        CPLTestBool(CPLGetConfigOption("GRIB_ADJUST_LONGITUDE_RANGE", "YES")))
291
0
    {
292
0
        CPLDebug("GRIB", "Source longitude range is %lf to %lf", dfLLX, dfURX);
293
0
        double dfOrigLLX = dfLLX;
294
0
        dfLLX = Lon180to360(dfLLX);
295
0
        dfURX = Lon180to360(dfURX);
296
297
0
        if (dfLLX > dfURX)
298
0
        {
299
0
            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
0
            else
311
0
            {
312
0
                CPLDebug("GRIB", "Writing a GRIB with 0-360 longitudes "
313
0
                                 "crossing the prime meridian");
314
0
            }
315
0
        }
316
0
        CPLDebug("GRIB", "Target longitudes range is %lf %lf", dfLLX, dfURX);
317
0
    }
318
319
1.24k
    WriteUInt32(fp, 0);  // Basic angle. 0 equivalent of 1
320
    // Subdivisions of basic angle used. ~0 equivalent of 10^6
321
1.24k
    WriteUInt32(fp, GRIB2MISSING_u4);
322
1.24k
    const double dfAngUnit = 1e-6;
323
1.24k
    WriteScaled(dfLLY, dfAngUnit);
324
1.24k
    WriteScaled(dfLLX, dfAngUnit);
325
1.24k
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
326
1.24k
    WriteScaled(dfURY, dfAngUnit);
327
1.24k
    WriteScaled(dfURX, dfAngUnit);
328
1.24k
    WriteScaled(m_gt[1], dfAngUnit);
329
1.24k
    WriteScaled(fabs(m_gt[5]), dfAngUnit);
330
1.24k
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
331
332
1.24k
    return true;
333
1.24k
}
334
335
/************************************************************************/
336
/*                         WriteRotatedLatLon()                         */
337
/************************************************************************/
338
339
bool GRIB2Section3Writer::WriteRotatedLatLon(double dfLatSouthernPole,
340
                                             double dfLonSouthernPole,
341
                                             double dfAxisRotation)
342
1
{
343
1
    WriteUInt16(fp, GS3_ROTATED_LATLON);  // Grid template number
344
345
1
    WriteEllipsoidAndRasterSize();
346
347
1
    if (dfLLX < 0 &&
348
0
        CPLTestBool(CPLGetConfigOption("GRIB_ADJUST_LONGITUDE_RANGE", "YES")))
349
0
    {
350
0
        CPLDebug("GRIB", "Source longitude range is %lf to %lf", dfLLX, dfURX);
351
0
        double dfOrigLLX = dfLLX;
352
0
        dfLLX = Lon180to360(dfLLX);
353
0
        dfURX = Lon180to360(dfURX);
354
355
0
        if (dfLLX > dfURX)
356
0
        {
357
0
            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
0
            else
369
0
            {
370
0
                CPLDebug("GRIB", "Writing a GRIB with 0-360 longitudes "
371
0
                                 "crossing the prime meridian");
372
0
            }
373
0
        }
374
0
        CPLDebug("GRIB", "Target longitudes range is %lf %lf", dfLLX, dfURX);
375
0
    }
376
377
1
    WriteUInt32(fp, 0);  // Basic angle. 0 equivalent of 1
378
    // Subdivisions of basic angle used. ~0 equivalent of 10^6
379
1
    WriteUInt32(fp, GRIB2MISSING_u4);
380
1
    const double dfAngUnit = 1e-6;
381
1
    WriteScaled(dfLLY, dfAngUnit);
382
1
    WriteScaled(dfLLX, dfAngUnit);
383
1
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
384
1
    WriteScaled(dfURY, dfAngUnit);
385
1
    WriteScaled(dfURX, dfAngUnit);
386
1
    WriteScaled(m_gt[1], dfAngUnit);
387
1
    WriteScaled(fabs(m_gt[5]), dfAngUnit);
388
1
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
389
1
    WriteScaled(dfLatSouthernPole, dfAngUnit);
390
1
    WriteScaled(Lon180to360(dfLonSouthernPole), dfAngUnit);
391
1
    WriteScaled(dfAxisRotation, dfAngUnit);
392
393
1
    return true;
394
1
}
395
396
/************************************************************************/
397
/*                           TransformToGeo()                           */
398
/************************************************************************/
399
400
bool GRIB2Section3Writer::TransformToGeo(double &dfX, double &dfY)
401
12
{
402
12
    OGRSpatialReference oLL;  // Construct the "geographic" part of oSRS.
403
12
    oLL.CopyGeogCSFrom(&oSRS);
404
12
    oLL.SetAxisMappingStrategy(OAMS_TRADITIONAL_GIS_ORDER);
405
12
    OGRCoordinateTransformation *poTransformSRSToLL =
406
12
        OGRCreateCoordinateTransformation(&(oSRS), &(oLL));
407
12
    if (poTransformSRSToLL == nullptr ||
408
12
        !poTransformSRSToLL->Transform(1, &dfX, &dfY))
409
0
    {
410
0
        delete poTransformSRSToLL;
411
0
        return false;
412
0
    }
413
12
    delete poTransformSRSToLL;
414
12
    if (dfX < 0.0)
415
0
        dfX += 360.0;
416
12
    return true;
417
12
}
418
419
/************************************************************************/
420
/*                           WriteMercator1SP()                         */
421
/************************************************************************/
422
423
bool GRIB2Section3Writer::WriteMercator1SP()
424
0
{
425
0
    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
0
    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
0
    OGRSpatialReference *poMerc2SP =
439
0
        oSRS.convertToOtherProjection(SRS_PT_MERCATOR_2SP);
440
0
    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
0
    bool bRet = WriteMercator2SP(poMerc2SP);
448
0
    delete poMerc2SP;
449
0
    return bRet;
450
0
}
451
452
/************************************************************************/
453
/*                           WriteMercator2SP()                         */
454
/************************************************************************/
455
456
bool GRIB2Section3Writer::WriteMercator2SP(OGRSpatialReference *poSRS)
457
0
{
458
0
    if (poSRS == nullptr)
459
0
        poSRS = &oSRS;
460
461
0
    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
0
    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
0
    WriteUInt16(fp, GS3_MERCATOR);  // Grid template number
475
476
0
    WriteEllipsoidAndRasterSize();
477
478
0
    if (!TransformToGeo(dfLLX, dfLLY) || !TransformToGeo(dfURX, dfURY))
479
0
        return false;
480
481
0
    const double dfAngUnit = 1e-6;
482
0
    WriteScaled(dfLLY, dfAngUnit);
483
0
    WriteScaled(dfLLX, dfAngUnit);
484
0
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
485
0
    WriteScaled(poSRS->GetNormProjParm(SRS_PP_STANDARD_PARALLEL_1, 0.0),
486
0
                dfAngUnit);
487
0
    WriteScaled(dfURY, dfAngUnit);
488
0
    WriteScaled(dfURX, dfAngUnit);
489
0
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
490
0
    WriteInt32(fp, 0);          // angle of the grid
491
0
    const double dfLinearUnit = 1e-3;
492
0
    WriteScaled(m_gt[1], dfLinearUnit);
493
0
    WriteScaled(fabs(m_gt[5]), dfLinearUnit);
494
495
0
    return true;
496
0
}
497
498
/************************************************************************/
499
/*                      WriteTransverseMercator()                       */
500
/************************************************************************/
501
502
bool GRIB2Section3Writer::WriteTransverseMercator()
503
29
{
504
29
    WriteUInt16(fp, GS3_TRANSVERSE_MERCATOR);  // Grid template number
505
29
    WriteEllipsoidAndRasterSize();
506
507
29
    const double dfAngUnit = 1e-6;
508
29
    WriteScaled(oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0),
509
29
                dfAngUnit);
510
29
    WriteScaled(Lon180to360(oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)),
511
29
                dfAngUnit);
512
29
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
513
29
    float fScale =
514
29
        static_cast<float>(oSRS.GetNormProjParm(SRS_PP_SCALE_FACTOR, 0.0));
515
29
    WriteFloat32(fp, fScale);
516
29
    const double dfLinearUnit = 1e-2;
517
29
    WriteScaled(oSRS.GetNormProjParm(SRS_PP_FALSE_EASTING, 0.0), dfLinearUnit);
518
29
    WriteScaled(oSRS.GetNormProjParm(SRS_PP_FALSE_NORTHING, 0.0), dfLinearUnit);
519
29
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
520
29
    WriteScaled(m_gt[1], dfLinearUnit);
521
29
    WriteScaled(fabs(m_gt[5]), dfLinearUnit);
522
29
    WriteScaled(dfLLX, dfLinearUnit);
523
29
    WriteScaled(dfLLY, dfLinearUnit);
524
29
    WriteScaled(dfURX, dfLinearUnit);
525
29
    WriteScaled(dfURY, dfLinearUnit);
526
527
29
    return true;
528
29
}
529
530
/************************************************************************/
531
/*                       WritePolarStereographic()                       */
532
/************************************************************************/
533
534
bool GRIB2Section3Writer::WritePolarStereographic()
535
12
{
536
12
    WriteUInt16(fp, GS3_POLAR);  // Grid template number
537
12
    WriteEllipsoidAndRasterSize();
538
539
12
    if (!TransformToGeo(dfLLX, dfLLY))
540
0
        return false;
541
542
12
    const double dfAngUnit = 1e-6;
543
12
    WriteScaled(dfLLY, dfAngUnit);
544
12
    WriteScaled(dfLLX, dfAngUnit);
545
12
    WriteByte(fp, GRIB2BIT_3 | GRIB2BIT_4);  // Resolution and component flags
546
12
    const double dfLatOrigin =
547
12
        oSRS.GetNormProjParm(SRS_PP_LATITUDE_OF_ORIGIN, 0.0);
548
12
    WriteScaled(dfLatOrigin, dfAngUnit);
549
12
    WriteScaled(Lon180to360(oSRS.GetNormProjParm(SRS_PP_CENTRAL_MERIDIAN, 0.0)),
550
12
                dfAngUnit);
551
12
    const double dfLinearUnit = 1e-3;
552
12
    WriteScaled(m_gt[1], dfLinearUnit);
553
12
    WriteScaled(fabs(m_gt[5]), dfLinearUnit);
554
    // Projection center flag: BIT1=0 North Pole, BIT1=1 South Pole
555
12
    WriteByte(fp, (dfLatOrigin < 0) ? GRIB2BIT_1 : 0);
556
12
    WriteByte(fp, GRIB2BIT_2);  // Scanning mode: bottom-to-top
557
558
12
    return true;
559
12
}
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
1.28k
{
666
    // Section 3: Grid Definition Section
667
1.28k
    vsi_l_offset nStartSection = VSIFTellL(fp);
668
669
1.28k
    WriteUInt32(fp, GRIB2MISSING_u4);  // section size
670
671
1.28k
    WriteByte(fp, 3);  // section number
672
673
    // Source of grid definition = Specified in Code Table 3.1
674
1.28k
    WriteByte(fp, 0);
675
676
1.28k
    const GUInt32 nDataPoints =
677
1.28k
        static_cast<GUInt32>(poSrcDS->GetRasterXSize()) *
678
1.28k
        poSrcDS->GetRasterYSize();
679
1.28k
    WriteUInt32(fp, nDataPoints);
680
681
    // Number of octets for optional list of numbers defining number of points
682
1.28k
    WriteByte(fp, 0);
683
684
    // Interpretation of list of numbers defining number of points =
685
    // No appended list
686
1.28k
    WriteByte(fp, 0);
687
688
1.28k
    bool bRet = false;
689
1.28k
    if (oSRS.IsGeographic())
690
1.24k
    {
691
1.24k
        if (oSRS.IsDerivedGeographic())
692
1
        {
693
1
            const OGR_SRSNode *poConversion =
694
1
                oSRS.GetAttrNode("DERIVINGCONVERSION");
695
1
            const char *pszMethod = oSRS.GetAttrValue("METHOD");
696
1
            if (!pszMethod)
697
0
                pszMethod = "unknown";
698
699
1
            std::map<std::string, double> oValMap;
700
1
            if (poConversion)
701
1
            {
702
6
                for (int iChild = 0; iChild < poConversion->GetChildCount();
703
5
                     iChild++)
704
5
                {
705
5
                    const OGR_SRSNode *poNode = poConversion->GetChild(iChild);
706
5
                    if (!EQUAL(poNode->GetValue(), "PARAMETER") ||
707
3
                        poNode->GetChildCount() <= 2)
708
2
                        continue;
709
3
                    const char *pszParamStr = poNode->GetChild(0)->GetValue();
710
3
                    const char *pszParamVal = poNode->GetChild(1)->GetValue();
711
3
                    oValMap[pszParamStr] = CPLAtof(pszParamVal);
712
3
                }
713
1
            }
714
715
1
            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
1
            else if (poConversion &&
728
1
                     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
1
            else if (poConversion &&
748
1
                     EQUAL(pszMethod, "Pole rotation (GRIB convention)"))
749
1
            {
750
1
                const double dfLatSouthernPole =
751
1
                    oValMap["Latitude of the southern pole (GRIB convention)"];
752
1
                const double dfLonSouthernPole =
753
1
                    oValMap["Longitude of the southern pole (GRIB convention)"];
754
1
                const double dfAxisRotation =
755
1
                    oValMap["Axis rotation (GRIB convention)"];
756
757
1
                bRet = WriteRotatedLatLon(dfLatSouthernPole, dfLonSouthernPole,
758
1
                                          dfAxisRotation);
759
1
            }
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
1
        }
768
1.24k
        else
769
1.24k
        {
770
1.24k
            bRet = WriteGeographic();
771
1.24k
        }
772
1.24k
    }
773
41
    else if (pszProjection && EQUAL(pszProjection, SRS_PT_MERCATOR_1SP))
774
0
    {
775
0
        bRet = WriteMercator1SP();
776
0
    }
777
41
    else if (pszProjection && EQUAL(pszProjection, SRS_PT_MERCATOR_2SP))
778
0
    {
779
0
        bRet = WriteMercator2SP();
780
0
    }
781
41
    else if (pszProjection && EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR))
782
29
    {
783
29
        bRet = WriteTransverseMercator();
784
29
    }
785
12
    else if (pszProjection && EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC))
786
12
    {
787
12
        bRet = WritePolarStereographic();
788
12
    }
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
1.28k
    PatchSectionSize(fp, nStartSection);
807
808
1.28k
    return bRet;
809
1.28k
}
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
32.1k
{
819
32.1k
    const char *pszVal = CSLFetchNameValue(
820
32.1k
        papszOptions, CPLSPrintf("BAND_%d_%s", nBand, pszKey));
821
32.1k
    if (pszVal == nullptr)
822
32.1k
    {
823
32.1k
        pszVal = CSLFetchNameValue(papszOptions, pszKey);
824
32.1k
    }
825
32.1k
    if (pszVal == nullptr && poSrcDS != nullptr)
826
12.8k
    {
827
12.8k
        pszVal = poSrcDS->GetRasterBand(nBand)->GetMetadataItem(
828
12.8k
            (CPLString("GRIB_") + pszKey).c_str());
829
12.8k
    }
830
32.1k
    if (pszVal == nullptr)
831
32.1k
    {
832
32.1k
        pszVal = pszDefault;
833
32.1k
    }
834
32.1k
    return pszVal;
835
32.1k
}
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
1.28k
    : m_fp(fp), m_poSrcDS(poSrcDS), m_nBand(nBand),
886
1.28k
      m_nXSize(poSrcDS->GetRasterXSize()), m_nYSize(poSrcDS->GetRasterYSize()),
887
1.28k
      m_nDataPoints(static_cast<GUInt32>(m_nXSize) * m_nYSize),
888
1.28k
      m_eDT(m_poSrcDS->GetRasterBand(m_nBand)->GetRasterDataType()),
889
1.28k
      m_nDecimalScaleFactor(0), m_dfDecimalScale(1.0), m_fMin(0.0f),
890
1.28k
      m_fMax(0.0f), m_dfMinScaled(0.0), m_nBits(0), m_bUseZeroBits(false),
891
1.28k
      m_fValOffset(0.0), m_bHasNoData(false), m_dfNoData(0.0),
892
1.28k
      m_nSplitAndSwap(nSplitAndSwap)
893
1.28k
{
894
1.28k
    m_poSrcDS->GetGeoTransform(m_gt);
895
1.28k
    m_dfNoData = m_poSrcDS->GetRasterBand(nBand)->GetNoDataValue(&m_bHasNoData);
896
1.28k
}
897
898
/************************************************************************/
899
/*                          GetFloatData()                              */
900
/************************************************************************/
901
902
float *GRIB2Section567Writer::GetFloatData()
903
31
{
904
31
    float *pafData =
905
31
        static_cast<float *>(VSI_MALLOC2_VERBOSE(m_nDataPoints, sizeof(float)));
906
31
    if (pafData == nullptr)
907
0
    {
908
0
        return nullptr;
909
0
    }
910
31
    CPLErr eErr = m_poSrcDS->GetRasterBand(m_nBand)->RasterIO(
911
31
        GF_Read, m_nSplitAndSwap, 0, m_nXSize - m_nSplitAndSwap, m_nYSize,
912
31
        pafData + (m_gt[5] < 0 ? (m_nYSize - 1) * m_nXSize : 0),
913
31
        m_nXSize - m_nSplitAndSwap, m_nYSize, GDT_Float32, sizeof(float),
914
31
        m_gt[5] < 0 ? -static_cast<GSpacing>(m_nXSize * sizeof(float))
915
31
                    : static_cast<GSpacing>(m_nXSize * sizeof(float)),
916
31
        nullptr);
917
31
    if (eErr != CE_None)
918
0
    {
919
0
        VSIFree(pafData);
920
0
        return nullptr;
921
0
    }
922
31
    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
31
    m_fMin = std::numeric_limits<float>::max();
940
31
    m_fMax = -std::numeric_limits<float>::max();
941
31
    bool bHasNoDataValuePoint = false;
942
31
    bool bHasDataValuePoint = false;
943
59.6k
    for (GUInt32 i = 0; i < m_nDataPoints; i++)
944
59.6k
    {
945
59.6k
        if (m_bHasNoData && pafData[i] == static_cast<float>(m_dfNoData))
946
406
        {
947
406
            if (!bHasNoDataValuePoint)
948
10
                bHasNoDataValuePoint = true;
949
406
            continue;
950
406
        }
951
59.2k
        if (!std::isfinite(pafData[i]))
952
1
        {
953
1
            CPLError(CE_Failure, CPLE_NotSupported,
954
1
                     "Non-finite values not supported for "
955
1
                     "this data encoding");
956
1
            VSIFree(pafData);
957
1
            return nullptr;
958
1
        }
959
59.2k
        if (!bHasDataValuePoint)
960
30
            bHasDataValuePoint = true;
961
59.2k
        pafData[i] += m_fValOffset;
962
59.2k
        if (pafData[i] < m_fMin)
963
68
            m_fMin = pafData[i];
964
59.2k
        if (pafData[i] > m_fMax)
965
173
            m_fMax = pafData[i];
966
59.2k
    }
967
30
    if (m_fMin > m_fMax)
968
0
    {
969
0
        m_fMin = m_fMax = static_cast<float>(m_dfNoData);
970
0
    }
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
30
    if (m_fMax > m_fMin && GDALDataTypeIsInteger(m_eDT) &&
978
29
        ceil(log(m_fMax - m_fMin) / log(2.0)) > GDALGetDataTypeSizeBits(m_eDT))
979
0
    {
980
0
        CPLError(CE_Failure, CPLE_AppDefined,
981
0
                 "Garbage values found when requesting input dataset");
982
0
        VSIFree(pafData);
983
0
        return nullptr;
984
0
    }
985
986
30
    m_dfMinScaled =
987
30
        m_dfDecimalScale == 1.0 ? m_fMin : floor(m_fMin * m_dfDecimalScale);
988
30
    if (!(m_dfMinScaled >= -std::numeric_limits<float>::max() &&
989
30
          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
30
    const double dfScaledMaxDiff = (m_fMax - m_fMin) * m_dfDecimalScale;
999
30
    if (GDALDataTypeIsFloating(m_eDT) && m_nBits == 0 && dfScaledMaxDiff > 0 &&
1000
0
        dfScaledMaxDiff <= 256)
1001
0
    {
1002
0
        m_nBits = 8;
1003
0
    }
1004
1005
30
    m_bUseZeroBits =
1006
30
        (m_fMin == m_fMax && !(bHasDataValuePoint && bHasNoDataValuePoint)) ||
1007
29
        (!GDALDataTypeIsFloating(m_eDT) && dfScaledMaxDiff < 1.0);
1008
1009
30
    return pafData;
1010
30
}
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
20
{
1019
20
    float *pafData = GetFloatData();
1020
20
    if (pafData == nullptr)
1021
0
        return false;
1022
1023
20
    const int nBitCorrectionForDec =
1024
20
        static_cast<int>(ceil(m_nDecimalScaleFactor * log(10.0) / log(2.0)));
1025
20
    const int nMaxBitsPerElt = std::max(
1026
20
        1, std::min(31, (m_nBits > 0) ? m_nBits
1027
20
                                      : GDALGetDataTypeSizeBits(m_eDT) +
1028
20
                                            nBitCorrectionForDec));
1029
20
    if (nMaxBitsPerElt > 0 &&
1030
20
        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
20
    const int nMaxSize = (m_nDataPoints * nMaxBitsPerElt + 7) / 8;
1039
20
    void *pabyData = VSI_MALLOC_VERBOSE(nMaxSize);
1040
20
    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
20
    enum
1049
20
    {
1050
20
        TMPL5_R_IDX = 0,      // Reference value (R)
1051
20
        TMPL5_E_IDX = 1,      // Binary scale factor (E)
1052
20
        TMPL5_D_IDX = 2,      // Decimal scale factor (D)
1053
20
        TMPL5_NBITS_IDX = 3,  // Number of bits used for each packed value
1054
20
        TMPL5_TYPE_IDX = 4    // type of original data
1055
20
    };
1056
1057
20
    g2int idrstmpl[TMPL5_TYPE_IDX + 1] = {0};
1058
20
    idrstmpl[TMPL5_R_IDX] = 0;  // reference value, to be filled by simpack
1059
20
    idrstmpl[TMPL5_E_IDX] = 0;  // binary scale factor, to be filled by simpack
1060
20
    idrstmpl[TMPL5_D_IDX] = m_nDecimalScaleFactor;
1061
    // to be filled by simpack if set to 0
1062
20
    idrstmpl[TMPL5_NBITS_IDX] = m_nBits;
1063
    // to be filled by simpack (and we will ignore it)
1064
20
    idrstmpl[TMPL5_TYPE_IDX] = 0;
1065
20
    g2int nLengthPacked = 0;
1066
20
    simpack(pafData, m_nDataPoints, idrstmpl,
1067
20
            static_cast<unsigned char *>(pabyData), &nLengthPacked);
1068
20
    CPLAssert(nLengthPacked <= nMaxSize);
1069
20
    if (nLengthPacked < 0)
1070
0
    {
1071
0
        CPLError(CE_Failure, CPLE_AppDefined, "Error while packing");
1072
0
        VSIFree(pafData);
1073
0
        VSIFree(pabyData);
1074
0
        return false;
1075
0
    }
1076
1077
    // Section 5: Data Representation Section
1078
20
    WriteUInt32(m_fp, 21);  // section size
1079
20
    WriteByte(m_fp, 5);     // section number
1080
20
    WriteUInt32(m_fp, m_nDataPoints);
1081
20
    WriteUInt16(m_fp, GS5_SIMPLE);
1082
20
    float fRefValue;
1083
20
    memcpy(&fRefValue, &idrstmpl[TMPL5_R_IDX], 4);
1084
20
    WriteFloat32(m_fp, fRefValue);
1085
20
    WriteInt16(m_fp, idrstmpl[TMPL5_E_IDX]);
1086
20
    WriteInt16(m_fp, idrstmpl[TMPL5_D_IDX]);
1087
20
    WriteByte(m_fp, idrstmpl[TMPL5_NBITS_IDX]);
1088
    // Type of original data: 0=Floating, 1=Integer
1089
20
    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
20
    {
1116
20
        WriteUInt32(m_fp, 6);              // section size
1117
20
        WriteByte(m_fp, 6);                // section number
1118
20
        WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1119
20
    }
1120
1121
    // Section 7: Data Section
1122
20
    WriteUInt32(m_fp, 5 + nLengthPacked);  // section size
1123
20
    WriteByte(m_fp, 7);                    // section number
1124
20
    if (static_cast<int>(VSIFWriteL(pabyData, 1, nLengthPacked, m_fp)) !=
1125
20
        nLengthPacked)
1126
0
    {
1127
0
        VSIFree(pafData);
1128
0
        VSIFree(pabyData);
1129
0
        return false;
1130
0
    }
1131
1132
20
    VSIFree(pafData);
1133
20
    VSIFree(pabyData);
1134
1135
20
    return true;
1136
20
}
1137
1138
/************************************************************************/
1139
/*                      WriteComplexPackingNoData()                     */
1140
/************************************************************************/
1141
1142
void GRIB2Section567Writer::WriteComplexPackingNoData()
1143
10
{
1144
10
    if (!m_bHasNoData)
1145
0
    {
1146
0
        WriteUInt32(m_fp, GRIB2MISSING_u4);
1147
0
    }
1148
10
    else if (GDALDataTypeIsFloating(m_eDT))
1149
0
    {
1150
0
        WriteFloat32(m_fp, static_cast<float>(m_dfNoData));
1151
0
    }
1152
10
    else
1153
10
    {
1154
10
        if (GDALIsValueInRange<int>(m_dfNoData))
1155
10
        {
1156
10
            WriteInt32(m_fp, static_cast<int>(m_dfNoData));
1157
10
        }
1158
0
        else
1159
0
        {
1160
0
            WriteUInt32(m_fp, GRIB2MISSING_u4);
1161
0
        }
1162
10
    }
1163
10
}
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
11
{
1174
11
    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
11
    float *pafData = GetFloatData();
1182
11
    if (pafData == nullptr)
1183
1
        return false;
1184
1185
10
    const float fNoData = static_cast<float>(m_dfNoData);
1186
10
    if (m_bUseZeroBits)
1187
0
    {
1188
        // Case where all values are at nodata or a single value
1189
0
        VSIFree(pafData);
1190
1191
        // Section 5: Data Representation Section
1192
0
        WriteUInt32(m_fp, 47);  // section size
1193
0
        WriteByte(m_fp, 5);     // section number
1194
0
        WriteUInt32(m_fp, m_nDataPoints);
1195
0
        WriteUInt16(m_fp, GS5_CMPLX);
1196
0
        WriteFloat32(m_fp, m_fMin);  // ref value = nodata or single data
1197
0
        WriteInt16(m_fp, 0);         // binary scale factor
1198
0
        WriteInt16(m_fp, 0);         // decimal scale factor
1199
0
        WriteByte(m_fp, 0);          // number of bits
1200
        // Type of original data: 0=Floating, 1=Integer
1201
0
        WriteByte(m_fp, GDALDataTypeIsFloating(m_eDT) ? 0 : 1);
1202
0
        WriteByte(m_fp, 0);
1203
0
        WriteByte(m_fp, m_bHasNoData ? 1 : 0);  // 1 missing value
1204
0
        WriteComplexPackingNoData();
1205
0
        WriteUInt32(m_fp, GRIB2MISSING_u4);
1206
0
        WriteUInt32(m_fp, 0);
1207
0
        WriteByte(m_fp, 0);
1208
0
        WriteByte(m_fp, 0);
1209
0
        WriteUInt32(m_fp, 0);
1210
0
        WriteByte(m_fp, 0);
1211
0
        WriteUInt32(m_fp, 0);
1212
0
        WriteByte(m_fp, 0);
1213
1214
        // Section 6: Bitmap section
1215
0
        WriteUInt32(m_fp, 6);              // section size
1216
0
        WriteByte(m_fp, 6);                // section number
1217
0
        WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1218
1219
        // Section 7: Data Section
1220
0
        WriteUInt32(m_fp, 5);  // section size
1221
0
        WriteByte(m_fp, 7);    // section number
1222
1223
0
        return true;
1224
0
    }
1225
1226
10
    const int nBitCorrectionForDec =
1227
10
        static_cast<int>(ceil(m_nDecimalScaleFactor * log(10.0) / log(2.0)));
1228
10
    const int nMaxBitsPerElt = std::max(
1229
10
        1, std::min(31, (m_nBits > 0) ? m_nBits
1230
10
                                      : GDALGetDataTypeSizeBits(m_eDT) +
1231
10
                                            nBitCorrectionForDec));
1232
10
    if (nMaxBitsPerElt > 0 &&
1233
10
        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
10
    const int nMaxSize = 10000 + 2 * ((m_nDataPoints * nMaxBitsPerElt + 7) / 8);
1244
10
    void *pabyData = VSI_MALLOC_VERBOSE(nMaxSize);
1245
10
    if (pabyData == nullptr)
1246
0
    {
1247
0
        VSIFree(pafData);
1248
0
        VSIFree(pabyData);
1249
0
        return false;
1250
0
    }
1251
1252
10
    const double dfScaledMaxDiff =
1253
10
        (m_fMax == m_fMin) ? 1 : (m_fMax - m_fMin) * m_dfDecimalScale;
1254
10
    if (m_nBits == 0)
1255
10
    {
1256
10
        double dfTemp = log(ceil(dfScaledMaxDiff)) / log(2.0);
1257
10
        m_nBits = std::max(1, std::min(31, static_cast<int>(ceil(dfTemp))));
1258
10
    }
1259
10
    const int nMaxNum = (m_nBits == 31) ? INT_MAX : ((1 << m_nBits) - 1);
1260
10
    double dfTemp = log(nMaxNum / dfScaledMaxDiff) / log(2.0);
1261
10
    int nBinaryScaleFactor = static_cast<GInt16>(ceil(-dfTemp));
1262
1263
    // Indices expected by cmplxpack()
1264
10
    enum
1265
10
    {
1266
10
        TMPL5_R_IDX = 0,                        // reference value
1267
10
        TMPL5_E_IDX = 1,                        // binary scale factor
1268
10
        TMPL5_D_IDX = 2,                        // decimal scale factor
1269
10
        TMPL5_NBITS_IDX = 3,                    // number of bits
1270
10
        TMPL5_TYPE_IDX = 4,                     // type of original data
1271
10
        TMPL5_GROUP_SPLITTING_IDX = 5,          // Group splitting method used
1272
10
        TMPL5_MISSING_VALUE_MGNT_IDX = 6,       // Missing value management used
1273
10
        TMPL5_PRIMARY_MISSING_VALUE_IDX = 7,    // Primary missing value
1274
10
        TMPL5_SECONDARY_MISSING_VALUE_IDX = 8,  // Secondary missing value
1275
10
        TMPL5_NG_IDX = 9,                 // number of groups of data values
1276
10
        TMPL5_REF_GROUP_WIDTHS_IDX = 10,  // Reference for group widths
1277
        // Number of bits used for the group widths
1278
10
        TMPL5_NBITS_GROUP_WIDTHS_IDX = 11,
1279
10
        TMPL5_REF_GROUP_LENGTHS_IDX = 12,  // Reference for group lengths
1280
        // Length increment for the group lengths
1281
10
        TMPL5_LENGTH_INCR_GROUP_LENGTHS_IDX = 13,
1282
10
        TMPL5_TRUE_LENGTH_LAST_GROUP_IDX = 14,  // True length of last group
1283
        // Number of bits used for the scaled group lengths
1284
10
        TMPL5_NBITS_SCALED_GROUP_LENGTHS_IDX = 15,
1285
        // Order of spatial differencing
1286
10
        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
10
        TMPL5_NB_OCTETS_EXTRA_DESCR_IDX = 17
1290
10
    };
1291
1292
10
    g2int idrstmpl[TMPL5_NB_OCTETS_EXTRA_DESCR_IDX + 1] = {0};
1293
10
    idrstmpl[TMPL5_E_IDX] = nBinaryScaleFactor;
1294
10
    idrstmpl[TMPL5_D_IDX] = m_nDecimalScaleFactor;
1295
10
    idrstmpl[TMPL5_MISSING_VALUE_MGNT_IDX] = m_bHasNoData ? 1 : 0;
1296
10
    idrstmpl[TMPL5_ORDER_SPATIAL_DIFFERENCE_IDX] = nSpatialDifferencingOrder;
1297
10
    if (m_bHasNoData)
1298
10
    {
1299
10
        memcpy(&idrstmpl[TMPL5_PRIMARY_MISSING_VALUE_IDX], &fNoData, 4);
1300
10
    }
1301
10
    g2int nLengthPacked = 0;
1302
10
    const int nTemplateNumber =
1303
10
        (nSpatialDifferencingOrder > 0) ? GS5_CMPLXSEC : GS5_CMPLX;
1304
10
    cmplxpack(pafData, m_nDataPoints, nTemplateNumber, idrstmpl,
1305
10
              static_cast<unsigned char *>(pabyData), &nLengthPacked);
1306
10
    CPLAssert(nLengthPacked <= nMaxSize);
1307
10
    if (nLengthPacked < 0)
1308
0
    {
1309
0
        CPLError(CE_Failure, CPLE_AppDefined, "Error while packing");
1310
0
        VSIFree(pafData);
1311
0
        VSIFree(pabyData);
1312
0
        return false;
1313
0
    }
1314
1315
    // Section 5: Data Representation Section
1316
10
    WriteUInt32(m_fp, nTemplateNumber == GS5_CMPLX ? 47 : 49);  // section size
1317
10
    WriteByte(m_fp, 5);  // section number
1318
10
    WriteUInt32(m_fp, m_nDataPoints);
1319
10
    WriteUInt16(m_fp, nTemplateNumber);
1320
10
    float fRefValue;
1321
10
    memcpy(&fRefValue, &idrstmpl[TMPL5_R_IDX], 4);
1322
10
    WriteFloat32(m_fp, fRefValue);
1323
10
    WriteInt16(m_fp, idrstmpl[TMPL5_E_IDX]);
1324
10
    WriteInt16(m_fp, idrstmpl[TMPL5_D_IDX]);
1325
10
    WriteByte(m_fp, idrstmpl[TMPL5_NBITS_IDX]);
1326
    // Type of original data: 0=Floating, 1=Integer
1327
10
    WriteByte(m_fp, GDALDataTypeIsFloating(m_eDT) ? 0 : 1);
1328
10
    WriteByte(m_fp, idrstmpl[TMPL5_GROUP_SPLITTING_IDX]);
1329
10
    WriteByte(m_fp, idrstmpl[TMPL5_MISSING_VALUE_MGNT_IDX]);
1330
10
    WriteComplexPackingNoData();
1331
10
    WriteUInt32(m_fp, GRIB2MISSING_u4);
1332
10
    WriteUInt32(m_fp, idrstmpl[TMPL5_NG_IDX]);
1333
10
    WriteByte(m_fp, idrstmpl[TMPL5_REF_GROUP_WIDTHS_IDX]);
1334
10
    WriteByte(m_fp, idrstmpl[TMPL5_NBITS_GROUP_WIDTHS_IDX]);
1335
10
    WriteUInt32(m_fp, idrstmpl[TMPL5_REF_GROUP_LENGTHS_IDX]);
1336
10
    WriteByte(m_fp, idrstmpl[TMPL5_LENGTH_INCR_GROUP_LENGTHS_IDX]);
1337
10
    WriteUInt32(m_fp, idrstmpl[TMPL5_TRUE_LENGTH_LAST_GROUP_IDX]);
1338
10
    WriteByte(m_fp, idrstmpl[TMPL5_NBITS_SCALED_GROUP_LENGTHS_IDX]);
1339
10
    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
10
    WriteUInt32(m_fp, 6);              // section size
1347
10
    WriteByte(m_fp, 6);                // section number
1348
10
    WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1349
1350
    // Section 7: Data Section
1351
10
    WriteUInt32(m_fp, 5 + nLengthPacked);  // section size
1352
10
    WriteByte(m_fp, 7);                    // section number
1353
10
    if (static_cast<int>(VSIFWriteL(pabyData, 1, nLengthPacked, m_fp)) !=
1354
10
        nLengthPacked)
1355
0
    {
1356
0
        VSIFree(pafData);
1357
0
        VSIFree(pabyData);
1358
0
        return false;
1359
0
    }
1360
1361
10
    VSIFree(pafData);
1362
10
    VSIFree(pabyData);
1363
1364
10
    return true;
1365
10
}
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
1.25k
{
1375
1.25k
    GDALDataType eReqDT;
1376
1.25k
    if (GDALGetDataTypeSizeBytes(m_eDT) <= 2 || m_eDT == GDT_Float32)
1377
1.24k
        eReqDT = GDT_Float32;
1378
11
    else
1379
11
        eReqDT = GDT_Float64;
1380
1381
    // Section 5: Data Representation Section
1382
1.25k
    WriteUInt32(m_fp, 12);  // section size
1383
1.25k
    WriteByte(m_fp, 5);     // section number
1384
1.25k
    WriteUInt32(m_fp, m_nDataPoints);
1385
1.25k
    WriteUInt16(m_fp, GS5_IEEE);
1386
1.25k
    WriteByte(m_fp, (eReqDT == GDT_Float32) ? 1 : 2);  // Precision
1387
1388
    // Section 6: Bitmap section
1389
1.25k
    WriteUInt32(m_fp, 6);              // section size
1390
1.25k
    WriteByte(m_fp, 6);                // section number
1391
1.25k
    WriteByte(m_fp, GRIB2MISSING_u1);  // no bitmap
1392
1393
    // Section 7: Data Section
1394
1.25k
    const size_t nBufferSize =
1395
1.25k
        static_cast<size_t>(m_nXSize) * GDALGetDataTypeSizeBytes(eReqDT);
1396
    // section size
1397
1.25k
    WriteUInt32(m_fp, static_cast<GUInt32>(5 + nBufferSize * m_nYSize));
1398
1.25k
    WriteByte(m_fp, 7);  // section number
1399
1.25k
    void *pData = CPLMalloc(nBufferSize);
1400
1.25k
    const int nDenominator = std::max(1, m_poSrcDS->GetRasterCount());
1401
1.25k
    void *pScaledProgressData = GDALCreateScaledProgress(
1402
1.25k
        static_cast<double>(m_nBand - 1) / nDenominator,
1403
1.25k
        static_cast<double>(m_nBand) / nDenominator, pfnProgress,
1404
1.25k
        pProgressData);
1405
11.2k
    for (int i = 0; i < m_nYSize; i++)
1406
10.0k
    {
1407
10.0k
        int iSrcLine = m_gt[5] < 0 ? m_nYSize - 1 - i : i;
1408
10.0k
        CPLErr eErr = m_poSrcDS->GetRasterBand(m_nBand)->RasterIO(
1409
10.0k
            GF_Read, m_nSplitAndSwap, iSrcLine, m_nXSize - m_nSplitAndSwap, 1,
1410
10.0k
            pData, m_nXSize - m_nSplitAndSwap, 1, eReqDT, 0, 0, nullptr);
1411
10.0k
        if (eErr != CE_None)
1412
0
        {
1413
0
            CPLFree(pData);
1414
0
            GDALDestroyScaledProgress(pScaledProgressData);
1415
0
            return false;
1416
0
        }
1417
10.0k
        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
10.0k
        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
10.0k
#ifdef CPL_LSB
1450
10.0k
        GDALSwapWords(pData, GDALGetDataTypeSizeBytes(eReqDT), m_nXSize,
1451
10.0k
                      GDALGetDataTypeSizeBytes(eReqDT));
1452
10.0k
#endif
1453
10.0k
        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
10.0k
        if (!GDALScaledProgress(static_cast<double>(i + 1) / m_nYSize, nullptr,
1460
10.0k
                                pScaledProgressData))
1461
0
        {
1462
0
            CPLFree(pData);
1463
0
            GDALDestroyScaledProgress(pScaledProgressData);
1464
0
            return false;
1465
0
        }
1466
10.0k
    }
1467
1.25k
    GDALDestroyScaledProgress(pScaledProgressData);
1468
1.25k
    CPLFree(pData);
1469
1470
1.25k
    return true;
1471
1.25k
}
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_UInt8 || 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_UInt8)
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_UInt8 : 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_UInt8 : 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
1.28k
{
1889
1.28k
    m_fValOffset = fValOffset;
1890
1891
1.28k
    typedef enum
1892
1.28k
    {
1893
1.28k
        SIMPLE_PACKING,
1894
1.28k
        COMPLEX_PACKING,
1895
1.28k
        IEEE_FLOATING_POINT,
1896
1.28k
        PNG,
1897
1.28k
        JPEG2000
1898
1.28k
    } GRIBDataEncoding;
1899
1900
1.28k
    if (m_eDT != GDT_UInt8 && m_eDT != GDT_UInt16 && m_eDT != GDT_Int16 &&
1901
1.25k
        m_eDT != GDT_UInt32 && m_eDT != GDT_Int32 && m_eDT != GDT_Float32 &&
1902
12
        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
1.28k
    const char *pszDataEncoding =
1909
1.28k
        GetBandOption(papszOptions, nullptr, m_nBand, "DATA_ENCODING", "AUTO");
1910
1.28k
    GRIBDataEncoding eDataEncoding(SIMPLE_PACKING);
1911
1.28k
    const char *pszJ2KDriver = GetBandOption(papszOptions, nullptr, m_nBand,
1912
1.28k
                                             "JPEG2000_DRIVER", nullptr);
1913
1.28k
    const char *pszSpatialDifferencingOrder = GetBandOption(
1914
1.28k
        papszOptions, nullptr, m_nBand, "SPATIAL_DIFFERENCING_ORDER", nullptr);
1915
1.28k
    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
1.28k
    if (m_bHasNoData && !EQUAL(pszDataEncoding, "COMPLEX_PACKING") &&
1924
1.25k
        pszSpatialDifferencingOrder == nullptr)
1925
1.25k
    {
1926
1.25k
        double *padfVals = static_cast<double *>(
1927
1.25k
            VSI_MALLOC2_VERBOSE(m_nXSize, sizeof(double)));
1928
1.25k
        if (padfVals == nullptr)
1929
0
            return false;
1930
1.25k
        bool bFoundNoData = false;
1931
11.2k
        for (int j = 0; j < m_nYSize; j++)
1932
9.97k
        {
1933
9.97k
            CPLErr eErr = m_poSrcDS->GetRasterBand(m_nBand)->RasterIO(
1934
9.97k
                GF_Read, 0, j, m_nXSize, 1, padfVals, m_nXSize, 1, GDT_Float64,
1935
9.97k
                0, 0, nullptr);
1936
9.97k
            if (eErr != CE_None)
1937
0
            {
1938
0
                VSIFree(padfVals);
1939
0
                return false;
1940
0
            }
1941
70.2k
            for (int i = 0; i < m_nXSize; i++)
1942
60.2k
            {
1943
60.2k
                if (padfVals[i] == m_dfNoData)
1944
11
                {
1945
11
                    bFoundNoData = true;
1946
11
                    break;
1947
11
                }
1948
60.2k
            }
1949
9.97k
            if (bFoundNoData)
1950
11
                break;
1951
9.97k
        }
1952
1.25k
        VSIFree(padfVals);
1953
1954
1.25k
        if (!bFoundNoData)
1955
1.24k
        {
1956
1.24k
            m_bHasNoData = false;
1957
1.24k
        }
1958
1.25k
    }
1959
1960
1.28k
    if (EQUAL(pszDataEncoding, "AUTO"))
1961
1.28k
    {
1962
1.28k
        if (m_bHasNoData || pszSpatialDifferencingOrder != nullptr)
1963
11
        {
1964
11
            eDataEncoding = COMPLEX_PACKING;
1965
11
            CPLDebug("GRIB", "Using COMPLEX_PACKING");
1966
11
        }
1967
1.27k
        else if (pszJ2KDriver != nullptr)
1968
0
        {
1969
0
            eDataEncoding = JPEG2000;
1970
0
            CPLDebug("GRIB", "Using JPEG2000");
1971
0
        }
1972
1.27k
        else if (m_eDT == GDT_Float32 || m_eDT == GDT_Float64)
1973
1.25k
        {
1974
1.25k
            eDataEncoding = IEEE_FLOATING_POINT;
1975
1.25k
            CPLDebug("GRIB", "Using IEEE_FLOATING_POINT");
1976
1.25k
        }
1977
20
        else
1978
20
        {
1979
20
            CPLDebug("GRIB", "Using SIMPLE_PACKING");
1980
20
        }
1981
1.28k
    }
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
1.28k
    const char *pszBits =
2010
1.28k
        GetBandOption(papszOptions, nullptr, m_nBand, "NBITS", nullptr);
2011
1.28k
    if (pszBits == nullptr && eDataEncoding != IEEE_FLOATING_POINT)
2012
31
    {
2013
31
        pszBits = m_poSrcDS->GetRasterBand(m_nBand)->GetMetadataItem(
2014
31
            "DRS_NBITS", "GRIB");
2015
31
    }
2016
1.25k
    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
1.28k
    if (pszBits == nullptr)
2022
1.28k
    {
2023
1.28k
        pszBits = "0";
2024
1.28k
    }
2025
1.28k
    m_nBits = std::max(0, atoi(pszBits));
2026
1.28k
    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
1.28k
    const char *pszDecimalScaleFactor = GetBandOption(
2033
1.28k
        papszOptions, nullptr, m_nBand, "DECIMAL_SCALE_FACTOR", nullptr);
2034
1.28k
    if (pszDecimalScaleFactor != nullptr)
2035
0
    {
2036
0
        m_nDecimalScaleFactor = atoi(pszDecimalScaleFactor);
2037
0
        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
0
        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
0
    }
2051
1.28k
    else if (eDataEncoding != IEEE_FLOATING_POINT)
2052
31
    {
2053
31
        pszDecimalScaleFactor =
2054
31
            m_poSrcDS->GetRasterBand(m_nBand)->GetMetadataItem(
2055
31
                "DRS_DECIMAL_SCALE_FACTOR", "GRIB");
2056
31
        if (pszDecimalScaleFactor != nullptr)
2057
0
        {
2058
0
            m_nDecimalScaleFactor = atoi(pszDecimalScaleFactor);
2059
0
        }
2060
31
    }
2061
1.28k
    m_dfDecimalScale = pow(10.0, static_cast<double>(m_nDecimalScaleFactor));
2062
2063
1.28k
    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
1.28k
    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
1.28k
    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
1.28k
    if (eDataEncoding == SIMPLE_PACKING)
2082
20
    {
2083
20
        return WriteSimplePacking();
2084
20
    }
2085
1.26k
    else if (eDataEncoding == COMPLEX_PACKING)
2086
11
    {
2087
11
        const int nSpatialDifferencingOrder =
2088
11
            pszSpatialDifferencingOrder ? atoi(pszSpatialDifferencingOrder) : 0;
2089
11
        return WriteComplexPacking(nSpatialDifferencingOrder);
2090
11
    }
2091
1.25k
    else if (eDataEncoding == IEEE_FLOATING_POINT)
2092
1.25k
    {
2093
1.25k
        return WriteIEEE(pfnProgress, pProgressData);
2094
1.25k
    }
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
1.28k
}
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
9.00k
{
2113
9.00k
    const char *pszValue =
2114
9.00k
        GetBandOption(papszOptions, nullptr, nBand,
2115
9.00k
                      (CPLString("IDS_") + pszKey).c_str(), nullptr);
2116
9.00k
    if (pszValue == nullptr)
2117
9.00k
    {
2118
9.00k
        const char *pszIDS =
2119
9.00k
            GetBandOption(papszOptions, poSrcDS, nBand, "IDS", nullptr);
2120
9.00k
        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
9.00k
    }
2129
9.00k
    if (pszValue == nullptr)
2130
9.00k
        pszValue = pszDefault;
2131
9.00k
    return pszValue;
2132
9.00k
}
2133
2134
/************************************************************************/
2135
/*                           WriteSection1()                            */
2136
/************************************************************************/
2137
2138
static void WriteSection1(VSILFILE *fp, GDALDataset *poSrcDS, int nBand,
2139
                          char **papszOptions)
2140
1.28k
{
2141
    // Section 1: Identification Section
2142
1.28k
    WriteUInt32(fp, 21);  // section size
2143
1.28k
    WriteByte(fp, 1);     // section number
2144
2145
1.28k
    GUInt16 nCenter = static_cast<GUInt16>(
2146
1.28k
        atoi(GetIDSOption(papszOptions, poSrcDS, nBand, "CENTER",
2147
1.28k
                          CPLSPrintf("%d", GRIB2MISSING_u1))));
2148
1.28k
    WriteUInt16(fp, nCenter);
2149
2150
1.28k
    GUInt16 nSubCenter = static_cast<GUInt16>(
2151
1.28k
        atoi(GetIDSOption(papszOptions, poSrcDS, nBand, "SUBCENTER",
2152
1.28k
                          CPLSPrintf("%d", GRIB2MISSING_u2))));
2153
1.28k
    WriteUInt16(fp, nSubCenter);
2154
2155
1.28k
    GByte nMasterTable = static_cast<GByte>(
2156
1.28k
        atoi(GetIDSOption(papszOptions, poSrcDS, nBand, "MASTER_TABLE", "2")));
2157
1.28k
    WriteByte(fp, nMasterTable);
2158
2159
1.28k
    WriteByte(fp, 0);  // local table
2160
2161
1.28k
    GByte nSignfRefTime = static_cast<GByte>(atoi(
2162
1.28k
        GetIDSOption(papszOptions, poSrcDS, nBand, "SIGNF_REF_TIME", "0")));
2163
1.28k
    WriteByte(fp, nSignfRefTime);  // Significance of reference time
2164
2165
1.28k
    const char *pszRefTime =
2166
1.28k
        GetIDSOption(papszOptions, poSrcDS, nBand, "REF_TIME", "");
2167
1.28k
    int nYear = 1970, nMonth = 1, nDay = 1, nHour = 0, nMinute = 0, nSecond = 0;
2168
1.28k
    sscanf(pszRefTime, "%04d-%02d-%02dT%02d:%02d:%02dZ", &nYear, &nMonth, &nDay,
2169
1.28k
           &nHour, &nMinute, &nSecond);
2170
1.28k
    WriteUInt16(fp, nYear);
2171
1.28k
    WriteByte(fp, nMonth);
2172
1.28k
    WriteByte(fp, nDay);
2173
1.28k
    WriteByte(fp, nHour);
2174
1.28k
    WriteByte(fp, nMinute);
2175
1.28k
    WriteByte(fp, nSecond);
2176
2177
1.28k
    GByte nProdStatus = static_cast<GByte>(
2178
1.28k
        atoi(GetIDSOption(papszOptions, poSrcDS, nBand, "PROD_STATUS",
2179
1.28k
                          CPLSPrintf("%d", GRIB2MISSING_u1))));
2180
1.28k
    WriteByte(fp, nProdStatus);
2181
2182
1.28k
    GByte nType = static_cast<GByte>(
2183
1.28k
        atoi(GetIDSOption(papszOptions, poSrcDS, nBand, "TYPE",
2184
1.28k
                          CPLSPrintf("%d", GRIB2MISSING_u1))));
2185
1.28k
    WriteByte(fp, nType);
2186
1.28k
}
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
1.28k
{
2323
    // Section 4: Product Definition Section
2324
1.28k
    vsi_l_offset nStartSection4 = VSIFTellL(fp);
2325
1.28k
    WriteUInt32(fp, GRIB2MISSING_u4);  // section size
2326
1.28k
    WriteByte(fp, 4);                  // section number
2327
1.28k
    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
1.28k
    int nPDTN =
2332
1.28k
        atoi(GetBandOption(papszOptions, poSrcDS, nBand, "PDS_PDTN", "0"));
2333
1.28k
    const char *pszPDSTemplateNumbers = GetBandOption(
2334
1.28k
        papszOptions, nullptr, nBand, "PDS_TEMPLATE_NUMBERS", nullptr);
2335
1.28k
    const char *pszPDSTemplateAssembledValues = GetBandOption(
2336
1.28k
        papszOptions, nullptr, nBand, "PDS_TEMPLATE_ASSEMBLED_VALUES", nullptr);
2337
1.28k
    if (pszPDSTemplateNumbers == nullptr &&
2338
1.28k
        pszPDSTemplateAssembledValues == nullptr)
2339
1.28k
    {
2340
1.28k
        pszPDSTemplateNumbers = GetBandOption(papszOptions, poSrcDS, nBand,
2341
1.28k
                                              "PDS_TEMPLATE_NUMBERS", nullptr);
2342
1.28k
    }
2343
1.28k
    CPLString osInputUnit;
2344
1.28k
    const char *pszInputUnit =
2345
1.28k
        GetBandOption(papszOptions, nullptr, nBand, "INPUT_UNIT", nullptr);
2346
1.28k
    if (pszInputUnit == nullptr)
2347
1.28k
    {
2348
1.28k
        const char *pszGribUnit =
2349
1.28k
            poSrcDS->GetRasterBand(nBand)->GetMetadataItem("GRIB_UNIT");
2350
1.28k
        if (pszGribUnit != nullptr)
2351
12
        {
2352
12
            osInputUnit = pszGribUnit;
2353
12
            pszInputUnit = osInputUnit.c_str();
2354
12
        }
2355
1.28k
    }
2356
1.28k
    WriteUInt16(fp, nPDTN);  // PDTN
2357
1.28k
    if (nPDTN == 0 && pszPDSTemplateNumbers == nullptr &&
2358
1.28k
        pszPDSTemplateAssembledValues == nullptr)
2359
1.28k
    {
2360
        // See http://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_temp4-0.shtml
2361
1.28k
        WriteByte(fp, GRIB2MISSING_u1);  // Parameter category = Missing
2362
1.28k
        WriteByte(fp, GRIB2MISSING_u1);  // Parameter number = Missing
2363
1.28k
        WriteByte(fp, GRIB2MISSING_u1);  // Type of generating process = Missing
2364
1.28k
        WriteByte(fp, 0);  // Background generating process identifier
2365
        // Analysis or forecast generating process identified
2366
1.28k
        WriteByte(fp, GRIB2MISSING_u1);
2367
1.28k
        WriteUInt16(fp, 0);  // Hours
2368
1.28k
        WriteByte(fp, 0);    // Minutes
2369
1.28k
        WriteByte(fp, 0);    // Indicator of unit of time range: 0=Minute
2370
1.28k
        WriteUInt32(fp, 0);  // Forecast time in units
2371
1.28k
        WriteByte(fp, 0);    // Type of first fixed surface
2372
1.28k
        WriteByte(fp, 0);    // Scale factor of first fixed surface
2373
1.28k
        WriteUInt32(fp, 0);  // Type of second fixed surface
2374
1.28k
        WriteByte(fp, GRIB2MISSING_u1);  // Type of second fixed surface
2375
1.28k
        WriteByte(fp, GRIB2MISSING_u1);  // Scale factor of second fixed surface
2376
        // Scaled value of second fixed surface
2377
1.28k
        WriteUInt32(fp, GRIB2MISSING_u4);
2378
1.28k
    }
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
1.28k
    PatchSectionSize(fp, nStartSection4);
2556
1.28k
    return true;
2557
1.28k
}
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
18
{
2570
18
    if (poSrcDS->GetRasterYSize() == 0 ||
2571
18
        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
18
    GDALGeoTransform gt;
2579
18
    if (poSrcDS->GetGeoTransform(gt) != CE_None)
2580
4
    {
2581
4
        CPLError(CE_Failure, CPLE_NotSupported,
2582
4
                 "Source dataset must have a geotransform");
2583
4
        return nullptr;
2584
4
    }
2585
14
    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
14
    OGRSpatialReference oSRS;
2593
14
    oSRS.importFromWkt(poSrcDS->GetProjectionRef());
2594
14
    if (oSRS.IsProjected())
2595
6
    {
2596
6
        const char *pszProjection = oSRS.GetAttrValue("PROJECTION");
2597
6
        if (pszProjection == nullptr ||
2598
6
            !(EQUAL(pszProjection, SRS_PT_TRANSVERSE_MERCATOR) ||
2599
1
              EQUAL(pszProjection, SRS_PT_MERCATOR_1SP) ||
2600
1
              EQUAL(pszProjection, SRS_PT_MERCATOR_2SP) ||
2601
1
              EQUAL(pszProjection, SRS_PT_POLAR_STEREOGRAPHIC) ||
2602
0
              EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_1SP) ||
2603
0
              EQUAL(pszProjection, SRS_PT_LAMBERT_CONFORMAL_CONIC_2SP) ||
2604
0
              EQUAL(pszProjection, SRS_PT_ALBERS_CONIC_EQUAL_AREA) ||
2605
0
              EQUAL(pszProjection, SRS_PT_LAMBERT_AZIMUTHAL_EQUAL_AREA)))
2606
0
        {
2607
0
            CPLError(CE_Failure, CPLE_NotSupported,
2608
0
                     "Unsupported projection: %s",
2609
0
                     pszProjection ? pszProjection : "");
2610
0
            return nullptr;
2611
0
        }
2612
6
    }
2613
8
    else if (!oSRS.IsGeographic())
2614
0
    {
2615
0
        CPLError(CE_Failure, CPLE_NotSupported,
2616
0
                 "Unsupported or missing spatial reference system");
2617
0
        return nullptr;
2618
0
    }
2619
2620
14
    const bool bAppendSubdataset = CPLTestBool(
2621
14
        CSLFetchNameValueDef(papszOptions, "APPEND_SUBDATASET", "NO"));
2622
14
    VSILFILE *fp = VSIFOpenL(pszFilename, bAppendSubdataset ? "rb+" : "wb+");
2623
14
    if (fp == nullptr)
2624
0
    {
2625
0
        CPLError(CE_Failure, CPLE_FileIO, "Cannot create %s", pszFilename);
2626
0
        return nullptr;
2627
0
    }
2628
14
    VSIFSeekL(fp, 0, SEEK_END);
2629
2630
14
    vsi_l_offset nStartOffset = 0;
2631
14
    vsi_l_offset nTotalSizeOffset = 0;
2632
14
    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
14
    const bool bWriteSubGrids =
2637
14
        CPLTestBool(CSLFetchNameValueDef(papszOptions, "WRITE_SUBGRIDS", "NO"));
2638
1.29k
    for (int nBand = 1; nBand <= poSrcDS->GetRasterCount(); nBand++)
2639
1.28k
    {
2640
1.28k
        if (nBand == 1 || !bWriteSubGrids)
2641
1.28k
        {
2642
            // Section 0: Indicator section
2643
1.28k
            nStartOffset = VSIFTellL(fp);
2644
1.28k
            VSIFWriteL("GRIB", 4, 1, fp);
2645
1.28k
            WriteByte(fp, 0);  // reserved
2646
1.28k
            WriteByte(fp, 0);  // reserved
2647
1.28k
            int nDiscipline =
2648
1.28k
                atoi(GetBandOption(papszOptions, poSrcDS, nBand, "DISCIPLINE",
2649
1.28k
                                   "0"));  // 0 = Meteorological
2650
1.28k
            WriteByte(fp, nDiscipline);    // discipline
2651
1.28k
            WriteByte(fp, 2);              // GRIB edition number
2652
1.28k
            nTotalSizeOffset = VSIFTellL(fp);
2653
1.28k
            WriteUInt32(fp, GRIB2MISSING_u4);  // dummy file size (high 32 bits)
2654
1.28k
            WriteUInt32(fp, GRIB2MISSING_u4);  // dummy file size (low 32 bits)
2655
2656
            // Section 1: Identification Section
2657
1.28k
            WriteSection1(fp, poSrcDS, nBand, papszOptions);
2658
2659
            // Section 2: Local use section
2660
1.28k
            WriteUInt32(fp, 5);  // section size
2661
1.28k
            WriteByte(fp, 2);    // section number
2662
2663
            // Section 3: Grid Definition Section
2664
1.28k
            GRIB2Section3Writer oSection3(fp, poSrcDS);
2665
1.28k
            if (!oSection3.Write())
2666
0
            {
2667
0
                VSIFCloseL(fp);
2668
0
                return nullptr;
2669
0
            }
2670
1.28k
            nSplitAndSwapColumn = oSection3.SplitAndSwap();
2671
1.28k
        }
2672
2673
        // Section 4: Product Definition Section
2674
1.28k
        float fValOffset = 0.0f;
2675
1.28k
        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
1.28k
        if (!GRIB2Section567Writer(fp, poSrcDS, nBand, nSplitAndSwapColumn)
2683
1.28k
                 .Write(fValOffset, papszOptions, pfnProgress, pProgressData))
2684
1
        {
2685
1
            VSIFCloseL(fp);
2686
1
            return nullptr;
2687
1
        }
2688
2689
1.28k
        if (nBand == poSrcDS->GetRasterCount() || !bWriteSubGrids)
2690
1.28k
        {
2691
            // Section 8: End section
2692
1.28k
            VSIFWriteL("7777", 4, 1, fp);
2693
2694
            // Patch total message size at end of section 0
2695
1.28k
            vsi_l_offset nCurOffset = VSIFTellL(fp);
2696
1.28k
            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
1.28k
            GUInt32 nTotalSize =
2704
1.28k
                static_cast<GUInt32>(nCurOffset - nStartOffset);
2705
1.28k
            VSIFSeekL(fp, nTotalSizeOffset, SEEK_SET);
2706
1.28k
            WriteUInt32(fp, 0);           // file size (high 32 bits)
2707
1.28k
            WriteUInt32(fp, nTotalSize);  // file size (low 32 bits)
2708
2709
1.28k
            VSIFSeekL(fp, nCurOffset, SEEK_SET);
2710
1.28k
        }
2711
2712
1.28k
        if (pfnProgress &&
2713
1.28k
            !pfnProgress(static_cast<double>(nBand) / poSrcDS->GetRasterCount(),
2714
1.28k
                         nullptr, pProgressData))
2715
0
        {
2716
0
            VSIFCloseL(fp);
2717
0
            return nullptr;
2718
0
        }
2719
1.28k
    }
2720
2721
13
    VSIFCloseL(fp);
2722
2723
13
    GDALOpenInfo oOpenInfo(pszFilename, GA_ReadOnly);
2724
13
    return Open(&oOpenInfo);
2725
14
}