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