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