Line data Source code
1 : /*****************************************************************************
2 : * metaname.c
3 : *
4 : * DESCRIPTION
5 : * This file contains the code necessary to parse the GRIB2 product
6 : * definition information into human readable text. In addition to the
7 : * tables in the GRIB2 specs, it also attempts to handle local table
8 : * definitions that NCEP and NDFD have developed.
9 : *
10 : * HISTORY
11 : * 1/2004 Arthur Taylor (MDL / RSIS): Created.
12 : *
13 : * NOTES
14 : *****************************************************************************
15 : */
16 : #include <string.h>
17 : #include <stdlib.h>
18 : #include <limits>
19 : #include "meta.h"
20 : #include "metaname.h"
21 : #include "myerror.h"
22 : #include "myassert.h"
23 : #include "myutil.h"
24 :
25 : #include "cpl_port.h"
26 : #include "cpl_csv.h"
27 :
28 2373 : static const char* GetGRIB2_CSVFilename(const char* pszFilename)
29 : {
30 2373 : const char* pszGribTableDirectory = CPLGetConfigOption("GRIB_RESOURCE_DIR", nullptr);
31 2373 : if( pszGribTableDirectory )
32 : {
33 0 : const char* pszFullFilename = CPLFormFilename(pszGribTableDirectory, pszFilename, nullptr);
34 : VSIStatBufL sStat;
35 0 : if( VSIStatL(pszFullFilename, &sStat) == 0 )
36 0 : return pszFullFilename;
37 0 : return nullptr;
38 : }
39 2373 : const char* pszRet = CSVFilename(pszFilename);
40 : // CSVFilename() returns the same content as pszFilename if it does not
41 : // find the file.
42 2373 : if( pszRet && strcmp(pszRet, pszFilename) == 0 )
43 513 : return nullptr;
44 1860 : return pszRet;
45 : }
46 :
47 86 : const char *centerLookup (unsigned short int center)
48 : {
49 86 : const char* pszFilename = GetGRIB2_CSVFilename("grib2_center.csv");
50 86 : if( pszFilename == nullptr )
51 : {
52 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find grib2_center.csv");
53 0 : return nullptr;
54 : }
55 86 : const char* pszName = CSVGetField( pszFilename, "code", CPLSPrintf("%d", center),
56 : CC_Integer, "name" );
57 86 : if( pszName && pszName[0] == 0 )
58 0 : pszName = nullptr;
59 86 : return pszName;
60 : }
61 :
62 112 : const char *subCenterLookup(unsigned short int center,
63 : unsigned short int subcenter)
64 : {
65 112 : const char* pszFilename = GetGRIB2_CSVFilename("grib2_subcenter.csv");
66 112 : if( pszFilename == nullptr )
67 : {
68 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find grib2_subcenter.csv");
69 0 : return nullptr;
70 : }
71 112 : int iCenter = CSVGetFileFieldId(pszFilename,"center_code");
72 112 : int iSubCenter = CSVGetFileFieldId(pszFilename,"subcenter_code");
73 112 : int iName = CSVGetFileFieldId(pszFilename,"name");
74 112 : if( iCenter < 0 || iSubCenter < 0 || iName < 0 )
75 : {
76 0 : CPLError(CE_Failure, CPLE_AppDefined, "Bad structure for %s", pszFilename);
77 0 : return nullptr;
78 : }
79 112 : CSVRewind(pszFilename);
80 6929 : while( char** papszFields = CSVGetNextLine(pszFilename) )
81 : {
82 6820 : if( atoi(papszFields[iCenter]) == static_cast<int>(center) &&
83 968 : atoi(papszFields[iSubCenter]) == static_cast<int>(subcenter) )
84 : {
85 3 : return papszFields[iName];
86 : }
87 6817 : }
88 109 : return nullptr;
89 : }
90 :
91 : #ifdef unused_by_GDAL
92 : const char *processLookup (unsigned short int center, unsigned char process)
93 : {
94 : const char* pszFilename = GetGRIB2_CSVFilename("grib2_process.csv");
95 : if( pszFilename == nullptr )
96 : {
97 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find grib2_process.csv");
98 : return nullptr;
99 : }
100 : int iCenter = CSVGetFileFieldId(pszFilename,"center_code");
101 : int iProcess = CSVGetFileFieldId(pszFilename,"process_code");
102 : int iName = CSVGetFileFieldId(pszFilename,"name");
103 : if( iCenter < 0 || iProcess < 0 || iName < 0 )
104 : {
105 : CPLError(CE_Failure, CPLE_AppDefined, "Bad structure for %s", pszFilename);
106 : return nullptr;
107 : }
108 : CSVRewind(pszFilename);
109 : while( char** papszFields = CSVGetNextLine(pszFilename) )
110 : {
111 : if( atoi(papszFields[iCenter]) == static_cast<int>(center) &&
112 : atoi(papszFields[iProcess]) == static_cast<int>(process) )
113 : {
114 : return papszFields[iName];
115 : }
116 : }
117 : return nullptr;
118 : }
119 : #endif
120 :
121 : typedef struct {
122 : const char *GRIB2name, *NDFDname;
123 : } NDFD_AbrevOverrideTable;
124 :
125 :
126 320 : static unit_convert GetUnitConvertFromString(const char* pszUnitConv)
127 : {
128 : unit_convert convert;
129 320 : if( strcmp(pszUnitConv, "UC_NONE") == 0 )
130 194 : convert = UC_NONE;
131 126 : else if( strcmp(pszUnitConv, "UC_K2F") == 0 )
132 118 : convert = UC_K2F;
133 8 : else if( strcmp(pszUnitConv, "UC_InchWater") == 0 )
134 0 : convert = UC_InchWater;
135 8 : else if( strcmp(pszUnitConv, "UC_M2Feet") == 0 )
136 0 : convert = UC_M2Feet;
137 8 : else if( strcmp(pszUnitConv, "UC_M2Inch") == 0 )
138 0 : convert = UC_M2Inch;
139 8 : else if( strcmp(pszUnitConv, "UC_MS2Knots") == 0 )
140 0 : convert = UC_MS2Knots;
141 8 : else if( strcmp(pszUnitConv, "UC_LOG10") == 0 )
142 0 : convert = UC_LOG10;
143 8 : else if( strcmp(pszUnitConv, "UC_UVIndex") == 0 )
144 0 : convert = UC_UVIndex;
145 8 : else if( strcmp(pszUnitConv, "UC_M2StatuteMile") == 0 )
146 8 : convert = UC_M2StatuteMile;
147 : else
148 : {
149 0 : convert = UC_NONE;
150 0 : CPLError(CE_Warning, CPLE_AppDefined,
151 : "Unhandled unit conversion: %s", pszUnitConv);
152 : }
153 320 : return convert;
154 : }
155 :
156 : /*****************************************************************************
157 : * GetGrib2Table4_2_Record() --
158 : *
159 : * PURPOSE
160 : * Chooses the correct Parameter table depending on what is in the GRIB2
161 : * message's "Product Definition Section".
162 : *
163 : * ARGUMENTS
164 : * prodType = The product type (meteo, hydro, land, space, ocean, etc) (In)
165 : * cat = The category inside the product (Input)
166 : * subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input)
167 : * shortName= Pointer to short name of the parameter, or nullptr(Output)
168 : * name = Pointer to longer name of the parameter, or nullptr (Output)
169 : * unit = Pointer to unit name, or nullptr (Output)
170 : * convert = Pointer to unit converter, or nullptr (Output)
171 : *
172 : * FILES/DATABASES: None
173 : *
174 : * RETURNS: TRUE in case of success
175 : *****************************************************************************
176 : */
177 791 : static int GetGrib2Table4_2_Record (int prodType, int cat, int subcat,
178 : const char** shortName,
179 : const char** name,
180 : const char** unit,
181 : unit_convert* convert)
182 : {
183 791 : const char* pszBaseFilename = CPLSPrintf("grib2_table_4_2_%d_%d.csv",
184 : prodType, cat);
185 791 : const char* pszFilename = GetGRIB2_CSVFilename(pszBaseFilename);
186 791 : if( pszFilename == nullptr )
187 : {
188 513 : return FALSE;
189 : }
190 278 : int iSubcat = CSVGetFileFieldId(pszFilename,"subcat");
191 278 : int iShortName = CSVGetFileFieldId(pszFilename,"short_name");
192 278 : int iName = CSVGetFileFieldId(pszFilename,"name");
193 278 : int iUnit = CSVGetFileFieldId(pszFilename,"unit");
194 278 : int iUnitConv = CSVGetFileFieldId(pszFilename,"unit_conv");
195 278 : if( iSubcat < 0 || iShortName < 0 || iName < 0 || iUnit < 0 || iUnitConv < 0 )
196 : {
197 0 : CPLError(CE_Failure, CPLE_AppDefined, "Bad structure for %s", pszFilename);
198 0 : return FALSE;
199 : }
200 278 : CSVRewind(pszFilename);
201 10313 : while( char** papszFields = CSVGetNextLine(pszFilename) )
202 : {
203 10313 : if( atoi(papszFields[iSubcat]) == subcat )
204 : {
205 278 : if( shortName )
206 : {
207 : // Short name is unavailable from WMO-only entries, so
208 : // use longer name
209 278 : if( papszFields[iShortName][0] == 0 )
210 41 : *shortName = papszFields[iName];
211 : else
212 237 : *shortName = papszFields[iShortName];
213 : }
214 278 : if( name )
215 278 : *name = papszFields[iName];
216 278 : if( unit )
217 278 : *unit = papszFields[iUnit];
218 278 : if( convert )
219 278 : *convert = GetUnitConvertFromString(papszFields[iUnitConv]);
220 278 : return TRUE;
221 : }
222 10035 : }
223 0 : return FALSE;
224 : }
225 :
226 : /* *INDENT-OFF* */
227 : static const NDFD_AbrevOverrideTable NDFD_Override[] = {
228 : /* 0 */ {"TMP", "T"},
229 : /* 1 */ {"TMAX", "MaxT"},
230 : /* 2 */ {"TMIN", "MinT"},
231 : /* 3 */ {"DPT", "Td"},
232 : /* 4 */ {"APCP", "QPF"},
233 : /* Don't need SNOD for now. */
234 : /* 5 */ /* {"SNOD", "SnowDepth"}, */
235 : /* 6 */ {"WDIR", "WindDir"},
236 : /* 7 */ {"WIND", "WindSpd"},
237 : /* 8 */ {"TCDC", "Sky"},
238 : /* 9 */ {"WVHGT", "WaveHeight"},
239 : /* 10 */ {"ASNOW", "SnowAmt"},
240 : /* 11 */ {"GUST", "WindGust"},
241 : /* 12 */ {"MAXRH", "MaxRH"}, /* MPA added 201202 */
242 : /* 13 */ {"HTSGW", "WaveHeight"}, /* MPA added 201709 */
243 : };
244 : /* *INDENT-ON* */
245 :
246 1490 : int IsData_NDFD (unsigned short int center, unsigned short int subcenter)
247 : {
248 1490 : return ((center == 8) &&
249 1490 : ((subcenter == GRIB2MISSING_u2) || (subcenter == 0)));
250 : }
251 :
252 425 : int IsData_MOS (unsigned short int center, unsigned short int subcenter)
253 : {
254 425 : return ((center == 7) && (subcenter == 14));
255 : }
256 :
257 555 : static const char* GetGrib2LocalTable4_2FileName(int center,
258 : int subcenter)
259 : {
260 555 : const char* pszFilename = GetGRIB2_CSVFilename("grib2_table_4_2_local_index.csv");
261 555 : if( pszFilename == nullptr )
262 : {
263 0 : return nullptr;
264 : }
265 555 : int iCenter = CSVGetFileFieldId(pszFilename,"center_code");
266 555 : int iSubCenter = CSVGetFileFieldId(pszFilename,"subcenter_code");
267 555 : int iFilename = CSVGetFileFieldId(pszFilename,"filename");
268 555 : if( iCenter < 0 || iSubCenter < 0 || iFilename < 0 )
269 : {
270 0 : CPLError(CE_Failure, CPLE_AppDefined, "Bad structure for %s", pszFilename);
271 0 : return nullptr;
272 : }
273 555 : CSVRewind(pszFilename);
274 3687 : while( char** papszFields = CSVGetNextLine(pszFilename) )
275 : {
276 3174 : if( atoi(papszFields[iCenter]) == center )
277 : {
278 81 : if( papszFields[iSubCenter][0] == '\0' ||
279 39 : atoi(papszFields[iSubCenter]) == subcenter )
280 : {
281 42 : return GetGRIB2_CSVFilename(papszFields[iFilename]);
282 : }
283 : }
284 3132 : }
285 513 : return nullptr;
286 : }
287 :
288 : /*****************************************************************************
289 : * GetGrib2LocalTable4_2_Record() --
290 : *
291 : * PURPOSE
292 : * Return the parameter definition depending on what is in the GRIB2
293 : * message's "Product Definition Section" from a local parameter table
294 : * for a given center/subcenter.
295 : * Typically this is called after the default Choose_ParmTable was tried,
296 : * since it consists of all the local specs, and one has to linearly walk
297 : * through the table.
298 : *
299 : * ARGUMENTS
300 : * center = The center that created the data. (Input)
301 : * subcenter = The subcenter that created the data. (Input)
302 : * prodType = The product type (meteo, hydro, land, space, ocean, etc) (In)
303 : * cat = The category inside the product (Input)
304 : * subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input)
305 : * shortName= Pointer to short name of the parameter, or nullptr(Output)
306 : * name = Pointer to longer name of the parameter, or nullptr (Output)
307 : * unit = Pointer to unit name, or nullptr (Output)
308 : * convert = Pointer to unit converter, or nullptr (Output)
309 : *
310 : * FILES/DATABASES: None
311 : *
312 : * RETURNS: TRUE in case of success
313 : *****************************************************************************
314 : */
315 :
316 555 : static int GetGrib2LocalTable4_2_Record (int center,
317 : int subcenter,
318 : int prodType, int cat, int subcat,
319 : const char** shortName,
320 : const char** name,
321 : const char** unit,
322 : unit_convert* convert)
323 : {
324 555 : const char* pszFilename = GetGrib2LocalTable4_2FileName(center, subcenter);
325 555 : if( pszFilename == nullptr )
326 : {
327 513 : return FALSE;
328 : }
329 42 : int iProd = CSVGetFileFieldId(pszFilename,"prod");
330 42 : int iCat = CSVGetFileFieldId(pszFilename,"cat");
331 42 : int iSubcat = CSVGetFileFieldId(pszFilename,"subcat");
332 42 : int iShortName = CSVGetFileFieldId(pszFilename,"short_name");
333 42 : int iName = CSVGetFileFieldId(pszFilename,"name");
334 42 : int iUnit = CSVGetFileFieldId(pszFilename,"unit");
335 42 : int iUnitConv = CSVGetFileFieldId(pszFilename,"unit_conv");
336 42 : if( iProd < 0 || iCat < 0 || iSubcat < 0 || iShortName < 0 ||
337 42 : iName < 0 || iUnit < 0 || iUnitConv < 0 )
338 : {
339 0 : CPLError(CE_Failure, CPLE_AppDefined, "Bad structure for %s", pszFilename);
340 0 : return FALSE;
341 : }
342 42 : CSVRewind(pszFilename);
343 7736 : while( char** papszFields = CSVGetNextLine(pszFilename) )
344 : {
345 7736 : if( atoi(papszFields[iProd]) == prodType &&
346 7736 : atoi(papszFields[iCat]) == cat &&
347 508 : atoi(papszFields[iSubcat]) == subcat )
348 : {
349 42 : if( shortName )
350 42 : *shortName = papszFields[iShortName];
351 42 : if( name )
352 42 : *name = papszFields[iName];
353 42 : if( unit )
354 42 : *unit = papszFields[iUnit];
355 42 : if( convert )
356 42 : *convert = GetUnitConvertFromString(papszFields[iUnitConv]);
357 42 : return TRUE;
358 : }
359 7694 : }
360 0 : return FALSE;
361 : }
362 :
363 :
364 : /*****************************************************************************
365 : * ParseElemName() --
366 : *
367 : * Arthur Taylor / MDL
368 : *
369 : * PURPOSE
370 : * Converts a prodType, template, category and subcategory quadruple to the
371 : * ASCII string abbreviation of that variable.
372 : * For example: 0, 0, 0, 0, = "T" for temperature.
373 : *
374 : * ARGUMENTS
375 : * center = The center that created the data. (Input)
376 : * subcenter = The subcenter that created the data. (Input)
377 : * prodType = The GRIB2, section 0 product type. (Input)
378 : * templat = The GRIB2 section 4 template number. (Input)
379 : * cat = The GRIB2 section 4 "General category of Product." (Input)
380 : * subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input)
381 : * lenTime = The length of time over which statistics are done
382 : * (see template 4.8). (Input)
383 : * genID = The Generating process ID (used for GFS MOS) (Input)
384 : * probType = For Probability templates (Input)
385 : * lowerProb = Lower Limit for probability templates. (Input)
386 : * upperProb = Upper Limit for probability templates. (Input)
387 : * name = Short name for the data set (T, MaxT, etc) (Output)
388 : * comment = Long form of the name (Temperature, etc) (Output)
389 : * unit = What unit this variable is originally in (Output)
390 : *
391 : * FILES/DATABASES: None
392 : *
393 : * RETURNS: void
394 : *
395 : * HISTORY
396 : * 1/2004 Arthur Taylor (MDL/RSIS): Re-Created.
397 : * 6/2004 AAT: Added deltTime (because of Ozone issues).
398 : * 8/2004 AAT: Adjusted so template 9 gets units of % and no convert.
399 : * 3/2005 AAT: ReWrote to handle template 5, 9 and MOS.
400 : * 9/2005 AAT: Added code to handle MOS PoP06 vs MOS PoP12.
401 : *
402 : * NOTES
403 : *****************************************************************************
404 : */
405 : /* Deal with probability templates 2/16/2006 */
406 0 : static void ElemNameProb (uChar mstrVersion, uShort2 center, uShort2 subcenter, int prodType,
407 : CPL_UNUSED int templat,
408 : uChar cat, uChar subcat, sInt4 lenTime,
409 : uChar timeRangeUnit,
410 : uChar timeIncrType,
411 : CPL_UNUSED uChar genID,
412 : uChar probType,
413 : double lowerProb, double upperProb, char **name,
414 : char **comment, char **unit, int *convert)
415 : {
416 0 : char f_isNdfd = IsData_NDFD (center, subcenter);
417 0 : char f_isMos = IsData_MOS (center, subcenter);
418 :
419 0 : *unit = (char *) malloc (strlen ("[%]") + 1);
420 0 : strcpy (*unit, "[%]");
421 :
422 : {
423 : // 25.4 mm = 1 inch
424 0 : const double tmp = upperProb * 25.4;
425 :
426 : // TODO(schwehr): Make a function and reuse it for other limit checks.
427 0 : if (upperProb > tmp ||
428 0 : tmp > std::numeric_limits<int>::max() ||
429 0 : tmp < std::numeric_limits<int>::min() ||
430 0 : CPLIsNan(tmp) ) {
431 : // TODO(schwehr): What is the correct response?
432 0 : errSprintf ("ERROR: upperProb out of range. Setting to 0.\n");
433 0 : upperProb = 0.0;
434 : }
435 : }
436 :
437 0 : if (f_isNdfd || f_isMos) {
438 : /* Deal with NDFD/MOS handling of Prob Precip_Tot -> PoP12 */
439 0 : if ((prodType == 0) && (cat == 1) && (subcat == 8)) {
440 0 : if (probType == 0) {
441 0 : if (lenTime > 0) {
442 0 : if (timeRangeUnit == 3) {
443 0 : mallocSprintf (name, "ProbPrcpBlw%02dm", lenTime);
444 0 : mallocSprintf (comment, "%02d mon Prob of Precip below average", lenTime);
445 0 : } else if (timeRangeUnit == 4) {
446 0 : mallocSprintf (name, "ProbPrcpBlw%02dy", lenTime);
447 0 : mallocSprintf (comment, "%02d yr Prob of Precip below average", lenTime);
448 : } else {
449 0 : mallocSprintf (name, "ProbPrcpBlw%02d", lenTime);
450 0 : mallocSprintf (comment, "%02d hr Prob of Precip below average", lenTime);
451 : }
452 : } else {
453 0 : mallocSprintf (name, "ProbPrcpBlw");
454 0 : mallocSprintf (comment, "Prob of precip below average");
455 : }
456 0 : } else if (probType == 3) {
457 0 : if (lenTime > 0) {
458 0 : if (timeRangeUnit == 3) {
459 0 : mallocSprintf (name, "ProbPrcpAbv%02dm", lenTime);
460 0 : mallocSprintf (comment, "%02d mon Prob of Precip above average", lenTime);
461 0 : } else if (timeRangeUnit == 4) {
462 0 : mallocSprintf (name, "ProbPrcpAbv%02dy", lenTime);
463 0 : mallocSprintf (comment, "%02d yr Prob of Precip above average", lenTime);
464 : } else {
465 0 : mallocSprintf (name, "ProbPrcpAbv%02d", lenTime);
466 0 : mallocSprintf (comment, "%02d hr Prob of Precip above average", lenTime);
467 : }
468 : } else {
469 0 : mallocSprintf (name, "ProbPrcpAbv");
470 0 : mallocSprintf (comment, "Prob of precip above average");
471 : }
472 : } else {
473 0 : myAssert (probType == 1);
474 0 : if (lenTime > 0) {
475 0 : if (timeRangeUnit == 3) {
476 0 : if (upperProb != (double) .254) {
477 0 : mallocSprintf (name, "PoP%02dm-%03d", lenTime, (int) (upperProb / .254 + .5));
478 : } else {
479 0 : mallocSprintf (name, "PoP%02dm", lenTime);
480 : }
481 0 : mallocSprintf (comment, "%02d mon Prob of Precip > %g In.", lenTime, upperProb / 25.4);
482 0 : } else if (timeRangeUnit == 4) {
483 0 : if (upperProb != (double) .254) {
484 0 : mallocSprintf (name, "PoP%02dy-%03d", lenTime, (int) (upperProb / .254 + .5));
485 : } else {
486 0 : mallocSprintf (name, "PoP%02dy", lenTime);
487 : }
488 0 : mallocSprintf (comment, "%02d yr Prob of Precip > %g In.", lenTime, upperProb / 25.4);
489 : } else {
490 : /* The 300 is to deal with an old NDFD encoding bug from 2002:
491 : * PDS-S4 | Upper limit (scale value, scale factor) | 300 (3, -2)
492 : * 25.4 mm = 1 inch. Rain typically .01 inches = .254 mm
493 : */
494 0 : if ((upperProb != (double) .254) && (upperProb != (double) 300)) {
495 0 : mallocSprintf (name, "PoP%02d-%03d", lenTime, (int) (upperProb / .254 + .5));
496 : } else {
497 0 : mallocSprintf (name, "PoP%02d", lenTime);
498 : }
499 0 : if (upperProb != (double) 300) {
500 0 : mallocSprintf (comment, "%02d hr Prob of Precip > %g In.", lenTime, upperProb / 25.4);
501 : } else {
502 0 : mallocSprintf (comment, "%02d hr Prob of Precip > 0.01 In.", lenTime);
503 : }
504 : }
505 : } else {
506 0 : if (upperProb != (double) .254) {
507 0 : mallocSprintf (name, "PoP-p%03d", (int) (upperProb / .254 + .5));
508 : } else {
509 0 : mallocSprintf (name, "PoP");
510 : }
511 0 : mallocSprintf (comment, "Prob of Precip > %g In.", upperProb / 25.4);
512 : }
513 : }
514 0 : *convert = UC_NONE;
515 0 : return;
516 : }
517 : /*
518 : * Deal with NDFD handling of Prob. Wind speeds.
519 : * There are different solutions for naming the Prob. Wind fields
520 : * AAT(Mine): ProbSurge5c
521 : */
522 0 : if ((prodType == 10) && (cat == 3) && (subcat == 192)) {
523 0 : myAssert (probType == 1);
524 0 : myAssert (lenTime > 0);
525 0 : if (timeIncrType == 2) {
526 : /* Incremental */
527 0 : mallocSprintf (name, "ProbSurge%02di",
528 0 : (int) ((upperProb / 0.3048) + .5));
529 : } else {
530 : /* Cumulative */
531 0 : myAssert (timeIncrType == 192);
532 0 : mallocSprintf (name, "ProbSurge%02dc",
533 0 : (int) ((upperProb / 0.3048) + .5));
534 : }
535 0 : if (timeRangeUnit == 3) {
536 0 : mallocSprintf (comment, "%02d mon Prob of Hurricane Storm Surge > %g "
537 : "m", lenTime, upperProb);
538 0 : } else if (timeRangeUnit == 4) {
539 0 : mallocSprintf (comment, "%02d yr Prob of Hurricane Storm Surge > %g "
540 : "m", lenTime, upperProb);
541 : } else {
542 0 : mallocSprintf (comment, "%02d hr Prob of Hurricane Storm Surge > %g "
543 : "m", lenTime, upperProb);
544 : }
545 0 : *convert = UC_NONE;
546 0 : return;
547 : }
548 : }
549 0 : if (f_isNdfd) {
550 : /*
551 : * Deal with NDFD handling of Prob. Wind speeds.
552 : * There are different solutions for naming the Prob. Wind fields
553 : * Tim Boyer: TCWindSpdIncr34 TCWindSpdIncr50 TCWindSpdIncr64
554 : * TCWindSpdCumu34 TCWindSpdCumu50 TCWindSpdCumu64
555 : * Dave Ruth: tcwspdabv34i tcwspdabv50i tcwspdabv64i
556 : * tcwspdabv34c tcwspdabv50c tcwspdabv64c
557 : * AAT(Mine): ProbWindSpd34c ProbWindSpd50c ProbWindSpd64c
558 : * ProbWindSpd34i ProbWindSpd50i ProbWindSpd64i
559 : */
560 0 : if ((prodType == 0) && (cat == 2) && (subcat == 1)) {
561 0 : myAssert (probType == 1);
562 0 : myAssert (lenTime > 0);
563 0 : if (timeIncrType == 2) {
564 : /* Incremental */
565 0 : mallocSprintf (name, "ProbWindSpd%02di",
566 0 : (int) ((upperProb * 3600. / 1852.) + .5));
567 : } else {
568 : /* Cumulative */
569 0 : myAssert (timeIncrType == 192);
570 0 : mallocSprintf (name, "ProbWindSpd%02dc",
571 0 : (int) ((upperProb * 3600. / 1852.) + .5));
572 : }
573 0 : if (timeRangeUnit == 3) {
574 0 : mallocSprintf (comment, "%02d mon Prob of Wind speed > %g m/s",
575 : lenTime, upperProb);
576 0 : } else if (timeRangeUnit == 4) {
577 0 : mallocSprintf (comment, "%02d yr Prob of Wind speed > %g m/s",
578 : lenTime, upperProb);
579 : } else {
580 0 : mallocSprintf (comment, "%02d hr Prob of Wind speed > %g m/s",
581 : lenTime, upperProb);
582 : }
583 0 : *convert = UC_NONE;
584 0 : return;
585 : }
586 : }
587 :
588 : /* Only look at Generic tables if mstrVersion is not 255. */
589 0 : int gotRecordGeneric = FALSE;
590 0 : const char* pszShortName = nullptr;
591 0 : const char* pszName = nullptr;
592 0 : const char* pszUnit = nullptr;
593 0 : if (mstrVersion != 255) {
594 0 : gotRecordGeneric = GetGrib2Table4_2_Record (prodType, cat, subcat,
595 : &pszShortName, &pszName, &pszUnit,
596 : nullptr);
597 : }
598 :
599 0 : if (gotRecordGeneric && strcmp(pszName, "Reserved for local use") == 0) {
600 0 : gotRecordGeneric = false;
601 : }
602 :
603 0 : if (gotRecordGeneric) {
604 : /* Check for NDFD over-rides. */
605 : /* The NDFD over-rides for probability templates have already been
606 : * handled. */
607 0 : if (lenTime > 0) {
608 0 : if (timeRangeUnit == 3) {
609 0 : mallocSprintf (name, "Prob%s%02dm", pszShortName, lenTime);
610 0 : mallocSprintf (comment, "%02d mon Prob of %s ", lenTime,
611 : pszName);
612 0 : } else if (timeRangeUnit == 4) {
613 0 : mallocSprintf (name, "Prob%s%02dy", pszShortName, lenTime);
614 0 : mallocSprintf (comment, "%02d yr Prob of %s ", lenTime,
615 : pszName);
616 : } else {
617 0 : mallocSprintf (name, "Prob%s%02d", pszShortName, lenTime);
618 0 : mallocSprintf (comment, "%02d hr Prob of %s ", lenTime,
619 : pszName);
620 : }
621 : } else {
622 0 : mallocSprintf (name, "Prob%s", pszShortName);
623 0 : mallocSprintf (comment, "Prob of %s ", pszName);
624 : }
625 0 : if (probType == 0) {
626 0 : if ((f_isNdfd || f_isMos) && (strcmp (pszShortName, "TMP") == 0)) {
627 0 : reallocSprintf (comment, "below average");
628 0 : free (*name);
629 0 : if (lenTime > 0) {
630 0 : if (timeRangeUnit == 3) {
631 0 : mallocSprintf (name, "Prob%sBlw%02dm", pszShortName, lenTime);
632 0 : } else if (timeRangeUnit == 4) {
633 0 : mallocSprintf (name, "Prob%sBlw%02dy", pszShortName, lenTime);
634 : } else {
635 0 : mallocSprintf (name, "Prob%sBlw%02d", pszShortName, lenTime);
636 : }
637 : } else {
638 0 : mallocSprintf (name, "Prob%sBlw", pszShortName);
639 : }
640 : } else {
641 0 : reallocSprintf (comment, "< %g %s", lowerProb, pszUnit);
642 : }
643 0 : } else if (probType == 1) {
644 0 : if ((f_isNdfd || f_isMos) && (strcmp (pszShortName, "TMP") == 0)) {
645 0 : reallocSprintf (comment, "above average");
646 0 : free (*name);
647 0 : if (lenTime > 0) {
648 0 : if (timeRangeUnit == 3) {
649 0 : mallocSprintf (name, "Prob%sAbv%02dm", pszShortName, lenTime);
650 0 : } else if (timeRangeUnit == 4) {
651 0 : mallocSprintf (name, "Prob%sAbv%02dy", pszShortName, lenTime);
652 : } else {
653 0 : mallocSprintf (name, "Prob%sAbv%02d", pszShortName, lenTime);
654 : }
655 : } else {
656 0 : mallocSprintf (name, "Prob%sAbv", pszShortName);
657 : }
658 : } else {
659 0 : reallocSprintf (comment, "> %g %s", upperProb, pszUnit);
660 : }
661 0 : } else if (probType == 2) {
662 0 : reallocSprintf (comment, ">= %g, < %g %s", lowerProb, upperProb,
663 : pszUnit);
664 0 : } else if (probType == 3) {
665 0 : if ((f_isNdfd || f_isMos) && (strcmp (pszShortName, "TMP") == 0)) {
666 0 : reallocSprintf (comment, "above average");
667 0 : free (*name);
668 0 : if (lenTime > 0) {
669 0 : if (timeRangeUnit == 3) {
670 0 : mallocSprintf (name, "Prob%sAbv%02dm", pszShortName, lenTime);
671 0 : } else if (timeRangeUnit == 4) {
672 0 : mallocSprintf (name, "Prob%sAbv%02dy", pszShortName, lenTime);
673 : } else {
674 0 : mallocSprintf (name, "Prob%sAbv%02d", pszShortName, lenTime);
675 : }
676 : } else {
677 0 : mallocSprintf (name, "Prob%sAbv", pszShortName);
678 : }
679 : } else {
680 0 : reallocSprintf (comment, "> %g %s", lowerProb, pszUnit);
681 : }
682 0 : } else if (probType == 4) {
683 0 : if ((f_isNdfd || f_isMos) && (strcmp (pszShortName, "TMP") == 0)) {
684 0 : reallocSprintf (comment, "below average");
685 0 : free (*name);
686 0 : if (lenTime > 0) {
687 0 : if (timeRangeUnit == 3) {
688 0 : mallocSprintf (name, "Prob%sBlw%02dm", pszShortName, lenTime);
689 0 : } else if (timeRangeUnit == 4) {
690 0 : mallocSprintf (name, "Prob%sBlw%02dy", pszShortName, lenTime);
691 : } else {
692 0 : mallocSprintf (name, "Prob%sBlw%02d", pszShortName, lenTime);
693 : }
694 : } else {
695 0 : mallocSprintf (name, "Prob%sBlw", pszShortName);
696 : }
697 : } else {
698 0 : reallocSprintf (comment, "< %g %s", upperProb, pszUnit);
699 : }
700 : } else {
701 0 : reallocSprintf (comment, "%s", pszUnit);
702 : }
703 0 : *convert = UC_NONE;
704 0 : return;
705 : }
706 :
707 : /* Local use tables. */
708 0 : int gotRecordLocal = GetGrib2LocalTable4_2_Record (center, subcenter,
709 : prodType, cat, subcat,
710 : &pszShortName, &pszName, &pszUnit,
711 : nullptr);
712 0 : if (gotRecordLocal) {
713 : /* Ignore adding Prob prefix and "Probability of" to NDFD SPC prob
714 : * products. */
715 0 : if (lenTime > 0) {
716 0 : if (timeRangeUnit == 3) {
717 0 : mallocSprintf (name, "Prob%s%02dm", pszShortName, lenTime);
718 0 : mallocSprintf (comment, "%02d mon Prob of %s ", lenTime,
719 : pszName);
720 0 : } else if (timeRangeUnit == 4) {
721 0 : mallocSprintf (name, "Prob%s%02dy", pszShortName, lenTime);
722 0 : mallocSprintf (comment, "%02d yr Prob of %s ", lenTime,
723 : pszName);
724 : } else {
725 0 : mallocSprintf (name, "Prob%s%02d", pszShortName, lenTime);
726 0 : mallocSprintf (comment, "%02d hr Prob of %s ", lenTime,
727 : pszName);
728 : }
729 : } else {
730 0 : mallocSprintf (name, "Prob%s", pszShortName);
731 0 : mallocSprintf (comment, "Prob of %s ", pszName);
732 : }
733 0 : if (probType == 0) {
734 0 : reallocSprintf (comment, "< %g %s", lowerProb,
735 : pszUnit);
736 0 : } else if (probType == 1) {
737 0 : reallocSprintf (comment, "> %g %s", upperProb,
738 : pszUnit);
739 0 : } else if (probType == 2) {
740 0 : reallocSprintf (comment, ">= %g, < %g %s", lowerProb,
741 : upperProb, pszUnit);
742 0 : } else if (probType == 3) {
743 0 : reallocSprintf (comment, "> %g %s", lowerProb,
744 : pszUnit);
745 0 : } else if (probType == 4) {
746 0 : reallocSprintf (comment, "< %g %s", upperProb,
747 : pszUnit);
748 : } else {
749 0 : reallocSprintf (comment, "%s", pszUnit);
750 : }
751 0 : *convert = UC_NONE;
752 0 : return;
753 : }
754 :
755 0 : *name = (char *) malloc (strlen ("ProbUnknown") + 1);
756 0 : strcpy (*name, "ProbUnknown");
757 0 : mallocSprintf (comment, "Prob of (prodType %d, cat %d, subcat %d)",
758 : prodType, cat, subcat);
759 0 : *convert = UC_NONE;
760 0 : return;
761 : }
762 :
763 : /* Deal with percentile templates 5/1/2006 */
764 0 : static void ElemNamePerc (uChar mstrVersion, uShort2 center, uShort2 subcenter, int prodType,
765 : CPL_UNUSED int templat,
766 : uChar cat, uChar subcat, sInt4 lenTime,
767 : uChar timeRangeUnit,
768 : sChar percentile, char **name, char **comment,
769 : char **unit, int *convert)
770 : {
771 : /* Only look at Generic tables if mstrVersion is not 255. */
772 0 : int gotRecordGeneric = FALSE;
773 0 : const char* pszShortName = nullptr;
774 0 : const char* pszName = nullptr;
775 0 : const char* pszUnit = nullptr;
776 0 : unit_convert unitConvert = UC_NONE;
777 0 : if (mstrVersion != 255) {
778 0 : gotRecordGeneric = GetGrib2Table4_2_Record (prodType, cat, subcat,
779 : &pszShortName, &pszName, &pszUnit,
780 : &unitConvert);
781 : }
782 :
783 0 : if (gotRecordGeneric && strcmp(pszName, "Reserved for local use") == 0) {
784 0 : gotRecordGeneric = false;
785 : }
786 :
787 0 : if (gotRecordGeneric) {
788 : /* Check for NDFD over-rides. */
789 0 : if (IsData_NDFD (center, subcenter) ||
790 0 : IsData_MOS (center, subcenter)) {
791 0 : for (size_t i = 0; i < (sizeof (NDFD_Override) /
792 : sizeof (NDFD_AbrevOverrideTable)); i++) {
793 0 : if (strcmp (pszShortName, "ASNOW") == 0) {
794 0 : if (timeRangeUnit == 3) {
795 0 : mallocSprintf (name, "%s%02dm%s%02dm", "Snow", lenTime, "e", percentile);
796 0 : mallocSprintf (comment, "%02d mon %s Percentile(%d)", lenTime,
797 : pszName, percentile);
798 0 : } else if (timeRangeUnit == 4) {
799 0 : mallocSprintf (name, "%s%02dy%s%02dy", "Snow", lenTime, "e", percentile);
800 0 : mallocSprintf (comment, "%02d yr %s Percentile(%d)", lenTime,
801 : pszName, percentile);
802 : } else {
803 0 : mallocSprintf (name, "%s%02d%s%02d", "Snow", lenTime, "e", percentile);
804 0 : mallocSprintf (comment, "%02d hr %s Percentile(%d)", lenTime,
805 : pszName, percentile);
806 : }
807 0 : mallocSprintf (unit, "[%s]", pszUnit);
808 0 : *convert = unitConvert;
809 0 : return;
810 : }
811 0 : if (strcmp (NDFD_Override[i].GRIB2name, pszShortName) ==
812 : 0) {
813 0 : mallocSprintf (name, "%s%02d", NDFD_Override[i].NDFDname,
814 : percentile);
815 0 : if (lenTime > 0) {
816 0 : if (timeRangeUnit == 3) {
817 0 : mallocSprintf (comment, "%02d mon %s Percentile(%d)",
818 : lenTime, pszName,
819 : percentile);
820 0 : } else if (timeRangeUnit == 4) {
821 0 : mallocSprintf (comment, "%02d yr %s Percentile(%d)",
822 : lenTime, pszName,
823 : percentile);
824 : } else {
825 0 : mallocSprintf (comment, "%02d hr %s Percentile(%d)",
826 : lenTime, pszName,
827 : percentile);
828 : }
829 : } else {
830 0 : mallocSprintf (comment, "%s Percentile(%d)",
831 : pszName, percentile);
832 : }
833 0 : mallocSprintf (unit, "[%s]", pszUnit);
834 0 : *convert = unitConvert;
835 0 : return;
836 : }
837 : }
838 : }
839 0 : mallocSprintf (name, "%s%02d", pszShortName, percentile);
840 0 : if (lenTime > 0) {
841 0 : if (timeRangeUnit == 3) {
842 0 : mallocSprintf (comment, "%02d mon %s Percentile(%d)",
843 : lenTime, pszName, percentile);
844 0 : } else if (timeRangeUnit == 4) {
845 0 : mallocSprintf (comment, "%02d yr %s Percentile(%d)",
846 : lenTime, pszName, percentile);
847 : } else {
848 0 : mallocSprintf (comment, "%02d hr %s Percentile(%d)",
849 : lenTime, pszName, percentile);
850 : }
851 : } else {
852 0 : mallocSprintf (comment, "%s Percentile(%d)",
853 : pszName, percentile);
854 : }
855 0 : mallocSprintf (unit, "[%s]", pszUnit);
856 0 : *convert = unitConvert;
857 0 : return;
858 : }
859 :
860 : /* Local use tables. */
861 0 : int gotRecordLocal = GetGrib2LocalTable4_2_Record (center, subcenter,
862 : prodType, cat, subcat,
863 : &pszShortName, &pszName, &pszUnit,
864 : &unitConvert);
865 0 : if (gotRecordLocal) {
866 : /* If last two characters in name are numbers, then the name contains
867 : * the percentile (or exceedance value) so don't tack on percentile here.*/
868 0 : size_t len = strlen(pszShortName);
869 0 : if (len >= 2 &&
870 0 : isdigit(static_cast<unsigned char>(pszShortName[len -1])) &&
871 0 : isdigit(static_cast<unsigned char>(pszShortName[len -2]))) {
872 0 : mallocSprintf (name, "%s", pszShortName);
873 0 : } else if ((strcmp (pszShortName, "Surge") == 0) ||
874 0 : (strcmp (pszShortName, "SURGE") == 0)) {
875 : /* Provide a special exception for storm surge exceedance.
876 : * Want exceedance value rather than percentile value.
877 : */
878 0 : mallocSprintf (name, "%s%02d", pszShortName, 100 - percentile);
879 : } else {
880 0 : mallocSprintf (name, "%s%02d", pszShortName, percentile);
881 : }
882 :
883 0 : if (lenTime > 0) {
884 0 : if (timeRangeUnit == 3) {
885 0 : mallocSprintf (comment, "%02d mon %s Percentile(%d)",
886 : lenTime, pszName, percentile);
887 0 : } else if (timeRangeUnit == 4) {
888 0 : mallocSprintf (comment, "%02d yr %s Percentile(%d)",
889 : lenTime, pszName, percentile);
890 : } else {
891 0 : mallocSprintf (comment, "%02d hr %s Percentile(%d)",
892 : lenTime, pszName, percentile);
893 : }
894 : } else {
895 0 : mallocSprintf (comment, "%s Percentile(%d)",
896 : pszName, percentile);
897 : }
898 0 : mallocSprintf (unit, "[%s]", pszUnit);
899 0 : *convert = unitConvert;
900 0 : return;
901 : }
902 :
903 0 : *name = (char *) malloc (strlen ("unknown") + 1);
904 0 : strcpy (*name, "unknown");
905 0 : mallocSprintf (comment, "(prodType %d, cat %d, subcat %d)", prodType,
906 : cat, subcat);
907 0 : *unit = (char *) malloc (strlen ("[-]") + 1);
908 0 : strcpy (*unit, "[-]");
909 0 : *convert = UC_NONE;
910 0 : return;
911 : }
912 :
913 : /* Deal with non-prob templates 2/16/2006 */
914 794 : static void ElemNameNorm (uChar mstrVersion, uShort2 center, uShort2 subcenter, int prodType,
915 : int templat, uChar cat, uChar subcat, sInt4 lenTime,
916 : uChar timeRangeUnit, uChar statProcessID,
917 : CPL_UNUSED uChar timeIncrType,
918 : CPL_UNUSED uChar genID,
919 : CPL_UNUSED uChar probType,
920 : CPL_UNUSED double lowerProb,
921 : CPL_UNUSED double upperProb,
922 : char **name,
923 : char **comment, char **unit, int *convert,
924 : sChar f_fstValue, double fstSurfValue,
925 : sChar f_sndValue, double sndSurfValue)
926 : {
927 : sChar f_accum;
928 : /* float delt; */
929 :
930 : /* Check for over-ride case for ozone. Originally just for NDFD, but I
931 : * think it is useful for ozone data that originated elsewhere. */
932 794 : if ((prodType == 0) && (templat == 8) && (cat == 14) && (subcat == 193)) {
933 0 : if (lenTime > 0) {
934 0 : if (timeRangeUnit == 3) {
935 0 : mallocSprintf (name, "Ozone%02dm", lenTime);
936 0 : mallocSprintf (comment, "%d mon Average Ozone Concentration", lenTime);
937 0 : } else if (timeRangeUnit == 4) {
938 0 : mallocSprintf (name, "Ozone%02dy", lenTime);
939 0 : mallocSprintf (comment, "%d yr Average Ozone Concentration", lenTime);
940 : } else {
941 0 : mallocSprintf (name, "Ozone%02d", lenTime);
942 0 : mallocSprintf (comment, "%d hr Average Ozone Concentration", lenTime);
943 : }
944 : } else {
945 0 : *name = (char *) malloc (strlen ("AVGOZCON") + 1);
946 0 : strcpy (*name, "AVGOZCON");
947 0 : *comment = (char *) malloc (strlen ("Average Ozone Concentration") +
948 : 1);
949 0 : strcpy (*comment, "Average Ozone Concentration");
950 : }
951 0 : *unit = (char *) malloc (strlen ("[PPB]") + 1);
952 0 : strcpy (*unit, "[PPB]");
953 0 : *convert = UC_NONE;
954 0 : return;
955 : }
956 : /* Check for over-ride case for smokec / smokes. */
957 794 : if (center == 7) {
958 143 : if ((prodType == 0) && (cat == 13) && (subcat == 195)) {
959 : /* If NCEP/ARL (genID=6) then it is dust */
960 0 : if (genID == 6) {
961 0 : if (f_fstValue && f_sndValue) {
962 0 : const double delt = fstSurfValue - sndSurfValue;
963 0 : if ((delt <= 100) && (delt >= -100)) {
964 0 : *name = (char *) malloc (strlen ("dusts") + 1);
965 0 : strcpy (*name, "dusts");
966 0 : *comment = (char *) malloc (strlen ("Surface level dust") + 1);
967 0 : strcpy (*comment, "Surface level dust");
968 0 : *unit = (char *) malloc (strlen ("[log10(10^-6g/m^3)]") + 1);
969 0 : strcpy (*unit, "[log10(10^-6g/m^3)]");
970 0 : *convert = UC_LOG10;
971 0 : return;
972 0 : } else if ((delt <= 5000) && (delt >= -5000)) {
973 0 : *name = (char *) malloc (strlen ("dustc") + 1);
974 0 : strcpy (*name, "dustc");
975 0 : *comment = (char *) malloc (strlen ("Average vertical column dust") + 1);
976 0 : strcpy (*comment, "Average vertical column dust");
977 0 : *unit = (char *) malloc (strlen ("[log10(10^-6g/m^3)]") + 1);
978 0 : strcpy (*unit, "[log10(10^-6g/m^3)]");
979 0 : *convert = UC_LOG10;
980 0 : return;
981 : }
982 : }
983 : } else {
984 0 : if (f_fstValue && f_sndValue) {
985 0 : const double delt = fstSurfValue - sndSurfValue;
986 0 : if ((delt <= 100) && (delt >= -100)) {
987 0 : *name = (char *) malloc (strlen ("smokes") + 1);
988 0 : strcpy (*name, "smokes");
989 0 : *comment = (char *) malloc (strlen ("Surface level smoke from fires") + 1);
990 0 : strcpy (*comment, "Surface level smoke from fires");
991 0 : *unit = (char *) malloc (strlen ("[log10(10^-6g/m^3)]") + 1);
992 0 : strcpy (*unit, "[log10(10^-6g/m^3)]");
993 0 : *convert = UC_LOG10;
994 0 : return;
995 0 : } else if ((delt <= 5000) && (delt >= -5000)) {
996 0 : *name = (char *) malloc (strlen ("smokec") + 1);
997 0 : strcpy (*name, "smokec");
998 0 : *comment = (char *) malloc (strlen ("Average vertical column smoke from fires") + 1);
999 0 : strcpy (*comment, "Average vertical column smoke from fires");
1000 0 : *unit = (char *) malloc (strlen ("[log10(10^-6g/m^3)]") + 1);
1001 0 : strcpy (*unit, "[log10(10^-6g/m^3)]");
1002 0 : *convert = UC_LOG10;
1003 0 : return;
1004 : }
1005 : }
1006 : }
1007 : }
1008 : }
1009 :
1010 : /* Only look at Generic tables if mstrVersion is not 255. */
1011 794 : int gotRecordGeneric = FALSE;
1012 794 : const char* pszShortName = nullptr;
1013 794 : const char* pszName = nullptr;
1014 794 : const char* pszUnit = nullptr;
1015 794 : unit_convert unitConvert = UC_NONE;
1016 794 : if (mstrVersion != 255) {
1017 791 : gotRecordGeneric = GetGrib2Table4_2_Record (prodType, cat, subcat,
1018 : &pszShortName, &pszName, &pszUnit,
1019 : &unitConvert);
1020 : }
1021 :
1022 794 : if (gotRecordGeneric && strcmp(pszName, "Reserved for local use") == 0) {
1023 39 : gotRecordGeneric = false;
1024 : }
1025 :
1026 794 : if (gotRecordGeneric) {
1027 : /* Check for NDFD over-rides. */
1028 239 : if (IsData_MOS (center, subcenter)) {
1029 2 : if (strcmp (pszShortName, "APCP") == 0) {
1030 0 : if (timeRangeUnit == 3) {
1031 0 : mallocSprintf (name, "%s%02dm", "QPF", lenTime);
1032 0 : mallocSprintf (comment, "%02d mon %s", lenTime,
1033 : pszName);
1034 0 : } else if (timeRangeUnit == 4) {
1035 0 : mallocSprintf (name, "%s%02dy", "QPF", lenTime);
1036 0 : mallocSprintf (comment, "%02d yr %s", lenTime,
1037 : pszName);
1038 : } else {
1039 0 : mallocSprintf (name, "%s%02d", "QPF", lenTime);
1040 0 : mallocSprintf (comment, "%02d hr %s", lenTime,
1041 : pszName);
1042 : }
1043 0 : mallocSprintf (unit, "[%s]", pszUnit);
1044 0 : *convert = unitConvert;
1045 0 : return;
1046 : }
1047 2 : if (strcmp (pszShortName, "ASNOW") == 0) {
1048 0 : if (timeRangeUnit == 3) {
1049 0 : mallocSprintf (name, "%s%02dm", "SnowAmt", lenTime);
1050 0 : mallocSprintf (comment, "%02d mon %s", lenTime,
1051 : pszName);
1052 0 : } else if (timeRangeUnit == 4) {
1053 0 : mallocSprintf (name, "%s%02dy", "SnowAmt", lenTime);
1054 0 : mallocSprintf (comment, "%02d yr %s", lenTime,
1055 : pszName);
1056 : } else {
1057 0 : mallocSprintf (name, "%s%02d", "SnowAmt", lenTime);
1058 0 : mallocSprintf (comment, "%02d hr %s", lenTime,
1059 : pszName);
1060 : }
1061 0 : mallocSprintf (unit, "[%s]", pszUnit);
1062 0 : *convert = unitConvert;
1063 0 : return;
1064 : }
1065 : }
1066 239 : if (IsData_NDFD (center, subcenter) || IsData_MOS (center, subcenter)) {
1067 55 : if (strcmp (pszShortName, "EVP") == 0) {
1068 0 : if (statProcessID == 10) {
1069 0 : mallocSprintf (name, "%s%02d", "EvpDep", lenTime);
1070 0 : mallocSprintf (comment, "%02d hr Evapo-Transpiration departure from normal",
1071 : lenTime);
1072 : } else {
1073 0 : mallocSprintf (name, "%s%02d", "Evp", lenTime);
1074 0 : mallocSprintf (comment, "%02d hr Evapo-Transpiration", lenTime);
1075 : }
1076 0 : mallocSprintf (unit, "[%s]", pszUnit);
1077 0 : *convert = unitConvert;
1078 0 : return;
1079 : }
1080 187 : for (size_t i = 0; i < (sizeof (NDFD_Override) /
1081 : sizeof (NDFD_AbrevOverrideTable)); i++) {
1082 185 : if (strcmp (NDFD_Override[i].GRIB2name, pszShortName) ==
1083 : 0) {
1084 53 : *name = (char *) malloc (strlen (NDFD_Override[i].NDFDname) + 1);
1085 53 : strcpy (*name, NDFD_Override[i].NDFDname);
1086 53 : *comment = (char *) malloc (strlen (pszName) + 1);
1087 53 : strcpy (*comment, pszName);
1088 53 : mallocSprintf (unit, "[%s]", pszUnit);
1089 53 : *convert = unitConvert;
1090 53 : return;
1091 : }
1092 : }
1093 : }
1094 : /* Allow hydrologic PoP, thunderstorm probability (TSTM), or APCP to
1095 : * have lenTime labels. */
1096 186 : f_accum = (((prodType == 1) && (cat == 1) && (subcat == 2)) ||
1097 186 : ((prodType == 0) && (cat == 19) && (subcat == 2)) ||
1098 547 : ((prodType == 0) && (cat == 1) && (subcat == 8)) ||
1099 175 : ((prodType == 0) && (cat == 19) && (subcat == 203)));
1100 186 : if (f_accum && (lenTime > 0)) {
1101 0 : if (timeRangeUnit == 3) {
1102 0 : mallocSprintf (name, "%s%02dm", pszShortName, lenTime);
1103 0 : mallocSprintf (comment, "%02d mon %s", lenTime,
1104 : pszName);
1105 0 : } else if (timeRangeUnit == 4) {
1106 0 : mallocSprintf (name, "%s%02dy", pszShortName, lenTime);
1107 0 : mallocSprintf (comment, "%02d yr %s", lenTime,
1108 : pszName);
1109 : } else {
1110 0 : mallocSprintf (name, "%s%02d", pszShortName, lenTime);
1111 0 : mallocSprintf (comment, "%02d hr %s", lenTime,
1112 : pszName);
1113 : }
1114 : } else {
1115 186 : *name = (char *) malloc (strlen (pszShortName) + 1);
1116 186 : strcpy (*name, pszShortName);
1117 186 : *comment = (char *) malloc (strlen (pszName) + 1);
1118 186 : strcpy (*comment, pszName);
1119 : }
1120 186 : mallocSprintf (unit, "[%s]", pszUnit);
1121 186 : *convert = unitConvert;
1122 186 : return;
1123 : }
1124 :
1125 : /* Local use tables. */
1126 555 : int gotRecordLocal = GetGrib2LocalTable4_2_Record (center, subcenter,
1127 : prodType, cat, subcat,
1128 : &pszShortName, &pszName, &pszUnit,
1129 : &unitConvert);
1130 555 : if (gotRecordLocal) {
1131 : /* Allow specific products with non-zero lenTime to reflect that.
1132 : */
1133 : #ifdef deadcode
1134 : f_accum = 0;
1135 : if (f_accum && (lenTime > 0)) {
1136 : if (timeRangeUnit == 3) {
1137 : mallocSprintf (name, "%s%02dm", pszShortName, lenTime);
1138 : mallocSprintf (comment, "%02d mon %s", lenTime,
1139 : pszName);
1140 : } else if (timeRangeUnit == 4) {
1141 : mallocSprintf (name, "%s%02dy", pszShortName, lenTime);
1142 : mallocSprintf (comment, "%02d yr %s", lenTime,
1143 : pszName);
1144 : } else {
1145 : mallocSprintf (name, "%s%02d", pszShortName, lenTime);
1146 : mallocSprintf (comment, "%02d hr %s", lenTime,
1147 : pszName);
1148 : }
1149 : } else
1150 : #endif
1151 : {
1152 42 : *name = (char *) malloc (strlen (pszShortName) + 1);
1153 42 : strcpy (*name, pszShortName);
1154 42 : *comment = (char *) malloc (strlen (pszName) + 1);
1155 42 : strcpy (*comment, pszName);
1156 : }
1157 42 : mallocSprintf (unit, "[%s]", pszUnit);
1158 42 : *convert = unitConvert;
1159 42 : return;
1160 : }
1161 :
1162 513 : *name = (char *) malloc (strlen ("unknown") + 1);
1163 513 : strcpy (*name, "unknown");
1164 513 : mallocSprintf (comment, "(prodType %d, cat %d, subcat %d)", prodType,
1165 : cat, subcat);
1166 513 : *unit = (char *) malloc (strlen ("[-]") + 1);
1167 513 : strcpy (*unit, "[-]");
1168 513 : *convert = UC_NONE;
1169 513 : return;
1170 : }
1171 :
1172 794 : void ParseElemName (CPL_UNUSED uChar mstrVersion, uShort2 center, uShort2 subcenter, int prodType,
1173 : int templat, int cat, int subcat, sInt4 lenTime,
1174 : uChar timeRangeUnit, CPL_UNUSED uChar statProcessID,
1175 : uChar timeIncrType, uChar genID, uChar probType,
1176 : double lowerProb, double upperProb,
1177 : uChar derivedFcst,
1178 : char **name,
1179 : char **comment, char **unit, int *convert,
1180 : sChar percentile, uChar genProcess,
1181 : sChar f_fstValue, double fstSurfValue,
1182 : sChar f_sndValue, double sndSurfValue)
1183 : {
1184 794 : char f_isNdfd = IsData_NDFD (center, subcenter);
1185 794 : myAssert (*name == nullptr);
1186 794 : myAssert (*comment == nullptr);
1187 794 : myAssert (*unit == nullptr);
1188 :
1189 : /* Check if this is Probability data */
1190 794 : if ((templat == GS4_PROBABIL_TIME) || (templat == GS4_PROBABIL_PNT)) {
1191 0 : if (f_isNdfd && (prodType == 0) && (cat == 19)) {
1192 : /* don't use ElemNameProb. */
1193 0 : ElemNameNorm (mstrVersion, center, subcenter, prodType, templat, cat, subcat,
1194 : lenTime, timeRangeUnit, statProcessID, timeIncrType, genID, probType, lowerProb,
1195 : upperProb, name, comment, unit, convert, f_fstValue, fstSurfValue,
1196 : f_sndValue, sndSurfValue);
1197 :
1198 : } else {
1199 0 : ElemNameProb (mstrVersion, center, subcenter, prodType, templat, cat, subcat,
1200 : lenTime, timeRangeUnit, timeIncrType, genID, probType, lowerProb,
1201 : upperProb, name, comment, unit, convert);
1202 : }
1203 794 : } else if ((templat == GS4_PERCENT_TIME) || (templat == GS4_PERCENT_PNT)) {
1204 0 : ElemNamePerc (mstrVersion, center, subcenter, prodType, templat, cat, subcat,
1205 : lenTime, timeRangeUnit, percentile, name, comment, unit, convert);
1206 : } else {
1207 794 : ElemNameNorm (mstrVersion, center, subcenter, prodType, templat, cat, subcat,
1208 : lenTime, timeRangeUnit, statProcessID, timeIncrType, genID, probType, lowerProb,
1209 : upperProb, name, comment, unit, convert, f_fstValue, fstSurfValue,
1210 : f_sndValue, sndSurfValue);
1211 : }
1212 :
1213 : // https://www.nco.ncep.noaa.gov/pmb/docs/grib2/grib2_doc/grib2_table4-7.shtml
1214 794 : if( derivedFcst == 2 // Standard Deviation with respect to Cluster Mean
1215 794 : || derivedFcst == 3 // Standard Deviation with respect to Cluster Mean, Normalized
1216 794 : || derivedFcst == 4 // Spread of All Members
1217 788 : || derivedFcst == 5 // Large Anomaly Index of All Members
1218 788 : || derivedFcst == 7 // Interquartile Range (Range between the 25th and 75th quantile)
1219 : )
1220 : {
1221 6 : const char* overrideUnit = nullptr;
1222 6 : switch( derivedFcst )
1223 : {
1224 0 : case 2: overrideUnit = "[stddev]"; break;
1225 0 : case 3: overrideUnit = "[stddev normalized]"; break;
1226 6 : case 4: overrideUnit = "[spread]"; break;
1227 0 : case 5: overrideUnit = "[large anomaly index]"; break;
1228 0 : case 7: overrideUnit = "[interquantile range]"; break;
1229 0 : default: break;
1230 : }
1231 6 : if( overrideUnit )
1232 : {
1233 6 : free(*unit);
1234 6 : *unit = (char *) malloc (strlen (overrideUnit) + 1);
1235 6 : strcpy (*unit, overrideUnit);
1236 : }
1237 6 : *convert = UC_NONE;
1238 : }
1239 :
1240 794 : if ((genProcess == 6) || (genProcess == 7)) {
1241 0 : *convert = UC_NONE;
1242 0 : reallocSprintf (name, "ERR");
1243 0 : reallocSprintf (comment, " error %s", *unit);
1244 : } else {
1245 794 : reallocSprintf (comment, " %s", *unit);
1246 : }
1247 794 : }
1248 :
1249 : /*****************************************************************************
1250 : * ParseElemName2() -- Review 12/2002
1251 : *
1252 : * Arthur Taylor / MDL
1253 : *
1254 : * PURPOSE
1255 : * Converts a prodType, template, category and subcategory quadruple to the
1256 : * ASCII string abbreviation of that variable.
1257 : * For example: 0, 0, 0, 0, = "T" for temperature.
1258 : *
1259 : * ARGUMENTS
1260 : * prodType = The GRIB2, section 0 product type. (Input)
1261 : * templat = The GRIB2 section 4 template number. (Input)
1262 : * cat = The GRIB2 section 4 "General category of Product." (Input)
1263 : * subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input)
1264 : * name = Where to store the result (assumed already allocated to at
1265 : * least 15 bytes) (Output)
1266 : * comment = Extra info about variable (assumed already allocated to at
1267 : * least 100 bytes) (Output)
1268 : * unit = What unit this variable is in. (assumed already allocated to at
1269 : * least 20 bytes) (Output)
1270 : *
1271 : * FILES/DATABASES: None
1272 : *
1273 : * RETURNS: char *
1274 : * Same as 'strcpy', i.e. it returns name.
1275 : *
1276 : * HISTORY
1277 : * 9/2002 Arthur Taylor (MDL/RSIS): Created.
1278 : * 11/2002 AAT: Added MOIST_TOT_SNOW (and switched MOIST_SNOWAMT to
1279 : * SnowDepth)
1280 : * 12/2002 (TK,AC,TB,&MS): Code Review.
1281 : * 2/2003 AAT: moved from degrib.c to metaparse.c
1282 : * (Reason: primarily for Sect2 Parsing)
1283 : * (renamed from ElementName to ParseElemName)
1284 : * 4/2003 AAT: Added the comment as a return element.(see GRIB2 discipline)
1285 : * 6/2003 AAT: Added the unit as a return element.
1286 : * 6/2003 AAT: Added Wave Height.
1287 : *
1288 : * NOTES
1289 : * Similar to GRIB1_Table2LookUp... May want to take this and the unit
1290 : * stuff and combine them into a module.
1291 : *****************************************************************************
1292 : */
1293 : /*
1294 : static void ParseElemName2 (int prodType, int templat, int cat, int subcat,
1295 : char *name, char *comment, char *unit)
1296 : {
1297 : if (prodType == 0) {
1298 : if (cat == CAT_TEMP) { * 0 *
1299 : switch (subcat) {
1300 : case TEMP_TEMP: * 0 *
1301 : strcpy (comment, "Temperature [K]");
1302 : strcpy (name, "T");
1303 : strcpy (unit, "[K]");
1304 : return;
1305 : case TEMP_MAXT: * 4 *
1306 : strcpy (comment, "Maximum temperature [K]");
1307 : strcpy (name, "MaxT");
1308 : strcpy (unit, "[K]");
1309 : return;
1310 : case TEMP_MINT: * 5 *
1311 : strcpy (comment, "Minimum temperature [K]");
1312 : strcpy (name, "MinT");
1313 : strcpy (unit, "[K]");
1314 : return;
1315 : case TEMP_DEW_TEMP: * 6 *
1316 : strcpy (comment, "Dew point temperature [K]");
1317 : strcpy (name, "Td");
1318 : strcpy (unit, "[K]");
1319 : return;
1320 : case TEMP_WINDCHILL: * 13 *
1321 : strcpy (comment, "Wind chill factor [K]");
1322 : strcpy (name, "WCI");
1323 : strcpy (unit, "[K]");
1324 : return;
1325 : case TEMP_HEAT: * 12 *
1326 : strcpy (comment, "Heat index [K]");
1327 : strcpy (name, "HeatIndex");
1328 : strcpy (unit, "[K]");
1329 : return;
1330 : }
1331 : } else if (cat == CAT_MOIST) { * 1 *
1332 : switch (subcat) {
1333 : case MOIST_REL_HUMID: * 1 *
1334 : strcpy (comment, "Relative Humidity [%]");
1335 : strcpy (name, "RH");
1336 : strcpy (unit, "[%]");
1337 : return;
1338 : case MOIST_PRECIP_TOT: * 8 *
1339 : if (templat == GS4_PROBABIL_TIME) { * template number 9 implies prob. *
1340 : strcpy (comment, "Prob of 0.01 In. of Precip [%]");
1341 : strcpy (name, "PoP12");
1342 : strcpy (unit, "[%]");
1343 : return;
1344 : } else {
1345 : strcpy (comment, "Total precipitation [kg/(m^2)]");
1346 : strcpy (name, "QPF");
1347 : strcpy (unit, "[kg/(m^2)]");
1348 : return;
1349 : }
1350 : case MOIST_SNOWAMT: * 11 *
1351 : strcpy (comment, "Snow Depth [m]");
1352 : strcpy (name, "SnowDepth");
1353 : strcpy (unit, "[m]");
1354 : return;
1355 : case MOIST_TOT_SNOW: * 29 *
1356 : strcpy (comment, "Total snowfall [m]");
1357 : strcpy (name, "SnowAmt");
1358 : strcpy (unit, "[m]");
1359 : return;
1360 : case 192: * local use moisture. *
1361 : strcpy (comment, "Weather (local use moisture) [-]");
1362 : strcpy (name, "Wx");
1363 : strcpy (unit, "[-]");
1364 : return;
1365 : }
1366 : } else if (cat == CAT_MOMENT) { * 2 *
1367 : switch (subcat) {
1368 : case MOMENT_WINDDIR: * 0 *
1369 : strcpy (comment, "Wind direction (from which blowing) "
1370 : "[deg true]");
1371 : strcpy (name, "WindDir");
1372 : strcpy (unit, "[deg true]");
1373 : return;
1374 : case MOMENT_WINDSPD: * 1 *
1375 : strcpy (comment, "Wind speed [m/s]");
1376 : strcpy (name, "WindSpd");
1377 : strcpy (unit, "[m/s]");
1378 : return;
1379 : }
1380 : } else if (cat == CAT_CLOUD) { * 6 *
1381 : switch (subcat) {
1382 : case CLOUD_COVER: * 1 *
1383 : strcpy (comment, "Total cloud cover [%]");
1384 : strcpy (name, "Sky");
1385 : strcpy (unit, "[%]");
1386 : return;
1387 : }
1388 : } else if (cat == CAT_MOISTURE_PROB) { * 10 *
1389 : if (subcat == 8) { * grandfather'ed in. *
1390 : strcpy (comment, "Prob of 0.01 In. of Precip [%]");
1391 : strcpy (name, "PoP12");
1392 : strcpy (unit, "[%]");
1393 : return;
1394 : }
1395 : }
1396 : } else if (prodType == 10) {
1397 : if (cat == OCEAN_CAT_WAVES) { * 0 *
1398 : if (subcat == OCEAN_WAVE_SIG_HT_WV) { * 5 *
1399 : strcpy (comment, "Significant height of wind waves [m]");
1400 : strcpy (name, "WaveHeight");
1401 : strcpy (unit, "[m]");
1402 : return;
1403 : }
1404 : }
1405 : }
1406 : strcpy (name, "");
1407 : strcpy (comment, "unknown");
1408 : strcpy (unit, "[-]");
1409 : return;
1410 : }
1411 : */
1412 :
1413 : /*****************************************************************************
1414 : * ComputeUnit() --
1415 : *
1416 : * Arthur Taylor / MDL
1417 : *
1418 : * PURPOSE
1419 : * Sets m, and b for equation y = mx + b, where x is in the unit
1420 : * specified by GRIB2, and y is the one specified by f_unit. The default
1421 : * is m = 1, b = 0.
1422 : *
1423 : * Currently:
1424 : * For f_unit = 1 (English) we return Fahrenheit, knots, and inches for
1425 : * temperature, wind speed, and amount of snow or rain. The original units
1426 : * are Kelvin, m/s, kg/m**2.
1427 : * For f_unit = 2 (metric) we return Celsius instead of Kelvin.
1428 : *
1429 : * ARGUMENTS
1430 : * convert = The enumerated type describing the type of conversion. (Input)
1431 : * origName = Original unit name (needed for log10 option) (Input)
1432 : * f_unit = What type of unit to return (see above) (Input).
1433 : * unitM = M in equation y = m x + b (Output)
1434 : * unitB = B in equation y = m x + b (Output)
1435 : * name = Where to store the result (assumed already allocated to at
1436 : * least 15 bytes) (Output)
1437 : *
1438 : * FILES/DATABASES: None
1439 : *
1440 : * RETURNS: int
1441 : * 0 if we set M and B, 1 if we used defaults.
1442 : *
1443 : * HISTORY
1444 : * 1/2004 Arthur Taylor (MDL/RSIS): Re-Created.
1445 : *
1446 : * NOTES
1447 : *****************************************************************************
1448 : */
1449 523 : int ComputeUnit (int convert, char *origName, sChar f_unit, double *unitM,
1450 : double *unitB, char *name)
1451 : {
1452 523 : switch (convert) {
1453 455 : case UC_NONE:
1454 455 : break;
1455 63 : case UC_K2F: /* Convert from Kelvin to F or C. */
1456 63 : if (f_unit == 1) {
1457 0 : strcpy (name, "[F]");
1458 0 : *unitM = 9. / 5.;
1459 : /* 32 - (9/5 * 273.15) = 32 - 491.67 = -459.67. */
1460 0 : *unitB = -459.67;
1461 0 : return 0;
1462 63 : } else if (f_unit == 2) {
1463 61 : strcpy (name, "[C]");
1464 61 : *unitM = 1;
1465 61 : *unitB = -273.15;
1466 61 : return 0;
1467 : }
1468 2 : break;
1469 0 : case UC_InchWater: /* Convert from kg/(m^2) to inches water. */
1470 0 : if (f_unit == 1) {
1471 0 : strcpy (name, "[inch]");
1472 : /*
1473 : * kg/m**2 / density of water (1000 kg/m**3)
1474 : * 1/1000 m * 1/2.54 in/cm * 100 cm/m = 1/25.4 inches
1475 : */
1476 0 : *unitM = 1. / 25.4;
1477 0 : *unitB = 0;
1478 0 : return 0;
1479 : }
1480 0 : break;
1481 0 : case UC_M2Feet: /* Convert from meters to feet. */
1482 0 : if (f_unit == 1) {
1483 : /* 1 (m) * (100cm/m) * (inch/2.54cm) * (ft/12inch) = X (ft) */
1484 0 : strcpy (name, "[feet]");
1485 0 : *unitM = 100. / 30.48;
1486 0 : *unitB = 0;
1487 0 : return 0;
1488 : }
1489 0 : break;
1490 0 : case UC_M2Inch: /* Convert from meters to inches. */
1491 0 : if (f_unit == 1) {
1492 0 : strcpy (name, "[inch]");
1493 0 : *unitM = 100. / 2.54; /* inch / m */
1494 0 : *unitB = 0;
1495 0 : return 0;
1496 : }
1497 0 : break;
1498 5 : case UC_M2StatuteMile: /* Convert from meters to statute miles. */
1499 5 : if (f_unit == 1) {
1500 0 : strcpy (name, "[statute mile]");
1501 0 : *unitM = 1. / 1609.344; /* mile / m */
1502 0 : *unitB = 0;
1503 0 : return 0;
1504 : }
1505 5 : break;
1506 : /* NCEP goes with a convention of 1 nm = 1853.248 m.
1507 : * http://www.sizes.com/units/mile_USnautical.htm Shows that on
1508 : * 7/1/1954 US Department of Commerce switched to 1 nm = 1852 m
1509 : * (International standard.) */
1510 0 : case UC_MS2Knots: /* Convert from m/s to knots. */
1511 0 : if (f_unit == 1) {
1512 0 : strcpy (name, "[knots]");
1513 0 : *unitM = 3600. / 1852.; /* knot / m s**-1 */
1514 0 : *unitB = 0;
1515 0 : return 0;
1516 : }
1517 0 : break;
1518 0 : case UC_UVIndex: /* multiply by Watts/ m**2 by 40 for the UV index. */
1519 0 : if (f_unit == 1) {
1520 0 : strcpy (name, "[UVI]");
1521 0 : *unitM = 40;
1522 0 : *unitB = 0;
1523 0 : return 0;
1524 : }
1525 0 : break;
1526 0 : case UC_LOG10: /* convert from log10 (x) to x */
1527 0 : if ((f_unit == 1) || (f_unit == 2)) {
1528 0 : origName[strlen (origName) - 2] = '\0';
1529 0 : if (strlen (origName) > 21)
1530 0 : origName[21] = '\0';
1531 0 : snprintf (name, 15, "[%s]", origName + 7);
1532 0 : *unitM = -10; /* M = -10 => take 10^(x) */
1533 0 : *unitB = 0;
1534 0 : return 0;
1535 : }
1536 0 : break;
1537 : }
1538 : /* Default case is for the unit in the GRIB2 document. */
1539 462 : strcpy (name, "[GRIB2 unit]");
1540 462 : *unitM = 1;
1541 462 : *unitB = 0;
1542 462 : return 1;
1543 : }
1544 :
1545 : /*****************************************************************************
1546 : * ComputeUnit2() --
1547 : *
1548 : * Arthur Taylor / MDL
1549 : *
1550 : * PURPOSE
1551 : * Sets m, and b for equation y = mx + b, where x is in the unit
1552 : * specified by GRIB2, and y is the one specified by f_unit. The default
1553 : * is m = 1, b = 0.
1554 : *
1555 : * Currently:
1556 : * For f_unit = 1 (English) we return Fahrenheit, knots, and inches for
1557 : * temperature, wind speed, and amount of snow or rain. The original units
1558 : * are Kelvin, m/s, kg/m**2.
1559 : * For f_unit = 2 (metric) we return Celsius instead of Kelvin.
1560 : *
1561 : * ARGUMENTS
1562 : * prodType = The GRIB2, section 0 product type. (Input)
1563 : * templat = The GRIB2 section 4 template number. (Input)
1564 : * cat = The GRIB2 section 4 "General category of Product." (Input)
1565 : * subcat = The GRIB2 section 4 "Specific subcategory of Product". (Input)
1566 : * f_unit = What type of unit to return (see above) (Input).
1567 : * unitM = M in equation y = m x + b (Output)
1568 : * unitB = B in equation y = m x + b (Output)
1569 : * name = Where to store the result (assumed already allocated to at
1570 : * least 15 bytes) (Output)
1571 : *
1572 : * FILES/DATABASES: None
1573 : *
1574 : * RETURNS: int
1575 : * 0 if we set M and B, 1 if we used defaults.
1576 : *
1577 : * HISTORY
1578 : * 11/2002 Arthur Taylor (MDL/RSIS): Created.
1579 : *
1580 : * NOTES
1581 : *****************************************************************************
1582 : */
1583 : /*
1584 : static int ComputeUnit2 (int prodType, int templat, int cat, int subcat,
1585 : sChar f_unit, double *unitM, double *unitB,
1586 : char *name)
1587 : {
1588 : if (prodType == 0) {
1589 : switch (cat) {
1590 : case CAT_TEMP:
1591 : * subcat 8 is K/m, 10, 11 is W/m**2 *
1592 : if ((subcat < 16) && (subcat != 8) &&
1593 : (subcat != 10) && (subcat != 11)) {
1594 : if (f_unit == 1) {
1595 : strcpy (name, "[F]");
1596 : *unitM = 9. / 5.;
1597 : * 32 - (9/5 * 273.15) = 32 - 491.67 = -459.67. *
1598 : *unitB = -459.67;
1599 : return 0;
1600 : } else if (f_unit == 2) {
1601 : strcpy (name, "[C]");
1602 : *unitM = 1;
1603 : *unitB = -273.15;
1604 : return 0;
1605 : }
1606 : }
1607 : break;
1608 : case CAT_MOIST:
1609 : if (subcat == MOIST_PRECIP_TOT) {
1610 : if (templat != 9) { * template number != 9 implies QPF. *
1611 : if (f_unit == 1) {
1612 : strcpy (name, "[inch]");
1613 : *
1614 : * kg/m**2 / density of water (1000 kg/m**3)
1615 : * 1/1000 m * 1/2.54 in/cm * 100 cm/m = 1/25.4 inches
1616 : *
1617 : *unitM = 1. / 25.4;
1618 : *unitB = 0;
1619 : return 0;
1620 : }
1621 : }
1622 : }
1623 : if ((subcat == MOIST_SNOWAMT) || (subcat == MOIST_TOT_SNOW)) {
1624 : if (f_unit == 1) {
1625 : strcpy (name, "[inch]");
1626 : *unitM = 100. / 2.54; * inch / m *
1627 : *unitB = 0;
1628 : return 0;
1629 : }
1630 : }
1631 : break;
1632 : case CAT_MOMENT:
1633 : if (subcat == MOMENT_WINDSPD) {
1634 : if (f_unit == 1) {
1635 : strcpy (name, "[knots]");
1636 : *unitM = 3600. / 1852.; * knot / m s**-1 *
1637 : *unitB = 0;
1638 : return 0;
1639 : }
1640 : }
1641 : break;
1642 : }
1643 : } else if (prodType == 10) {
1644 : if (cat == OCEAN_CAT_WAVES) { * 0 *
1645 : if (subcat == OCEAN_WAVE_SIG_HT_WV) { * 5 *
1646 : if (f_unit == 1) {
1647 : * 1 (m) * (100cm/m) * (inch/2.54cm) * (ft/12inch) = X (ft) *
1648 : strcpy (name, "[feet]");
1649 : *unitM = 100. / 30.48;
1650 : *unitB = 0;
1651 : return 0;
1652 : }
1653 : }
1654 : }
1655 : }
1656 : * Default case is for the unit in the GRIB2 document. *
1657 : strcpy (name, "[GRIB2 unit]");
1658 : *unitM = 1;
1659 : *unitB = 0;
1660 : return 1;
1661 : }
1662 : */
1663 :
1664 : /* GRIB2 Code Table 4.5 */
1665 : /* *INDENT-OFF* */
1666 :
1667 :
1668 : /*****************************************************************************
1669 : * Table45Lookup() --
1670 : *
1671 : * Arthur Taylor / MDL
1672 : *
1673 : * PURPOSE
1674 : * To figure out the entry in the "Surface" table (used for Code Table 4.5)
1675 : *
1676 : * ARGUMENTS
1677 : * code = The original index to look up. (Input)
1678 : * center = Center code (Input)
1679 : * subcenter = Subcenter code (Input)
1680 : * f_reserved = If the index is a "reserved" index (Output)
1681 : * shortName = Pointer to short name of the parameter, or nullptr(Output)
1682 : * name = Pointer to longer name of the parameter, or nullptr (Output)
1683 : * unit = Pointer to unit name, or nullptr (Output)
1684 : * FILES/DATABASES: None
1685 : *
1686 : * RETURNS: TRUE in case of success
1687 :
1688 : * NOTES
1689 : *****************************************************************************
1690 : */
1691 787 : int Table45Lookup (int code,
1692 : uShort2 center,
1693 : uShort2 /* subcenter */,
1694 : int *f_reserved,
1695 : const char** shortName,
1696 : const char** name,
1697 : const char** unit
1698 : )
1699 : {
1700 787 : *f_reserved = 1;
1701 787 : if( shortName )
1702 787 : *shortName = "RESERVED";
1703 787 : if( name )
1704 787 : *name = "Reserved";
1705 787 : if( unit )
1706 787 : *unit = "-";
1707 :
1708 787 : if ((code > 255) || (code < 0)) {
1709 : #ifdef DEBUG
1710 0 : printf ("Surface index is out of 0..255 range?\n");
1711 : #endif
1712 0 : return FALSE;
1713 : }
1714 :
1715 : // Substantially changed by GDAL
1716 787 : *f_reserved = 0;
1717 787 : if( code > 191 && code < 255 && center != 7) {
1718 : // Codes in range [192,254] are reserved for local use.
1719 : // grib2_table_4_5.csv contains the codes valid for NCEP only
1720 : // so for other centers, do not use the .csv file
1721 0 : *f_reserved = 1;
1722 0 : if( shortName )
1723 0 : *shortName = "RESERVED";
1724 0 : if( name )
1725 0 : *name = "Reserved Local use";
1726 0 : if( unit )
1727 0 : *unit = "-";
1728 0 : return TRUE;
1729 : }
1730 :
1731 787 : const char* pszFilename = GetGRIB2_CSVFilename("grib2_table_4_5.csv");
1732 787 : if( pszFilename == nullptr )
1733 : {
1734 0 : CPLError(CE_Failure, CPLE_AppDefined, "Cannot find grib2_table_4_5.csv");
1735 0 : return FALSE;
1736 : }
1737 787 : int iCode = CSVGetFileFieldId(pszFilename,"code");
1738 787 : int iShortName = CSVGetFileFieldId(pszFilename,"short_name");
1739 787 : int iName = CSVGetFileFieldId(pszFilename,"name");
1740 787 : int iUnit = CSVGetFileFieldId(pszFilename,"unit");
1741 787 : if( iCode < 0 || iShortName < 0 || iName < 0 || iUnit < 0 )
1742 : {
1743 0 : CPLError(CE_Failure, CPLE_AppDefined, "Bad structure for %s", pszFilename);
1744 0 : return FALSE;
1745 : }
1746 787 : CSVRewind(pszFilename);
1747 24883 : while( char** papszFields = CSVGetNextLine(pszFilename) )
1748 : {
1749 24883 : if( atoi(papszFields[iCode]) == code )
1750 : {
1751 787 : const char* pszShortName = papszFields[iShortName];
1752 787 : if( code > 191 && code < 255 &&
1753 38 : strcmp(papszFields[iName], "Reserved for local use") == 0 )
1754 : {
1755 35 : pszShortName = "RESERVED";
1756 35 : *f_reserved = 1;
1757 : }
1758 :
1759 787 : if( shortName )
1760 787 : *shortName = pszShortName;
1761 787 : if( name )
1762 787 : *name = papszFields[iName];
1763 787 : if( unit )
1764 787 : *unit = papszFields[iUnit];
1765 787 : return TRUE;
1766 : }
1767 24096 : }
1768 :
1769 0 : return FALSE;
1770 : }
1771 :
1772 787 : void ParseLevelName (unsigned short int center, unsigned short int subcenter,
1773 : uChar surfType, double value, sChar f_sndValue,
1774 : double sndValue, char **shortLevelName,
1775 : char **longLevelName)
1776 : {
1777 : int f_reserved;
1778 : char valBuff[512];
1779 : char sndBuff[512];
1780 787 : const char* surfshortname = nullptr;
1781 787 : const char* surfname = nullptr;
1782 787 : const char* surfunit = nullptr;
1783 787 : Table45Lookup (surfType, center, subcenter,
1784 : &f_reserved, &surfshortname, &surfname, &surfunit);
1785 :
1786 : /* Check if index is defined... 191 is undefined. */
1787 787 : free (*shortLevelName);
1788 787 : *shortLevelName = nullptr;
1789 787 : free (*longLevelName);
1790 787 : *longLevelName = nullptr;
1791 787 : snprintf (valBuff, sizeof(valBuff), "%f", value);
1792 787 : strTrimRight (valBuff, '0');
1793 787 : if (valBuff[strlen (valBuff) - 1] == '.') {
1794 787 : valBuff[strlen (valBuff) - 1] = '\0';
1795 : }
1796 787 : if (f_sndValue) {
1797 5 : snprintf (sndBuff, sizeof(sndBuff), "%f", sndValue);
1798 5 : strTrimRight (sndBuff, '0');
1799 5 : if (sndBuff[strlen (sndBuff) - 1] == '.') {
1800 5 : sndBuff[strlen (sndBuff) - 1] = '\0';
1801 : }
1802 5 : if (f_reserved) {
1803 0 : reallocSprintf (shortLevelName, "%s-%s-%s(%d)", valBuff, sndBuff,
1804 : surfshortname, surfType);
1805 0 : reallocSprintf (longLevelName, "%s-%s[%s] %s(%d) (%s)", valBuff,
1806 : sndBuff, surfunit, surfshortname, surfType,
1807 : surfname);
1808 : } else {
1809 5 : reallocSprintf (shortLevelName, "%s-%s-%s", valBuff, sndBuff,
1810 : surfshortname);
1811 5 : reallocSprintf (longLevelName, "%s-%s[%s] %s=\"%s\"", valBuff,
1812 : sndBuff, surfunit, surfshortname, surfname);
1813 : }
1814 : } else {
1815 782 : if (f_reserved) {
1816 35 : reallocSprintf (shortLevelName, "%s-%s(%d)", valBuff, surfshortname,
1817 : surfType);
1818 35 : reallocSprintf (longLevelName, "%s[%s] %s(%d) (%s)", valBuff,
1819 : surfunit, surfshortname, surfType, surfname);
1820 : } else {
1821 747 : reallocSprintf (shortLevelName, "%s-%s", valBuff, surfshortname);
1822 747 : reallocSprintf (longLevelName, "%s[%s] %s=\"%s\"", valBuff,
1823 : surfunit, surfshortname, surfname);
1824 : }
1825 : }
1826 787 : }
|