1 /*****************************************************************************
2  * metaprint.c
3  *
4  * DESCRIPTION
5  *    This file contains the code necessary to write out the meta data
6  * structure.
7  *
8  * HISTORY
9  *    9/2002 Arthur Taylor (MDL / RSIS): Created.
10  *
11  * NOTES
12  * 1) Need to add support for GS3_ORTHOGRAPHIC = 90,
13  *    GS3_EQUATOR_EQUIDIST = 110, GS3_AZIMUTH_RANGE = 120
14  * 2) Need to add support for GS4_RADAR = 20, GS4_SATELLITE = 30
15  *****************************************************************************
16  */
17 #include <stdio.h>
18 #include <stdlib.h>
19 #include <stdarg.h>
20 #include <math.h>
21 #ifndef DONT_DEPRECATE_SPRINTF
22 #define DONT_DEPRECATE_SPRINTF
23 #endif
24 #include "cpl_port.h"
25 #include "meta.h"
26 #include "metaname.h"
27 #include "myerror.h"
28 #include "myutil.h"
29 #if 0
30 /* tdlpack is no longer supported by GDAL */
31 #include "tdlpack.h"
32 #endif
33 #include "myassert.h"
34 #include "clock.h"
35 
36 /*****************************************************************************
37  * Lookup() --
38  *
39  * Arthur Taylor / MDL
40  *
41  * PURPOSE
42  *   To lookup the string value in a table, given the table, the index, and
43  * some default values.
44  *
45  * ARGUMENTS
46  * table = The table to look in. (Input)
47  *     n = Size of table. (Input)
48  * index = Index to look up. (Input)
49  *
50  * FILES/DATABASES: None
51  *
52  * RETURNS: char *
53  *   The desired index value, or the appropriate default message.
54  *
55  * HISTORY
56  *   9/2002 Arthur Taylor (MDL/RSIS): Created.
57  *
58  * NOTES
59  * Can not do a sizeof(table) here because table is now of arbitrary length.
60  * Instead do sizeof(table) in calling procedure.
61  *****************************************************************************
62  */
63 #if 0  // Unused with GDAL.
64 static const char *Lookup(const char * const *table, size_t n, size_t index)
65 {
66    static const char * const def[] =
67     { "Reserved", "Reserved for local use", "Missing" };
68    if (index < (n / sizeof (char *))) {
69       return table[index];
70    } else if (index < 192) {
71       return def[0];
72    } else if (index < 255) {
73       return def[1];
74    } else {
75       return def[2];
76    }
77 }
78 #endif
79 
80 /*****************************************************************************
81  * Print() --
82  *
83  * Arthur Taylor / MDL
84  *
85  * PURPOSE
86  *   To print the message to a local static array in a way similar to
87  * myerror.c::errSprintf.  This allows us to pass the results back to Tcl/Tk,
88  * as well as to save it to disk.  It also serves as a central place to
89  * change if we want a different style of output.
90  *
91  *   The caller gives a series of calls with fmt != NULL, followed by
92  * a fmt == NULL.  This last call will return the constructed message to the
93  * caller, and reset the message to NULL.  It is caller's responsibility to
94  * free the message, and to make sure that last call to Print has fmt = NULL,
95  * so that the routine doesn't accidentally keep memory.
96  *
97  * ARGUMENTS
98  *   label = A label for this set of data. (Input)
99  * varName = A char string describing the variable.. (Input)
100  *     fmt = Prt_NULL, Prt_D, Prt_DS, Prt_DSS, Prt_S, Prt_F, Prt_FS, Prt_E,
101  *           Prt_ES. (Input)
102  *           determines what to expect in the rest of the arguments.
103  *           d = sInt4, s = char *, f = double,
104  *           NULL = return the constructed answer, and reset answer to NULL.
105  *
106  * FILES/DATABASES: None
107  *
108  * RETURNS: char *
109  *   NULL if (fmt != NULL) (i.e. we added to message)
110  *   message if (fmt == NULL) (i.e. return the message).
111  *       It is caller's responsibility to free the message, and to make sure
112  *       that last call to Print has fmt = NULL.
113  *
114  * HISTORY
115  *   9/2002 Arthur Taylor (MDL/RSIS): Created.
116  *   4/2003 AAT: Changed so it could print different types of labels
117  *               "GDS" instead of "S3"
118  *  10/2004 AAT: Added Prt_SS
119  *
120  * NOTES
121  * Using enumerated type instead of "ds" "dss" etc.  For speed considerations.
122  *****************************************************************************
123  */
Print(const char * label,const char * varName,int fmt,...)124 char *Print(const char *label, const char *varName, int fmt, ...)
125 {
126    static char *buffer = nullptr; /* Copy of message generated so far. */
127    va_list ap;          /* pointer to variable argument list. */
128    sInt4 lival;         /* Store a sInt4 val from argument list. */
129    char *sval;          /* Store a string val from argument. */
130    char *unit;          /* Second string val is usually a unit string. */
131    double dval;         /* Store a double val from argument list. */
132    char *ans;           /* Final message to return if fmt = Prt_NULL. */
133 
134    if (fmt == Prt_NULL) {
135       ans = buffer;
136       buffer = nullptr;
137       return ans;
138    }
139    va_start (ap, fmt);  /* make ap point to 1st unnamed arg. */
140    switch (fmt) {
141       case Prt_D:
142          lival = va_arg (ap, sInt4);
143          reallocSprintf (&buffer, "%s | %s | %ld\n", label, varName, lival);
144          break;
145       case Prt_DS:
146          lival = va_arg (ap, sInt4);
147          sval = va_arg (ap, char *);
148          reallocSprintf (&buffer, "%s | %s | %ld (%s)\n", label, varName,
149                          lival, sval);
150          break;
151       case Prt_DSS:
152          lival = va_arg (ap, sInt4);
153          sval = va_arg (ap, char *);
154          unit = va_arg (ap, char *);
155          reallocSprintf (&buffer, "%s | %s | %ld (%s [%s])\n", label,
156                          varName, lival, sval, unit);
157          break;
158       case Prt_S:
159          sval = va_arg (ap, char *);
160          reallocSprintf (&buffer, "%s | %s | %s\n", label, varName, sval);
161          break;
162       case Prt_SS:
163          sval = va_arg (ap, char *);
164          unit = va_arg (ap, char *);
165          reallocSprintf (&buffer, "%s | %s | %s (%s)\n", label, varName,
166                          sval, unit);
167          break;
168       case Prt_F:
169          dval = va_arg (ap, double);
170          reallocSprintf (&buffer, "%s | %s | %f\n", label, varName, dval);
171          break;
172       case Prt_E:
173          dval = va_arg (ap, double);
174          reallocSprintf (&buffer, "%s | %s | %e\n", label, varName, dval);
175          break;
176       case Prt_G:
177          dval = va_arg (ap, double);
178          reallocSprintf (&buffer, "%s | %s | %g\n", label, varName, dval);
179          break;
180       case Prt_FS:
181          dval = va_arg (ap, double);
182          unit = va_arg (ap, char *);
183          reallocSprintf (&buffer, "%s | %s | %f (%s)\n", label, varName,
184                          dval, unit);
185          break;
186       case Prt_ES:
187          dval = va_arg (ap, double);
188          unit = va_arg (ap, char *);
189          reallocSprintf (&buffer, "%s | %s | %e (%s)\n", label, varName,
190                          dval, unit);
191          break;
192       case Prt_GS:
193          dval = va_arg (ap, double);
194          unit = va_arg (ap, char *);
195          reallocSprintf (&buffer, "%s | %s | %g (%s)\n", label, varName,
196                          dval, unit);
197          break;
198       default:
199          reallocSprintf (&buffer, "ERROR: Invalid Print option '%d'\n", fmt);
200    }
201    va_end (ap);         /* clean up when done. */
202    return nullptr;
203 }
204 
205 #if 0  // Unused with GDAL.
206 /*****************************************************************************
207  * PrintSect1() --
208  *
209  * Arthur Taylor / MDL
210  *
211  * PURPOSE
212  *   To generate the message for GRIB2 section 1.
213  *
214  * ARGUMENTS
215  *      pds2 = The GRIB2 Product Definition Section to print. (Input)
216  *    center = The Center that created the data (Input)
217  * subcenter = The Sub Center that created the data (Input)
218  *
219  * FILES/DATABASES: None
220  *
221  * RETURNS: void
222  *
223  * HISTORY
224  *   9/2002 Arthur Taylor (MDL/RSIS): Created.
225  *   4/2003 AAT: Changed to accept pointer to pdsG2Type pds2
226  *  10/2005 AAT: Adjusted to take center, subcenter as we moved that out of
227  *               the pdsG2 type.
228  *
229  * NOTES
230  *****************************************************************************
231  */
232 static void PrintSect1 (pdsG2Type * pds2, unsigned short int center,
233                         unsigned short int subcenter)
234 {
235    /* Based on Grib2 Code Table 1.2 */
236    static const char * const table12[] = { "Analysis", "Start of Forecast",
237       "Verifying time of forecast", "Observation time"
238    };
239 
240    /* Based on Grib2 Code Table 1.3 */
241    static const char * const table13[] = { "Operational products",
242       "Operational test products", "Research products",
243       "Re-analysis products"
244    };
245 
246    /* Based on Grib2 Code Table 1.4 */
247    static const char * const table14[] = { "Analysis products",
248       "Forecast products", "Analysis and forecast products",
249       "Control forecast products", "Perturbed forecast products",
250       "Control and perturbed forecast products",
251       "Processed satellite observations", "Processed radar observations"
252    };
253 
254    char buffer[25];     /* Stores format of pds2->refTime. */
255    const char *ptr;
256 
257    ptr = centerLookup (center);
258    if (ptr != NULL) {
259       Print ("PDS-S1", "Originating center", Prt_DS, center, ptr);
260    } else {
261       Print ("PDS-S1", "Originating center", Prt_D, center);
262    }
263    if (subcenter != GRIB2MISSING_u2) {
264       ptr = subCenterLookup (center, subcenter);
265       if (ptr != NULL) {
266          Print ("PDS-S1", "Originating sub-center", Prt_DS, subcenter, ptr);
267       } else {
268          Print ("PDS-S1", "Originating sub-center", Prt_D, subcenter);
269       }
270    }
271    Print ("PDS-S1", "GRIB Master Tables Version", Prt_D, pds2->mstrVersion);
272    Print ("PDS-S1", "GRIB Local Tables Version", Prt_D, pds2->lclVersion);
273    Print ("PDS-S1", "Significance of reference time", Prt_DS, pds2->sigTime,
274           Lookup (table12, sizeof (table12), pds2->sigTime));
275 
276 /*   strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC", gmtime (&(pds2->refTime)));*/
277    Clock_Print (buffer, 25, pds2->refTime, "%m/%d/%Y %H:%M:%S UTC", 0);
278 
279    Print ("PDS-S1", "Reference Time", Prt_S, buffer);
280    Print ("PDS-S1", "Operational Status", Prt_DS, pds2->operStatus,
281           Lookup (table13, sizeof (table13), pds2->operStatus));
282    Print ("PDS-S1", "Type of Data", Prt_DS, pds2->dataType,
283           Lookup (table14, sizeof (table14), pds2->dataType));
284 }
285 
286 /*****************************************************************************
287  * PrintSect2() --
288  *
289  * Arthur Taylor / MDL
290  *
291  * PURPOSE
292  *   To generate a message for the section 2 data.  This may be more
293  * appropriate in its own file (particularly the weather table.)
294  *
295  * ARGUMENTS
296  * sect2 = The sect2 structure to print (initialized by ParseSect2). (Input)
297  *
298  * FILES/DATABASES: None
299  *
300  * RETURNS: void
301  *
302  * HISTORY
303  *   2/2003 Arthur Taylor (MDL/RSIS): Created.
304  *
305  * NOTES
306  *****************************************************************************
307  */
308 static void PrintSect2 (sect2_type * sect2)
309 {
310    size_t i;            /* loop counter over number of sect2 data. */
311    char buffer[25];     /* Assists with labeling. */
312 
313    switch (sect2->ptrType) {
314       case GS2_WXTYPE:
315          Print ("PDS-S2", "Number of Elements in Section 2", Prt_D,
316                 sect2->wx.dataLen);
317          for (i = 0; i < sect2->wx.dataLen; i++) {
318             if (sect2->wx.ugly[i].validIndex != -1) {
319                sprintf (buffer, "Elem %3d  Is Used", (int) i);
320             } else {
321                sprintf (buffer, "Elem %3d NOT Used", (int) i);
322             }
323             Print ("PDS-S2", buffer, Prt_S, sect2->wx.data[i]);
324          }
325          break;
326       case GS2_UNKNOWN:
327          Print ("PDS-S2", "Number of Elements in Section 2", Prt_D,
328                 sect2->unknown.dataLen);
329          for (i = 0; i < sect2->unknown.dataLen; i++) {
330             sprintf (buffer, "Element %d", (int) i);
331             Print ("PDS-S2", buffer, Prt_F, sect2->unknown.data[i]);
332          }
333          break;
334       default:
335          return;
336    }
337 }
338 
339 /*****************************************************************************
340  * PrintSect4_Category() --
341  *
342  * Arthur Taylor / MDL
343  *
344  * PURPOSE
345  *   To generate the category message for section 4.
346  *
347  * ARGUMENTS
348  * meta = The meta file structure to generate the message for. (Input)
349  *
350  * FILES/DATABASES: None
351  *
352  * RETURNS: void
353  *
354  * HISTORY
355  *   6/2003 Arthur Taylor (MDL/RSIS): Extracted this part from PrintSect4().
356  *   1/2004 AAT: Combined PrintSect4_Meteo and PrintSect4_Ocean into this.
357  *   5/2004 AAT: Found out that I skipped "Momentum Probabilities" in tbl41_0.
358  *
359  * NOTES
360  *****************************************************************************
361  */
362 static void PrintSect4_Category_OAR (uChar cat) {
363    if (cat == 2) {
364       Print ("PDS-S4", "Category Description", Prt_DS, cat, "Lightning Products");
365    } else if (cat == 3) {
366       Print ("PDS-S4", "Category Description", Prt_DS, cat, "Severe Weather Products");
367    } else if (cat == 4) {
368       Print ("PDS-S4", "Category Description", Prt_DS, cat, "Satellite Products");
369    } else if (cat == 5) {
370       Print ("PDS-S4", "Category Description", Prt_DS, cat, "Forecast Products");
371    } else if (cat == 6) {
372       Print ("PDS-S4", "Category Description", Prt_DS, cat, "Precipitation Products");
373    } else if (cat == 7) {
374       Print ("PDS-S4", "Category Description", Prt_DS, cat, "Model-based Products");
375    } else if (cat == 8) {
376       Print ("PDS-S4", "Category Description", Prt_DS, cat, "Intermediate Products");
377    } else if (cat == 9) {
378       Print ("PDS-S4", "Category Description", Prt_DS, cat, "3D Reflectivity Mosaics");
379    } else if (cat == 10) {
380       Print ("PDS-S4", "Category Description", Prt_DS, cat, "Composite Reflectivity Mosaics");
381    } else if (cat == 11) {
382       Print ("PDS-S4", "Category Description", Prt_DS, cat, "Other Reflectivity Mosaics");
383    } else  {
384       Print ("PDS-S4", "Category Description", Prt_DS, cat, "unknown");
385    }
386 }
387 
388 static void PrintSect4_Category (grib_MetaData *meta)
389 {
390    sect4_type *sect4 = &(meta->pds2.sect4);
391 
392    /* Based on Grib2 Code Table 4.1 discipline 0 */
393    static const char * const tbl41_0[] = {
394       "Temperature", "Moisture", "Momentum", "Mass", "Short-wave Radiation",
395       "Long-wave Radiation", "Cloud", "Thermodynamic Stability indices",
396       "Kinematic Stability indices", "Temperature Probabilities",
397       "Moisture Probabilities", "Momentum Probabilities",
398       "Mass Probabilities", "Aerosols", "Trace gases (e.g. ozone, C02)",
399       "Radar", "Forecast Radar Imagery", "Electro-dynamics",
400       "Nuclear/radiology", "Physical atmospheric properties",
401       "Atmospheric chemical Constituents",
402    };
403    /* Based on Grib2 Code Table 4.1 discipline 1 */
404    static const char * const tbl41_1[] = {
405       "Hydrology basic products", "Hydrology probabilities"
406    };
407    /* Based on Grib2 Code Table 4.1 discipline 2 */
408    static const char * const tbl41_2[] = {
409       "Vegetation/Biomass", "Agri-/aquacultural Special Products",
410       "Transportation-related Products", "Soil Products"
411    };
412    /* Based on Grib2 Code Table 4.1 discipline 3 */
413    static const char * const tbl41_3[] = {
414       "Image format products", "Quantitative products"
415    };
416    /* Based on Grib2 Code Table 4.1 discipline 10 */
417    static const char * const tbl41_10[] = {
418       "Waves", "Currents", "Ice", "Surface Properties",
419       "Sub-surface Properties"
420    };
421 
422    if (meta->pds2.mstrVersion == 255) {
423       if (meta->center == 161) {
424          PrintSect4_Category_OAR(sect4->cat);
425       } else {
426          Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat, "unknown");
427       }
428       return;
429    }
430    switch (meta->pds2.prodType) {
431       case 0:          /* Meteo category. */
432          switch (sect4->cat) {
433             case 190:
434                Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat,
435                       "CCITT IA5 string");
436                break;
437             case 191:
438                Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat,
439                       "Miscellaneous");
440                break;
441             case 192:
442                Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat,
443                       "Covariance");
444                break;
445             default:
446                Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat,
447                       Lookup (tbl41_0, sizeof (tbl41_0), sect4->cat));
448          }
449          break;
450       case 1:          /* Hydrological */
451          Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat,
452                 Lookup (tbl41_1, sizeof (tbl41_1), sect4->cat));
453          break;
454       case 2:          /* Land surface */
455          Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat,
456                 Lookup (tbl41_2, sizeof (tbl41_2), sect4->cat));
457          break;
458       case 3:          /* Space */
459          switch (sect4->cat) {
460             case 192:
461                Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat,
462                       "Forecast Satellite Imagery");
463                break;
464             default:
465                Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat,
466                       Lookup (tbl41_3, sizeof (tbl41_3), sect4->cat));
467          }
468          break;
469       case 10:         /* Oceanographic */
470          switch (sect4->cat) {
471             case 191:
472                Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat,
473                       "Miscellaneous");
474                break;
475             default:
476                Print ("PDS-S4", "Category Description", Prt_DS, sect4->cat,
477                       Lookup (tbl41_10, sizeof (tbl41_10), sect4->cat));
478          }
479          break;
480       default:
481          Print ("PDS-S4", "PrintSect4() does not handle this prodType",
482                 Prt_D, meta->pds2.prodType);
483    }
484 }
485 
486 /*****************************************************************************
487  * PrintSect4() --
488  *
489  * Arthur Taylor / MDL
490  *
491  * PURPOSE
492  *   To generate the message for section 4.
493  *
494  * ARGUMENTS
495  *   meta = The meta file structure to generate the message for. (Input)
496  * f_unit = 0 (GRIB unit), 1 (english), 2 (metric) (Input)
497  *
498  * FILES/DATABASES: None
499  *
500  * RETURNS: int (could use errSprintf())
501  *  0 if no error.
502  * -2 if asked to print data for a template that we don't support.
503  *
504  * HISTORY
505  *   9/2002 Arthur Taylor (MDL/RSIS): Created.
506  *   2/2003 AAT: Adjusted the interpretation of the scale vector and value.
507  *          to be consistent with what Matt found from email conversations
508  *          with WMO GRIB2 experts.
509  *   2/2003 AAT: Switched from: value / pow (10, factor)
510  *                          to: value * pow (10, -1 * factor)
511  *   6/2003 AAT: Extracted the prodType = 0 only info and put in _Meteo
512  *   6/2003 AAT: Created a PrintSect4_Ocean for ocean stuff.
513  *   1/2004 AAT: Updated table 4.7
514  *   1/2004 AAT: Modified to use "comment" from metaname.c instead of
515  *          PrintSect4_Meteo, and PrintSect4_Ocean (that way local tables are
516  *          enabled.) (Fixed meta file for PoP12).
517  *   3/2004 AAT: Added emphasis on "computation" of unit conversion.
518  *   3/2005 AAT: Added support for GS4_PROBABIL_PNT
519  *
520  * NOTES
521  * Need to add support for GS4_RADAR = 20
522  *****************************************************************************
523  */
524 static int PrintSect4 (grib_MetaData *meta, sChar f_unit)
525 {
526    sect4_type *sect4 = &(meta->pds2.sect4);
527    /* Based on Grib2 Code Table 4.0 */
528    static const char * const tbl40[] = {
529       "Analysis at a horizontal layer at a point in time",
530       "Individual ensemble forecast at a horizontal layer at a point in time",
531       "Derived forecast based on ensemble members at a horizontal layer at a"
532             " point in time",
533       "Probability forecast at a horizontal layer or level at a point in "
534             "time",
535       "Percentile forecasts at a horizontal layer or level at a point in "
536             "time",
537       "Statistically processed data at a horizontal layer or level in a time"
538             " interval",
539       "Probability forecast at a horizontal layer or level in a time "
540             "interval",
541       "Percentile forecasts at a horizontal layer or level in a time "
542             "interval",
543       "Individual ensemble forecast at a horizontal layer or level in a time"
544             " interval",
545       "Derived forecasts based in all ensemble members at a horizontal level "
546             "or layer in a time interval",
547       "Radar product", "Satellite product"
548    };
549 
550    /* Based on Grib2 Code Table 4.3 */
551    static const char * const tbl43[] = {
552       "Analysis", "Initialization", "Forecast", "Bias corrected forecast",
553       "Ensemble forecast", "Probability forecast", "Forecast error",
554       "Analysis error", "Observation"
555    };
556 
557    /* Based on Grib2 Code Table 4.4 */
558    static const char * const tbl44[] = {
559       "Minute", "Hour", "Day", "Month", "Year", "Decade",
560       "Normal (30 years)", "Century", "Reserved", "Reserved",
561       "3 hours", "6 hours", "12 hours", "Second"
562    };
563 
564    /* Based on Grib2 Code Table 4.5 */
565    /* See "metaname.c :: Surface[]" */
566 
567    /* Based on Grib2 Code Table 4.6 */
568    static const char * const tbl46[] = {
569       "Unperturbed high-resolution control forecast",
570       "Unperturbed low-resolution control forecast",
571       "Negatively perturbed forecast", "Positively perturbed forecast"
572    };
573 
574    /* Based on Grib2 Code Table 4.7 */
575    static const char * const tbl47[] = {
576       "Unweighted mean of all members", "Weighted mean of all members",
577       "Standard deviation with respect to cluster mean",
578       "Standard deviation with respect to cluster mean, normalized",
579       "Spread of all members",
580       "Large anomaly index of all members",
581       "Unweighted mean of the cluster members"
582    };
583 
584    /* Based on Grib2 Code Table 4.9 */
585    static const char * const tbl49[] = {
586       "Probability of event below lower limit",
587       "Probability of event above upper limit",
588       "Probability of event between limits (include lower, exclude upper)",
589       "Probability of event above lower limit",
590       "Probability of event below upper limit"
591    };
592 
593    /* Based on Grib2 Code Table 4.10 */
594    static const char * const tbl410[] = {
595       "Average", "Accumulation", "Maximum", "Minimum",
596       "Difference (Value at end of time minus beginning)",
597       "Root mean square", "Standard deviation",
598       "Covariance (Temporal variance)",
599       "Difference (Value at beginning of time minus end)", "Ratio",
600       "Standardized Anomaly", "Summation", "Confidence Index",
601       "Quality Indicator"
602    };
603 
604    /* Based on Grib2 Code Table 4.11 */
605    static const char * const tbl411[] = {
606       "Reserved",
607       "Successive times; same forecast time, start time incremented",
608       "Successive times; same start time, forecast time incremented",
609       "Successive times; start time incremented, forecast time decremented, "
610             "valid time constant",
611       "Successive times; start time decremented, forecast time incremented, "
612             "valid time constant",
613       "Floating subinterval of time between forecast time, and end"
614    };
615 
616    char buffer[50];     /* Temp storage for various uses including time
617                          * format. */
618    int i;               /* counter for templat 4.8/4.9 for num time range
619                          * specs. */
620    int f_reserved;      /* Whether Table4.5 is a reserved entry or not. */
621    GRIB2SurfTable surf; /* Surface look up in Table4.5. */
622    const char *ptr;
623 
624    switch (sect4->templat) {
625       case GS4_ANALYSIS:
626       case GS4_ERROR:
627       case GS4_ENSEMBLE:
628       case GS4_DERIVED:
629          Print ("PDS-S4", "Product type", Prt_DS, sect4->templat,
630                 tbl40[sect4->templat]);
631          break;
632       case GS4_PROBABIL_PNT:
633          Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[3]);
634          break;
635       case GS4_PERCENT_PNT:
636          Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[4]);
637          break;
638       case GS4_STATISTIC:
639          Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[5]);
640          break;
641       case GS4_PROBABIL_TIME:
642          Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[6]);
643          break;
644       case GS4_PERCENT_TIME:
645          Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[7]);
646          break;
647       case GS4_ENSEMBLE_STAT:
648          Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[8]);
649          break;
650       case GS4_DERIVED_INTERVAL:
651          Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[9]);
652          break;
653 /*
654  * The following lines were removed until such time that the rest of this
655  * procedure can properly handle this template type.
656  *
657       case GS4_RADAR:
658          Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[10]);
659          break;
660 */
661       case GS4_SATELLITE:
662          Print ("PDS-S4", "Product type", Prt_DS, sect4->templat, tbl40[11]);
663          break;
664       default:
665          Print ("PDS-S4", "Product type", Prt_D, sect4->templat);
666          errSprintf ("Un-supported Sect4 template %ld\n", sect4->templat);
667          return -2;
668    }
669 
670    PrintSect4_Category (meta);
671    Print ("PDS-S4", "Category Sub-Description", Prt_DS, sect4->subcat,
672           meta->comment);
673 
674    if (f_unit == 1) {
675       Print ("PDS-S4", "Output grid, (COMPUTED) english unit is", Prt_S,
676              meta->unitName);
677    } else if (f_unit == 2) {
678       Print ("PDS-S4", "Output grid, (COMPUTED) metric unit is", Prt_S,
679              meta->unitName);
680    }
681    Print ("PDS-S4", "Generation process", Prt_DS, sect4->genProcess,
682           Lookup (tbl43, sizeof (tbl43), sect4->genProcess));
683    if (sect4->templat == GS4_SATELLITE) {
684       Print ("PDS-S4", "Observation generating process", Prt_D, sect4->genID);
685       Print ("PDS-S4", "Number of contributing spectral bands", Prt_D,
686              sect4->numBands);
687       for (i = 0; i < sect4->numBands; i++) {
688          Print ("PDS-S4", "Satellite series", Prt_D, sect4->bands[i].series);
689          Print ("PDS-S4", "Satellite numbers", Prt_D,
690                 sect4->bands[i].numbers);
691          Print ("PDS-S4", "Instrument type", Prt_D, sect4->bands[i].instType);
692          Print ("PDS-S4", "Scale Factor of central wave number", Prt_D,
693                 sect4->bands[i].centWaveNum.factor);
694          Print ("PDS-S4", "Scale Value of central wave number", Prt_D,
695                 sect4->bands[i].centWaveNum.value);
696       }
697       return 0;
698    }
699    if (sect4->bgGenID != GRIB2MISSING_u1) {
700       ptr = processLookup (meta->center, sect4->bgGenID);
701       if (ptr != NULL) {
702          Print ("PDS-S4", "Background generating process ID", Prt_DS,
703                 sect4->bgGenID, ptr);
704       } else {
705          Print ("PDS-S4", "Background generating process ID", Prt_D,
706                 sect4->bgGenID);
707       }
708    }
709    if (sect4->genID != GRIB2MISSING_u1) {
710       ptr = processLookup (meta->center, sect4->genID);
711       if (ptr != NULL) {
712          Print ("PDS-S4", "Forecast generating process ID", Prt_DS,
713                 sect4->genID, ptr);
714       } else {
715          Print ("PDS-S4", "Forecast generating process ID", Prt_D,
716                 sect4->genID);
717       }
718    }
719    if (sect4->f_validCutOff) {
720       Print ("PDS-S4", "Data cut off after reference time in seconds", Prt_D,
721              sect4->cutOff);
722    }
723    Print ("PDS-S4", "Forecast time in hours", Prt_F,
724           (double) (sect4->foreSec / 3600.));
725    surf = Table45Index (sect4->fstSurfType, &f_reserved, meta->center,
726                         meta->subcenter);
727    Print ("PDS-S4", "Type of first fixed surface", Prt_DSS,
728           sect4->fstSurfType, surf.comment, surf.unit);
729    Print ("PDS-S4", "Value of first fixed surface", Prt_F,
730           sect4->fstSurfValue);
731    if (sect4->sndSurfType != GRIB2MISSING_u1) {
732       surf = Table45Index (sect4->sndSurfType, &f_reserved, meta->center,
733                            meta->subcenter);
734       Print ("PDS-S4", "Type of second fixed surface", Prt_DSS,
735              sect4->sndSurfType, surf.comment, surf.unit);
736       Print ("PDS-S4", "Value of second fixed surface", Prt_F,
737              sect4->sndSurfValue);
738    }
739    switch (sect4->templat) {
740       case GS4_ANALYSIS:
741       case GS4_ERROR:
742          break;
743       case GS4_ENSEMBLE:
744          Print ("PDS-S4", "Type of Ensemble forecast", Prt_DS,
745                 sect4->typeEnsemble,
746                 Lookup (tbl46, sizeof (tbl46), sect4->typeEnsemble));
747          Print ("PDS-S4", "Perturbation number", Prt_D, sect4->perturbNum);
748          Print ("PDS-S4", "Number of forecasts in ensemble", Prt_D,
749                 sect4->numberFcsts);
750          break;
751       case GS4_ENSEMBLE_STAT:
752          Print ("PDS-S4", "Type of Ensemble forecast", Prt_DS,
753                 sect4->typeEnsemble,
754                 Lookup (tbl46, sizeof (tbl46), sect4->typeEnsemble));
755          Print ("PDS-S4", "Perturbation number", Prt_D, sect4->perturbNum);
756          Print ("PDS-S4", "Number of forecasts in ensemble", Prt_D,
757                 sect4->numberFcsts);
758          Clock_Print (buffer, 100, sect4->validTime, "%m/%d/%Y %H:%M:%S UTC",
759                       0);
760          Print ("PDS-S4", "End of overall time interval", Prt_S, buffer);
761          Print ("PDS-S4", "Total number of missing values", Prt_D,
762                 sect4->numMissing);
763          Print ("PDS-S4", "Number of time range specifications", Prt_D,
764                 sect4->numInterval);
765          for (i = 0; i < sect4->numInterval; i++) {
766             Print ("PDS-S4", "Interval number", Prt_D, i + 1);
767             Print ("PDS-S4", "Statistical process", Prt_DS,
768                    sect4->Interval[i].processID,
769                    Lookup (tbl410, sizeof (tbl410),
770                            sect4->Interval[i].processID));
771             Print ("PDS-S4", "Type of time increment", Prt_DS,
772                    sect4->Interval[i].incrType,
773                    Lookup (tbl411, sizeof (tbl411),
774                            sect4->Interval[i].incrType));
775             /* Following is so we get "# str" not "# (str)" */
776             sprintf (buffer, "%d %s", sect4->Interval[i].lenTime,
777                      Lookup (tbl44, sizeof (tbl44),
778                              sect4->Interval[i].timeRangeUnit));
779             Print ("PDS-S4", "Time range for processing", Prt_S, buffer);
780             /* Following is so we get "# str" not "# (str)" */
781             sprintf (buffer, "%d %s", sect4->Interval[i].timeIncr,
782                      Lookup (tbl44, sizeof (tbl44),
783                              sect4->Interval[i].incrUnit));
784             Print ("PDS-S4", "Time increment", Prt_S, buffer);
785          }
786          break;
787       case GS4_DERIVED:
788          Print ("PDS-S4", "Derived forecast", Prt_DS, sect4->derivedFcst,
789                 Lookup (tbl47, sizeof (tbl47), sect4->derivedFcst));
790          Print ("PDS-S4", "Number of forecasts in ensemble", Prt_D,
791                 sect4->numberFcsts);
792          break;
793       case GS4_DERIVED_INTERVAL:
794          Print ("PDS-S4", "Derived forecast", Prt_DS, sect4->derivedFcst,
795                 Lookup (tbl47, sizeof (tbl47), sect4->derivedFcst));
796          Print ("PDS-S4", "Number of forecasts in ensemble", Prt_D,
797                 sect4->numberFcsts);
798          Clock_Print (buffer, 100, sect4->validTime, "%m/%d/%Y %H:%M:%S UTC",
799                       0);
800 
801          Print ("PDS-S4", "End of overall time interval", Prt_S, buffer);
802          Print ("PDS-S4", "Total number of missing values", Prt_D,
803                 sect4->numMissing);
804          Print ("PDS-S4", "Number of time range specifications", Prt_D,
805                 sect4->numInterval);
806          for (i = 0; i < sect4->numInterval; i++) {
807             Print ("PDS-S4", "Interval number", Prt_D, i + 1);
808             Print ("PDS-S4", "Statistical process", Prt_DS,
809                    sect4->Interval[i].processID,
810                    Lookup (tbl410, sizeof (tbl410),
811                            sect4->Interval[i].processID));
812             Print ("PDS-S4", "Type of time increment", Prt_DS,
813                    sect4->Interval[i].incrType,
814                    Lookup (tbl411, sizeof (tbl411),
815                            sect4->Interval[i].incrType));
816             /* Following is so we get "# str" not "# (str)" */
817             sprintf (buffer, "%d %s", sect4->Interval[i].lenTime,
818                      Lookup (tbl44, sizeof (tbl44),
819                              sect4->Interval[i].timeRangeUnit));
820             Print ("PDS-S4", "Time range for processing", Prt_S, buffer);
821             /* Following is so we get "# str" not "# (str)" */
822             sprintf (buffer, "%d %s", sect4->Interval[i].timeIncr,
823                      Lookup (tbl44, sizeof (tbl44),
824                              sect4->Interval[i].incrUnit));
825             Print ("PDS-S4", "Time increment", Prt_S, buffer);
826          }
827          break;
828       case GS4_PROBABIL_PNT:
829          Print ("PDS-S4", "Forecast Probability Number", Prt_D,
830                 sect4->foreProbNum);
831          Print ("PDS-S4", "Total Number of Forecast Probabilities", Prt_D,
832                 sect4->numForeProbs);
833          Print ("PDS-S4", "Probability type", Prt_DS, sect4->probType,
834                 Lookup (tbl49, sizeof (tbl49), sect4->probType));
835          sprintf (buffer, "%d, %d", sect4->lowerLimit.value,
836                   sect4->lowerLimit.factor);
837          Print ("PDS-S4", "Lower limit (scale value, scale factor)", Prt_GS,
838                 sect4->lowerLimit.value *
839                 pow (10.0, -1 * sect4->lowerLimit.factor), buffer);
840          sprintf (buffer, "%d, %d", sect4->upperLimit.value,
841                   sect4->upperLimit.factor);
842          Print ("PDS-S4", "Upper limit (scale value, scale factor)", Prt_GS,
843                 sect4->upperLimit.value *
844                 pow (10.0, -1 * sect4->upperLimit.factor), buffer);
845 /*         printf ("Hello world 1\n");*/
846          break;
847       case GS4_PERCENT_PNT:
848          Print ("PDS-S4", "Percentile", Prt_DS, sect4->percentile, "[%]");
849          break;
850       case GS4_PERCENT_TIME:
851          Print ("PDS-S4", "Percentile", Prt_DS, sect4->percentile, "[%]");
852 /*         strftime (buffer, 100, "%m/%d/%Y %H:%M:%S UTC",
853                    gmtime (&(sect4->validTime)));*/
854          Clock_Print (buffer, 100, sect4->validTime, "%m/%d/%Y %H:%M:%S UTC",
855                       0);
856 
857          Print ("PDS-S4", "End of overall time interval", Prt_S, buffer);
858          Print ("PDS-S4", "Total number of missing values", Prt_D,
859                 sect4->numMissing);
860          Print ("PDS-S4", "Number of time range specifications", Prt_D,
861                 sect4->numInterval);
862          for (i = 0; i < sect4->numInterval; i++) {
863             Print ("PDS-S4", "Interval number", Prt_D, i + 1);
864             Print ("PDS-S4", "Statistical process", Prt_DS,
865                    sect4->Interval[i].processID,
866                    Lookup (tbl410, sizeof (tbl410),
867                            sect4->Interval[i].processID));
868             Print ("PDS-S4", "Type of time increment", Prt_DS,
869                    sect4->Interval[i].incrType,
870                    Lookup (tbl411, sizeof (tbl411),
871                            sect4->Interval[i].incrType));
872             /* Following is so we get "# str" not "# (str)" */
873             sprintf (buffer, "%d %s", sect4->Interval[i].lenTime,
874                      Lookup (tbl44, sizeof (tbl44),
875                              sect4->Interval[i].timeRangeUnit));
876             Print ("PDS-S4", "Time range for processing", Prt_S, buffer);
877             /* Following is so we get "# str" not "# (str)" */
878             sprintf (buffer, "%d %s", sect4->Interval[i].timeIncr,
879                      Lookup (tbl44, sizeof (tbl44),
880                              sect4->Interval[i].incrUnit));
881             Print ("PDS-S4", "Time increment", Prt_S, buffer);
882          }
883          break;
884       case GS4_PROBABIL_TIME:
885          Print ("PDS-S4", "Forecast Probability Number", Prt_D,
886                 sect4->foreProbNum);
887          Print ("PDS-S4", "Total Number of Forecast Probabilities", Prt_D,
888                 sect4->numForeProbs);
889          Print ("PDS-S4", "Probability type", Prt_DS, sect4->probType,
890                 Lookup (tbl49, sizeof (tbl49), sect4->probType));
891          sprintf (buffer, "%d, %d", sect4->lowerLimit.value,
892                   sect4->lowerLimit.factor);
893          Print ("PDS-S4", "Lower limit (scale value, scale factor)", Prt_GS,
894                 sect4->lowerLimit.value *
895                 pow (10.0, -1 * sect4->lowerLimit.factor), buffer);
896          sprintf (buffer, "%d, %d", sect4->upperLimit.value,
897                   sect4->upperLimit.factor);
898          Print ("PDS-S4", "Upper limit (scale value, scale factor)", Prt_GS,
899                 sect4->upperLimit.value *
900                 pow (10.0, -1 * sect4->upperLimit.factor), buffer);
901          /* Intentionally fall through. */
902          CPL_FALLTHROUGH
903       case GS4_STATISTIC:
904 /*         strftime (buffer, 100, "%m/%d/%Y %H:%M:%S UTC",
905                    gmtime (&(sect4->validTime)));*/
906          Clock_Print (buffer, 100, sect4->validTime, "%m/%d/%Y %H:%M:%S UTC",
907                       0);
908 
909          Print ("PDS-S4", "End of overall time interval", Prt_S, buffer);
910          Print ("PDS-S4", "Total number of missing values", Prt_D,
911                 sect4->numMissing);
912          Print ("PDS-S4", "Number of time range specifications", Prt_D,
913                 sect4->numInterval);
914          for (i = 0; i < sect4->numInterval; i++) {
915             Print ("PDS-S4", "Interval number", Prt_D, i + 1);
916             Print ("PDS-S4", "Statistical process", Prt_DS,
917                    sect4->Interval[i].processID,
918                    Lookup (tbl410, sizeof (tbl410),
919                            sect4->Interval[i].processID));
920             Print ("PDS-S4", "Type of time increment", Prt_DS,
921                    sect4->Interval[i].incrType,
922                    Lookup (tbl411, sizeof (tbl411),
923                            sect4->Interval[i].incrType));
924             /* Following is so we get "# str" not "# (str)" */
925             sprintf (buffer, "%d %s", sect4->Interval[i].lenTime,
926                      Lookup (tbl44, sizeof (tbl44),
927                              sect4->Interval[i].timeRangeUnit));
928             Print ("PDS-S4", "Time range for processing", Prt_S, buffer);
929             /* Following is so we get "# str" not "# (str)" */
930             sprintf (buffer, "%d %s", sect4->Interval[i].timeIncr,
931                      Lookup (tbl44, sizeof (tbl44),
932                              sect4->Interval[i].incrUnit));
933             Print ("PDS-S4", "Time increment", Prt_S, buffer);
934          }
935          break;
936       default:
937          /* This case should have been handled in first switch statement of
938           * this procedure, but just in case... */
939          errSprintf ("Un-supported Sect4 template %d\n", sect4->templat);
940          return -2;
941    }
942    return 0;
943 }
944 
945 /*****************************************************************************
946  * PrintPDS2() --
947  *
948  * Arthur Taylor / MDL
949  *
950  * PURPOSE
951  *   To generate the message for the Product Definition Sections of the GRIB2
952  * Message.
953  *
954  * ARGUMENTS
955  *   meta = The meta file structure to generate the message for. (Input)
956  * f_unit = 0 (GRIB unit), 1 (english), 2 (metric) (Input)
957  *
958  * FILES/DATABASES: None
959  *
960  * RETURNS: void
961  *
962  * HISTORY
963  *   4/2003 Arthur Taylor (MDL/RSIS): Created.
964  *
965  * NOTES
966  *****************************************************************************
967  */
968 static int PrintPDS2 (grib_MetaData *meta, sChar f_unit)
969 {
970    pdsG2Type *pds2 = &(meta->pds2);
971    /* Based on Grib2 Code Table 0.0 */
972    static const char * const table0[] = {
973       "Meteorological products", "Hydrological products",
974       "Land surface products", "Space products", "Oceanographic products"
975    };
976    int ierr;            /* The error code of a called routine */
977 
978    /* Print the data from Section 0 */
979    switch (pds2->prodType) {
980       case 10:         /* Oceanographic Product. */
981          Print ("PDS-S0", "DataType", Prt_DS, pds2->prodType, table0[4]);
982          break;
983       case 5:          /* Reserved. */
984          Print ("PDS-S0", "DataType", Prt_DS, pds2->prodType,
985                 Lookup (table0, sizeof (table0), 191));
986          break;
987       default:
988          Print ("PDS-S0", "DataType", Prt_DS, pds2->prodType,
989                 Lookup (table0, sizeof (table0), pds2->prodType));
990    }
991    PrintSect1 (pds2, meta->center, meta->subcenter);
992    PrintSect2 (&(pds2->sect2));
993    if ((ierr = PrintSect4 (meta, f_unit)) != 0) {
994       return ierr;
995    }
996    return 0;
997 }
998 
999 /*****************************************************************************
1000  * PrintPDS1() --
1001  *
1002  * Arthur Taylor / MDL
1003  *
1004  * PURPOSE
1005  *   To generate the message for the Product Definition Sections of the GRIB1
1006  * Message.
1007  *
1008  * ARGUMENTS
1009  *      pds1 = The GRIB1 Product Definition Section to print. (Input)
1010  *   comment = A description about this element. See GRIB1_Table2LookUp (Input)
1011  *    center = The Center that created the data (Input)
1012  * subcenter = The Sub Center that created the data (Input)
1013  *    f_unit = The unit conversion method used on the output data (Input)
1014  *  unitName = The name of the output unit type. (Input)
1015  *   convert = Conversion method used. (Input)
1016  *
1017  * FILES/DATABASES: None
1018  *
1019  * RETURNS: void
1020  *
1021  * HISTORY
1022  *   4/2003 Arthur Taylor (MDL/RSIS): Created.
1023  *  10/2005 AAT: Adjusted to take center, subcenter as we moved that out of
1024  *               the pdsG1 type.
1025  *  11/2005 AAT: Added f_unit variable.
1026  *
1027  * NOTES
1028  *****************************************************************************
1029  */
1030 static void PrintPDS1 (pdsG1Type *pds1, char *comment,
1031                        unsigned short int center,
1032                        unsigned short int subcenter, sChar f_unit,
1033                        char *unitName, int convert)
1034 {
1035    char buffer[25];     /* Stores format of pds1->refTime. */
1036    const char *ptr;
1037 
1038    Print ("PDS-S1", "Parameter Tables Version", Prt_D, pds1->mstrVersion);
1039    ptr = centerLookup (center);
1040    if (ptr != NULL) {
1041       Print ("PDS-S1", "Originating center", Prt_DS, center, ptr);
1042    } else {
1043       Print ("PDS-S1", "Originating center", Prt_D, center);
1044    }
1045    ptr = subCenterLookup (center, subcenter);
1046    if (ptr != NULL) {
1047       Print ("PDS-S1", "Originating sub-center", Prt_DS, subcenter, ptr);
1048    } else {
1049       Print ("PDS-S1", "Originating sub-center", Prt_D, subcenter);
1050    }
1051    ptr = processLookup (center, pds1->genProcess);
1052    if (ptr != NULL) {
1053       Print ("PDS-S1", "Generation process", Prt_DS, pds1->genProcess, ptr);
1054    } else {
1055       Print ("PDS-S1", "Generation process", Prt_D, pds1->genProcess);
1056    }
1057    Print ("PDS-S1", "Grid Identification Number", Prt_D, pds1->gridID);
1058    Print ("PDS-S1", "Indicator of parameter and units", Prt_DS, pds1->cat,
1059           comment);
1060    if (convert != UC_NONE) {
1061       if (f_unit == 1) {
1062          Print ("PDS-S1", "Output grid, (COMPUTED) english unit is", Prt_S,
1063                 unitName);
1064       } else if (f_unit == 2) {
1065          Print ("PDS-S1", "Output grid, (COMPUTED) metric unit is", Prt_S,
1066                 unitName);
1067       }
1068    }
1069    Print ("PDS-S1", "Type of fixed surface", Prt_D, pds1->levelType);
1070    Print ("PDS-S1", "Value of fixed surface", Prt_D, pds1->levelVal);
1071 
1072 /* strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC", gmtime (&(pds1->refTime))); */
1073    Clock_Print (buffer, 25, pds1->refTime, "%m/%d/%Y %H:%M:%S UTC", 0);
1074 
1075    Print ("PDS-S1", "Reference Time", Prt_S, buffer);
1076 
1077 /* strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC",
1078              gmtime (&(pds1->validTime))); */
1079    Clock_Print (buffer, 25, pds1->validTime, "%m/%d/%Y %H:%M:%S UTC", 0);
1080 
1081    Print ("PDS-S1", "Valid Time", Prt_S, buffer);
1082 
1083 /* strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC", gmtime (&(pds1->P1))); */
1084    Clock_Print (buffer, 25, pds1->P1, "%m/%d/%Y %H:%M:%S UTC", 0);
1085 
1086    Print ("PDS-S1", "P1 Time", Prt_S, buffer);
1087 
1088 /* strftime (buffer, 25, "%m/%d/%Y %H:%M:%S UTC", gmtime (&(pds1->P2))); */
1089    Clock_Print (buffer, 25, pds1->P2, "%m/%d/%Y %H:%M:%S UTC", 0);
1090 
1091    Print ("PDS-S1", "P2 Time", Prt_S, buffer);
1092    Print ("PDS-S1", "Time range indicator", Prt_D, pds1->timeRange);
1093    Print ("PDS-S1", "Number included in average", Prt_D, pds1->Average);
1094    Print ("PDS-S1", "Number missing from average or accumulation", Prt_D,
1095           pds1->numberMissing);
1096 
1097    if (pds1->f_hasEns) {
1098       Print ("PDS-S1", "Ensemble BitFlag (octet 29)", Prt_D,
1099              pds1->ens.BitFlag);
1100       Print ("PDS-S1", "Ensemble Application", Prt_D, pds1->ens.Application);
1101       Print ("PDS-S1", "Ensemble Type", Prt_D, pds1->ens.Type);
1102       Print ("PDS-S1", "Ensemble Number", Prt_D, pds1->ens.Number);
1103       Print ("PDS-S1", "Ensemble ProdID", Prt_D, pds1->ens.ProdID);
1104       Print ("PDS-S1", "Ensemble Smoothing", Prt_D, pds1->ens.Smooth);
1105    }
1106    if (pds1->f_hasProb) {
1107       Print ("PDS-S1", "Prob Category", Prt_D, pds1->prob.Cat);
1108       Print ("PDS-S1", "Prob Type", Prt_D, pds1->prob.Type);
1109       Print ("PDS-S1", "Prob lower", Prt_F, pds1->prob.lower);
1110       Print ("PDS-S1", "Prob upper", Prt_F, pds1->prob.upper);
1111    }
1112    if (pds1->f_hasCluster) {
1113       Print ("PDS-S1", "Cluster Ens Size", Prt_D, pds1->cluster.ensSize);
1114       Print ("PDS-S1", "Cluster Size", Prt_D, pds1->cluster.clusterSize);
1115       Print ("PDS-S1", "Cluster Number", Prt_D, pds1->cluster.Num);
1116       Print ("PDS-S1", "Cluster Method", Prt_D, pds1->cluster.Method);
1117       Print ("PDS-S1", "Cluster North Latitude", Prt_F, pds1->cluster.NorLat);
1118       Print ("PDS-S1", "Cluster South Latitude", Prt_F, pds1->cluster.SouLat);
1119       Print ("PDS-S1", "Cluster East Longitude", Prt_F, pds1->cluster.EasLon);
1120       Print ("PDS-S1", "Cluster West Longitude", Prt_F, pds1->cluster.WesLon);
1121       sprintf (buffer, "'%10s'", pds1->cluster.Member);
1122       Print ("PDS-S1", "Cluster Membership", Prt_S, buffer);
1123    }
1124 }
1125 
1126 /*****************************************************************************
1127  * PrintGDS() --
1128  *
1129  * Arthur Taylor / MDL
1130  *
1131  * PURPOSE
1132  *   To generate the message for the Grid Definition Section.
1133  *
1134  * ARGUMENTS
1135  *     gds = The gds structure to print. (Input)
1136  * version = The GRIB version number (so we know what type of projection) (In)
1137  *
1138  * FILES/DATABASES: None
1139  *
1140  * RETURNS: int (could use errSprintf())
1141  *  0 if no error.
1142  * -1 if asked to print a map projection that we don't support.
1143  *
1144  * HISTORY
1145  *   9/2002 Arthur Taylor (MDL/RSIS): Created.
1146  *   4/2003 AAT: Switched from sect3 to gds
1147  *   5/2003 AAT: Since the number for ProjectionType changed from GRIB1 to
1148  *          GRIB2, we use the GRIB2 internally, but for meta data we want to
1149  *          print the appropriate one.
1150  *   5/2003 AAT: Decided to have 1,1 be lower left corner in .shp files.
1151  *  10/2004 AAT: Added TDLP support.
1152  *
1153  * NOTES
1154  * Need to add support for GS3_ORTHOGRAPHIC = 90,
1155  * GS3_EQUATOR_EQUIDIST = 110, GS3_AZIMUTH_RANGE = 120
1156  *****************************************************************************
1157  */
1158 static int PrintGDS (gdsType *gds, int version)
1159 {
1160    /* Based on Grib2 Code Table 3.1 */
1161    static const char * const table31[] = { "Latitude/Longitude", "Mercator",
1162       "Polar Stereographic", "Lambert Conformal",
1163       "Space view perspective orthographic",
1164       "Equatorial azimuthal equidistant projection",
1165       "Azimuth-range projection"
1166    };
1167    char buffer[50];     /* Temporary storage for info about scan flag. */
1168 
1169    Print ("GDS", "Number of Points", Prt_D, gds->numPts);
1170    switch (gds->projType) {
1171       case GS3_LATLON: /* 0 */
1172          if (version == 1) {
1173             Print ("GDS", "Projection Type", Prt_DS, GB1S2_LATLON,
1174                    table31[0]);
1175          } else {
1176             Print ("GDS", "Projection Type", Prt_DS, gds->projType,
1177                    table31[0]);
1178          }
1179          break;
1180       case GS3_MERCATOR: /* 10 */
1181          if (version == 1) {
1182             Print ("GDS", "Projection Type", Prt_DS, GB1S2_MERCATOR,
1183                    table31[1]);
1184          } else if (version == -1) {
1185             Print ("GDS", "Projection Type", Prt_DS, TDLP_MERCATOR,
1186                    table31[1]);
1187          } else {
1188             Print ("GDS", "Projection Type", Prt_DS, gds->projType,
1189                    table31[1]);
1190          }
1191          break;
1192       case GS3_POLAR:  /* 20 */
1193          if (version == 1) {
1194             Print ("GDS", "Projection Type", Prt_DS, GB1S2_POLAR, table31[2]);
1195          } else if (version == -1) {
1196             Print ("GDS", "Projection Type", Prt_DS, TDLP_POLAR, table31[2]);
1197          } else {
1198             Print ("GDS", "Projection Type", Prt_DS, gds->projType,
1199                    table31[2]);
1200          }
1201          break;
1202       case GS3_LAMBERT: /* 30 */
1203          if (version == 1) {
1204             Print ("GDS", "Projection Type", Prt_DS, GB1S2_LAMBERT,
1205                    table31[3]);
1206          } else if (version == -1) {
1207             Print ("GDS", "Projection Type", Prt_DS, TDLP_LAMBERT,
1208                    table31[3]);
1209          } else {
1210             Print ("GDS", "Projection Type", Prt_DS, gds->projType,
1211                    table31[3]);
1212          }
1213          break;
1214 /*
1215  * The following lines were removed until such time that the rest of this
1216  * procedure can properly handle these three projection types.
1217  *
1218       case GS3_ORTHOGRAPHIC:  * 90 *
1219          Print ("GDS", "Projection Type", Prt_DS, gds->projType, table31[4]);
1220          break;
1221       case GS3_EQUATOR_EQUIDIST:  * 110 *
1222          Print ("GDS", "Projection Type", Prt_DS, gds->projType, table31[5]);
1223          break;
1224       case GS3_AZIMUTH_RANGE:  * 120 *
1225          Print ("GDS", "Projection Type", Prt_DS, gds->projType, table31[6]);
1226          break;
1227 */
1228       default:
1229          Print ("GDS", "Projection Type", Prt_D, gds->projType);
1230          errSprintf ("Un-supported Map Projection %d\n", gds->projType);
1231          return -1;
1232    }
1233    if (gds->f_sphere) {
1234       Print ("GDS", "Shape of Earth", Prt_S, "sphere");
1235       Print ("GDS", "Radius", Prt_FS, gds->majEarth, "km");
1236    } else {
1237       Print ("GDS", "Shape of Earth", Prt_S, "oblate spheroid");
1238       Print ("GDS", "semi Major axis", Prt_FS, gds->majEarth, "km");
1239       Print ("GDS", "semi Minor axis", Prt_FS, gds->minEarth, "km");
1240    }
1241    Print ("GDS", "Nx (Number of points on parallel)", Prt_D, gds->Nx);
1242    Print ("GDS", "Ny (Number of points on meridian)", Prt_D, gds->Ny);
1243    Print ("GDS", "Lat1", Prt_F, gds->lat1);
1244    Print ("GDS", "Lon1", Prt_F, gds->lon1);
1245    if (gds->resFlag & GRIB2BIT_5) {
1246       Print ("GDS", "u/v vectors relative to", Prt_S, "grid");
1247    } else {
1248       Print ("GDS", "u/v vectors relative to", Prt_S, "easterly/northerly");
1249    }
1250    if (gds->projType == GS3_LATLON) {
1251       Print ("GDS", "Lat2", Prt_F, gds->lat2);
1252       Print ("GDS", "Lon2", Prt_F, gds->lon2);
1253       Print ("GDS", "Dx", Prt_FS, gds->Dx, "degrees");
1254       Print ("GDS", "Dy", Prt_FS, gds->Dy, "degrees");
1255    } else if (gds->projType == GS3_MERCATOR) {
1256       Print ("GDS", "Lat2", Prt_F, gds->lat2);
1257       Print ("GDS", "Lon2", Prt_F, gds->lon2);
1258       Print ("GDS", "Dx", Prt_FS, gds->Dx, "m");
1259       Print ("GDS", "Dy", Prt_FS, gds->Dy, "m");
1260    } else if ((gds->projType == GS3_POLAR)
1261               || (gds->projType == GS3_LAMBERT)) {
1262       Print ("GDS", "Dx", Prt_FS, gds->Dx, "m");
1263       Print ("GDS", "Dy", Prt_FS, gds->Dy, "m");
1264    }
1265    /* For scan mode... The user of this data doesn't necessarily care how it
1266     * was stored in the Grib2 grid (i.e. gds->scan), they just care about how
1267     * the data they are accessing is scanned (i.e. scan=0000) */
1268    sprintf (buffer, "%d%d%d%d", ((gds->scan & GRIB2BIT_1) / GRIB2BIT_1),
1269             ((gds->scan & GRIB2BIT_2) / GRIB2BIT_2),
1270             ((gds->scan & GRIB2BIT_3) / GRIB2BIT_3),
1271             ((gds->scan & GRIB2BIT_4) / GRIB2BIT_4));
1272    Print ("GDS", "Input GRIB2 grid, scan mode", Prt_DS, gds->scan, buffer);
1273 /*
1274    Print ("GDS", "Output grid, scan mode", Prt_DS, 0, "0000");
1275    Print ("GDS", "Output grid, scan i/x direction", Prt_S, "positive");
1276    Print ("GDS", "Output grid, scan j/y direction", Prt_S, "negative");
1277 */
1278    Print ("GDS", "Output grid, scan mode", Prt_DS, 64, "0100");
1279    Print ("GDS", "(.flt file grid), scan mode", Prt_DS, 0, "0000");
1280    Print ("GDS", "Output grid, scan i/x direction", Prt_S, "positive");
1281    Print ("GDS", "Output grid, scan j/y direction", Prt_S, "positive");
1282    Print ("GDS", "(.flt file grid), scan j/y direction", Prt_S, "negative");
1283    Print ("GDS", "Output grid, consecutive points in", Prt_S,
1284           "i/x direction");
1285    Print ("GDS", "Output grid, adjacent rows scan in", Prt_S,
1286           "same direction");
1287 
1288    /* Meshlat/orient lon/scale lat have no meaning for lat/lon grids. */
1289    if (gds->projType != GS3_LATLON) {
1290       Print ("GDS", "MeshLat", Prt_F, gds->meshLat);
1291       Print ("GDS", "OrientLon", Prt_F, gds->orientLon);
1292       if ((gds->projType == GS3_POLAR) || (gds->projType == GS3_LAMBERT)) {
1293          if (gds->center & GRIB2BIT_1) {
1294             Print ("GDS", "Which pole is on the plane", Prt_S, "South");
1295          } else {
1296             Print ("GDS", "Which pole is on the plane", Prt_S, "North");
1297          }
1298          if (gds->center & GRIB2BIT_2) {
1299             Print ("GDS", "bi-polar projection", Prt_S, "Yes");
1300          } else {
1301             Print ("GDS", "bi-polar projection", Prt_S, "No");
1302          }
1303       }
1304       Print ("GDS", "Tangent Lat1", Prt_F, gds->scaleLat1);
1305       Print ("GDS", "Tangent Lat2", Prt_F, gds->scaleLat2);
1306       Print ("GDS", "Southern Lat", Prt_F, gds->southLat);
1307       Print ("GDS", "Southern Lon", Prt_F, gds->southLon);
1308    }
1309    return 0;
1310 }
1311 
1312 /*****************************************************************************
1313  * PrintGridAttrib() --
1314  *
1315  * Arthur Taylor / MDL
1316  *
1317  * PURPOSE
1318  *   To generate the message for the various attributes of the grid.
1319  *
1320  * ARGUMENTS
1321  *  attrib = The Grid Attribute structure to print. (Input)
1322  * decimal = How many decimals to round to. (Input)
1323  *
1324  * FILES/DATABASES: None
1325  *
1326  * RETURNS: void
1327  *
1328  * HISTORY
1329  *   9/2002 Arthur Taylor (MDL/RSIS): Created.
1330  *   5/2003 AAT: Added rounding to decimal.
1331  *
1332  * NOTES
1333  *****************************************************************************
1334  */
1335 static void PrintGridAttrib (gridAttribType *attrib, sChar decimal)
1336 {
1337    /* Based on Grib2 Code Table 5.0 */
1338    static const char * const table50[] = {
1339       "Grid point data - simple packing", "Matrix value - simple packing",
1340       "Grid point data - complex packing",
1341       "Grid point data - complex packing and spatial differencing"
1342    };
1343 
1344    /* Based on Grib2 Code Table 5.1 */
1345    static const char * const table51[] = { "Floating point", "Integer" };
1346 
1347    /* Based on Grib2 Code Table 5.5 */
1348    static const char * const table55[] = {
1349       "No explicit missing value included with data",
1350       "Primary missing value included with data",
1351       "Primary and Secondary missing values included with data"
1352    };
1353 
1354    if ((attrib->packType == GS5_JPEG2000) ||
1355        (attrib->packType == GS5_JPEG2000_ORG)) {
1356       Print ("Info", "Packing that was used", Prt_DS, attrib->packType,
1357              "JPEG 2000");
1358    } else if ((attrib->packType == GS5_PNG) ||
1359               (attrib->packType == GS5_PNG_ORG)) {
1360       Print ("Info", "Packing that was used", Prt_DS, attrib->packType,
1361              "Portable Network Graphics (PNG)");
1362    } else {
1363       Print ("Info", "Packing that was used", Prt_DS, attrib->packType,
1364              Lookup (table50, sizeof (table50), attrib->packType));
1365    }
1366    /* Added next two 1/27/2006 because of questions from Val. */
1367    Print ("Info", "Decimal Scale Factor", Prt_D, attrib->DSF);
1368    Print ("Info", "Binary Scale Factor", Prt_D, attrib->ESF);
1369    Print ("Info", "Original field type", Prt_DS, attrib->fieldType,
1370           Lookup (table51, sizeof (table51), attrib->fieldType));
1371    Print ("Info", "Missing value management", Prt_DS, attrib->f_miss,
1372           Lookup (table55, sizeof (table55), attrib->f_miss));
1373    if (attrib->f_miss == 1) {
1374       Print ("Info", "Primary missing value", Prt_F,
1375              myRound (attrib->missPri, decimal));
1376    } else if (attrib->f_miss == 2) {
1377       Print ("Info", "Primary missing value", Prt_F,
1378              myRound (attrib->missPri, decimal));
1379       Print ("Info", "Secondary missing value", Prt_F,
1380              myRound (attrib->missSec, decimal));
1381    }
1382    Print ("Info", "Detected number of Missing", Prt_D, attrib->numMiss);
1383    if (attrib->f_maxmin) {
1384       Print ("Info", "Field minimum value", Prt_F,
1385              myRound (attrib->min, decimal));
1386       Print ("Info", "Field maximum value", Prt_F,
1387              myRound (attrib->max, decimal));
1388    }
1389 }
1390 
1391 /*****************************************************************************
1392  * MetaPrintGDS() --
1393  *
1394  * Arthur Taylor / MDL
1395  *
1396  * PURPOSE
1397  *   To generate a message specific for the GDS.  Basically a wrapper for
1398  * PrintGDS and Print.
1399  *
1400  * ARGUMENTS
1401  * gds = The Grid Definition Section to generate the message for. (Input)
1402  * version = The GRIB version number (so we know what type of projection) (In)
1403  * ans = The resulting message. Up to caller to free. (Output)
1404  *
1405  * FILES/DATABASES: None
1406  *
1407  * RETURNS: int (could use errSprintf())
1408  *  0 if no error.
1409  * -1 if asked to print a map projection that we don't support.
1410  * -2 if asked to print data for a template that we don't support.
1411  *
1412  * HISTORY
1413  *   4/2003 Arthur Taylor (MDL/RSIS): Created.
1414  *   5/2003 AAT: Commented out.  Purpose was mainly for debugging degrib1.c,
1415  *               which is now working.
1416  *
1417  * NOTES
1418  *****************************************************************************
1419  */
1420 int MetaPrintGDS (gdsType *gds, int version, char **ans)
1421 {
1422    int ierr;            /* The error code of a called routine */
1423 
1424    if ((ierr = PrintGDS (gds, version)) != 0) {
1425       *ans = Print (NULL, NULL, Prt_NULL);
1426       preErrSprintf ("Print error Section 3\n");
1427       return ierr;
1428    }
1429    *ans = Print (NULL, NULL, Prt_NULL);
1430    return 0;
1431 }
1432 
1433 /*****************************************************************************
1434  * MetaPrint() --
1435  *
1436  * Arthur Taylor / MDL
1437  *
1438  * PURPOSE
1439  *   To generate the meta file message.
1440  *
1441  * ARGUMENTS
1442  *    meta = The meta file structure to generate the message for. (Input)
1443  *     ans = The resulting message. Up to caller to free. (Output)
1444  * decimal = How many decimals to round to. (Input)
1445  *  f_unit = 0 (GRIB unit), 1 (english), 2 (metric) (Input)
1446  *
1447  * FILES/DATABASES: None
1448  *
1449  * RETURNS: int (could use errSprintf())
1450  *  0 if no error.
1451  * -1 if asked to print a map projection that we don't support.
1452  * -2 if asked to print data for a template that we don't support.
1453  *
1454  * HISTORY
1455  *   9/2002 Arthur Taylor (MDL/RSIS): Created.
1456  *   5/2003 AAT: Added rounding to decimal.
1457  *
1458  * NOTES
1459  *****************************************************************************
1460  */
1461 int MetaPrint (grib_MetaData *meta, char **ans, sChar decimal, sChar f_unit)
1462 {
1463    int ierr;            /* The error code of a called routine */
1464 
1465    if (meta->GribVersion == 1) {
1466       PrintPDS1 (&(meta->pds1), meta->comment, meta->center,
1467                  meta->subcenter, f_unit, meta->unitName, meta->convert);
1468    } else if (meta->GribVersion == -1) {
1469       PrintPDS_TDLP (&(meta->pdsTdlp));
1470    } else {
1471       if ((ierr = PrintPDS2 (meta, f_unit)) != 0) {
1472          *ans = Print (NULL, NULL, Prt_NULL);
1473          preErrSprintf ("Print error in PDS for GRIB2\n");
1474          return ierr;
1475       }
1476    }
1477    if ((ierr = PrintGDS (&(meta->gds), meta->GribVersion)) != 0) {
1478       *ans = Print (NULL, NULL, Prt_NULL);
1479       preErrSprintf ("Print error Section 3\n");
1480       return ierr;
1481    }
1482    PrintGridAttrib (&(meta->gridAttrib), decimal);
1483    *ans = Print (NULL, NULL, Prt_NULL);
1484    return 0;
1485 }
1486 #endif  // Unused with GDAL.
1487