LCOV - code coverage report
Current view: top level - frmts/grib/degrib/degrib - metaname.cpp (source / functions) Hit Total Coverage
Test: gdal_filtered.info Lines: 279 761 36.7 %
Date: 2025-05-27 16:22:57 Functions: 15 17 88.2 %

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

Generated by: LCOV version 1.14