LCOV - code coverage report
Current view: top level - frmts/grib - gribcreatecopy.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 1159 1361 85.2 %
Date: 2026-02-27 12:28:37 Functions: 42 43 97.7 %

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

Generated by: LCOV version 1.14