LCOV - code coverage report
Current view: top level - frmts/grib/degrib/degrib - metaname.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 275 757 36.3 %
Date: 2024-04-27 17:22:41 Functions: 14 16 87.5 %

          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 : }

Generated by: LCOV version 1.14