NCCOOS Trac Projects: Top | Web | Platforms | Processing | Viz | Sprints | Sandbox | (Wind)

root/raw2proc/trunk/raw2proc/procutil.py

Revision 336 (checked in by haines, 14 years ago)

added CODAR Total processing

Line 
1 #!/usr/bin/env python
2 # Last modified:  Time-stamp: <2010-07-30 14:10:56 haines>
3 """Utilities to help data processing
4
5    Mostly time functions right now
6
7 """
8
9 __version__ = "v0.1"
10 __author__ = "Sara Haines <sara_haines@unc.edu>"
11
12 import os.path
13 from datetime import datetime, timedelta, tzinfo
14 from dateutil.tz import tzlocal, tzutc
15 import time
16 import math
17
18 from ncutil import *
19
20 def check_configs():
21     """Test config files for comformnity
22
23     check either one or all for a platform
24     
25     id in filename == platform.id
26     datetime in filename <= platform.config_start_date
27        (close in time usually the same day
28     also platform.config_start_date < platform.config_end_date
29        (there needs to be some time that the platform was operational)
30     test existence of specific structural elements (platform info and sensor info)
31     and specific fields for both platform and sensor
32     verify that for each platform_info['packages'] there is sensor_info and same id
33       for pi['packages'][0] in si.keys()
34       pi['packages'][0] == si['adcp']['id']
35     bounds on data in fields
36     show difference between two consecutive configs?
37     pretty print to screen of dictionary info for platform and sensor info
38     
39         cn = os.path.splitext(os.path.basename(config))[0]
40         cndt = filt_datetime(os.path.basename(config))[0]
41         pi = get_config(cn+'.platform_info')
42         if pi['config_start_date']:
43             config_start_dt = filt_datetime(pi['config_start_date'])[0]
44         elif pi['config_start_date'] == None:
45             config_start_dt = now_dt
46         if pi['config_end_date']:
47             config_end_dt = filt_datetime(pi['config_end_date'])[0]
48         elif pi['config_end_date'] == None:
49             config_end_dt = now_dt
50
51         print cn + ' -----------------'
52         print cndt
53         print config_start_dt
54         print config_end_dt
55         print now_dt
56         print 'file date ok? ' + str(cndt <= config_start_dt)
57         print 'operation date ok? ' + str(config_start_dt < config_end_dt)
58     """
59
60 def dt2es(dt):
61     """Convert datetime object to epoch seconds (es) as seconds since Jan-01-1970 """
62     # microseconds of timedelta object not used
63     delta = dt - datetime(1970,1,1,0,0,0)
64     es = delta.days*24*60*60 + delta.seconds
65     return es
66
67 def es2dt(es):
68     """ Convert epoch seconds (es) to datetime object"""
69     dt = datetime(*time.gmtime(es)[0:6])
70     return dt
71
72 def find_months(year, month=1):
73     """Find which months to process
74
75     Since data are in subdirectories based on months determine
76     previous, current, and next month to look in directories for data
77     of the current month or month to process.
78
79     :Parameters:
80         year : int value or str 'yyyy_mm'
81         month : int value
82
83     :Returns:
84         which_months : tuple of 3 datetime objects
85              (prev_month, current_month, next_month)
86
87     Examples
88     --------
89     >>> find_months(2007, 2)
90     >>> find_months('2007_02')
91     
92     """
93     if type(year) == int and type(month) == int :
94         dt = datetime(year, month, day=1)
95         this_month = dt
96     elif type(year) == str :
97         dt = filt_datetime(year)[0]
98         this_month = dt
99     #
100     if dt.month == 1: # if January
101         prev_month = datetime(dt.year-1, month=12, day=1) # Dec
102         next_month = datetime(dt.year, dt.month+1, day=1) # Feb
103     elif dt.month == 12: # if December
104         prev_month = datetime(dt.year, dt.month-1, day=1) # Nov
105         next_month = datetime(dt.year+1, month=1, day=1)  # Jan
106     else:
107         prev_month = datetime(dt.year, dt.month-1, day=1)
108         next_month = datetime(dt.year, dt.month+1, day=1)
109     #
110     return (prev_month, this_month, next_month)
111
112 def this_month():
113     """Return this month (GMT) as formatted string (yyyy_mm) """
114     this_month_str = "%4d_%02d" % time.gmtime()[0:2]
115     return this_month_str
116
117 def scanf_datetime(ts, fmt='%Y-%m-%dT%H:%M:%S'):
118     """Convert string representing date and time to datetime object"""
119     # default string format follows convention YYYY-MM-DDThh:mm:ss
120     
121     try:
122         t = time.strptime(ts, fmt)
123         # the '*' operator unpacks the tuple, producing the argument list.
124         dt = datetime(*t[0:6])
125     except ValueError, e:
126         # value error if something not valid for datetime
127         # e.g. month 1...12, something parsed wrong
128         dt = None
129     else:
130         # absolute difference in days from now (UTC)
131         z = dt - datetime.utcnow()
132         daysdiff = abs(z.days)
133         # if this date unreasonable (>10 years*365), throw it out
134         # something parsed wrong
135         if daysdiff > 3650:
136             dt = None               
137
138     return dt
139
140 def filt_datetime(input_string, remove_ext=True):
141     """
142     Following the template, (YY)YYMMDDhhmmss
143     and versions with of this with decreasing time precision,
144     find the most precise, reasonable string match and
145     return its datetime object.
146     """
147
148     # remove any trailing filename extension
149     from os.path import splitext
150     import re
151     if remove_ext:
152         (s, e) = splitext(input_string)
153         input_string = s
154    
155     # YYYYMMDDhhmmss and should handle most cases of the stamp
156     # other forms this should pass
157     # YY_MM_DD_hh:mm:ss
158     # YYYY_MM_DD_hh:mm:ss
159     # YYYY,MM,DD,hh,mm,ss
160     # YY,MM,DD,hh,mm,ss
161
162     case1_regex = r"""
163     # case 1: (YY)YYMMDDhhmmss
164     (\d{4}|\d{2})     # 2- or 4-digit YEAR (e.g. '07' or '2007')
165     \D?               # optional 1 character non-digit separator (e.g. ' ' or '-')
166     (\d{2})           # 2-digit MONTH (e.g. '12')
167     \D?               # optional 1 character non-digit separator
168     (\d{2})           # 2-digit DAY of month (e.g. '10')
169     \D?               # optional 1 character non-digit separator (e.g. ' ' or 'T')
170     (\d{2})           # 2-digit HOUR (e.g. '10')
171     \D?               # optional 1 character non-digit separator (e.g. ' ' or ':')
172     (\d{2})           # 2-digit MINUTE (e.g. '10')
173     \D?               # optional 1 character non-digit separator (e.g. ' ' or ':')
174     (\d{2})           # 2-digit SECOND (e.g. '10')
175     """
176
177     case2_regex = r"""
178     # case 2: (YY)YYMMDDhhmm (no seconds)
179     (\d{4}|\d{2})     # 2- or 4-digit YEAR
180     \D?               # optional 1 character non-digit separator (e.g. ' ' or '-')
181     (\d{2})           # 2-digit MONTH
182     \D?               # optional 1 character non-digit separator
183     (\d{2})           # 2-digit DAY
184     \D?               # optional 1 character non-digit separator (e.g. ' ' or 'T')
185     (\d{2})           # 2-digit HOUR
186     \D?               # optional 1 character non-digit separator (e.g. ' ' or ':')
187     (\d{2})           # 2-digit MINUTE
188     """
189
190     case3_regex = r"""
191     # case 3: (YY)YYMMDDhh (no seconds, no minutes)
192     (\d{4}|\d{2})     # 2- or 4-digit YEAR
193     \D?               # optional 1 character non-digit separator (e.g. ' ' or '-')
194     (\d{2})           # 2-digit MONTH
195     \D?               # optional 1 character non-digit separator
196     (\d{2})           # 2-digit DAY
197     \D?               # optional 1 character non-digit separator (e.g. ' ' or 'T')
198     (\d{2})           # 2-digit HOUR
199     """
200
201     case4_regex = r"""
202     # case 4: (YY)YYMMDD (no time values, just date)
203     (\d{4}|\d{2})     # 2- or 4-digit YEAR
204     \D?               # optional 1 character non-digit separator (e.g. ' ' or '-')
205     (\d{2})           # 2-digit MONTH
206     \D?               # optional 1 character non-digit separator
207     (\d{2})           # 2-digit DAY
208     """
209
210     case5_regex = r"""
211     # case 5: (YY)YYMM (no time values, just month year)
212     (\d{4}|\d{2})     # 2- or 4-digit YEAR
213     \D?               # optional 1 character non-digit separator (e.g. ' ' or '-')
214     (\d{2})           # 2-digit MONTH
215     """
216
217     ##  Verbose regular expressions require use of re.VERBOSE flag.
218     ##  so we can use multiline regexp
219
220     # cases are ordered from precise to more coarse resolution of time
221     cases = [case1_regex, case2_regex, case3_regex, case4_regex, case5_regex]
222     patterns = [re.compile(c, re.VERBOSE) for c in cases]
223     matches = [p.search(input_string) for p in patterns]
224
225     # for testing, try to computer datetime objects
226     # just because there is a match does not mean it makes sense
227     for ind in range(len(matches)):
228         if bool(matches[ind]):
229             # print matches[ind].groups()
230             bits = matches[ind].groups()
231             values = [int(yi) for yi in bits]
232             # check for 2-digit year
233             if values[0] < 50:
234                 values[0] += 2000
235             elif values[0]>=50 and values[0]<100:
236                 values[0] += 1900
237             #
238             # we must have at least 3 arg input to datetime
239             if len(values)==1:
240                 values.extend([1,1]) # add First of January
241             elif len(values)==2:
242                 values.extend([1]) # add first day of month
243
244             #
245             # compute dt
246             try:
247                 dt = datetime(*values)
248             except ValueError, e:
249                 # value error if something not valid for datetime
250                 # e.g. month 1...12, something parsed wrong
251                 dt = None
252             else:
253                 # absolute difference in days from now (UTC)
254                 z = dt - datetime.utcnow()
255                 daysdiff = abs(z.days)
256                 # if this date unreasonable (>10 years*365), throw it out
257                 # something parsed wrong
258                 if daysdiff > 3650:
259                     dt = None               
260         else:
261             dt = None
262
263         # place datetime object or None within sequence of matches
264         matches[ind] = dt
265
266     # find the first (most precise) date match since there might be more than
267     # as we searched more coarse templates, but now we have thrown out
268     
269     b = [bool(x) for x in matches]
270     try:
271         ind = b.index(True)
272     except ValueError, e:
273         print 'filt_datetime: No date found in ', input_string
274         dt = None
275     else:
276        dt = matches[ind]
277        return dt,ind
278
279 def display_time_diff(diff):
280     """Display time difference in HH:MM:DD using number weeks (W)
281     and days (D) if necessary"""
282     # weeks, days = divmod(diff.days, 7)
283     days = diff.days
284     minutes, seconds = divmod(diff.seconds, 60)
285     hours, minutes = divmod(minutes, 60)   
286     # if (weeks>2 and days>0):
287     #    str = "%d Weeks, %d Days %02d:%02d" % (days, hours, minutes)
288     if (days==1):
289         str = "%02d:%02d" % (24+hours, minutes)
290     elif (days>1):
291         str = "%d Days %02d:%02d" % (days, hours, minutes)
292     else:
293         str = "%02d:%02d" % (hours, minutes)
294     return str
295
296 def copy_loop_sequence(src, dst, fn_glob, numFiles=24):
297     """ """
298     # src = '/seacoos/data/nccoos/level3/bogue/adcpwaves/dspec/'+this_month.strftime("%Y_%m")
299     # dst = '/home/haines/rayleigh/loop/'
300     # fn_glob = 'bogue_dspec_plot*'
301
302 def addnan(dt, data, maxdelta=None):
303     """
304     insert NaN for time gaps
305
306     :Parameters:
307         dt : numpy.array of datetime
308         data : numpy.array of data
309         maxdelta : size of time gap (fraction or number of days) to insert
310             [default is two times its own sample interval]
311
312     :Returns:
313         new_dt : numpy.array of datetime
314         new_data : numpy.array of data
315
316
317     """
318     # dt to be only 1-dimension and data to be 1- or 2-dimensional
319     
320     from matplotlib.dates import date2num, num2date
321
322     # print dt.shape
323     # print data.shape
324     
325     dn = date2num(dt)
326     delta = numpy.diff(dn)
327     sample_interval = numpy.median(delta)
328     if maxdelta==None:
329         maxdelta = 2.*sample_interval
330     # print maxdelta
331     igap = (delta > maxdelta).nonzero()[0]
332     ngap = len(igap)
333     if not ngap:
334         return (dt, data)
335     else:
336         # convert sample interval to dt object
337         sample_interval = timedelta(0.5*sample_interval)
338         # for each gap in time create datetime value
339         dt_insert = [dt[gap]+sample_interval for gap in igap]
340         # insert new sample times at indices of the gaps
341         new_dt = numpy.insert(numpy.array(dt), igap+1, dt_insert)
342         # insert NaN value at the gaps (insert placed just before obs)
343         new_data = numpy.insert(numpy.array(data), igap+1, numpy.nan, axis=0)
344         # if all the data is NaN, then autoscale crocks.  This prevents
345         # throwing an error (but be careful if using for anything other than grafs)
346         if numpy.isnan(new_data).all():
347             new_data[-1]=0.
348         return (new_dt, new_data)
349
350 #
351
352 # unit conversion using udunits
353 def udconvert(val, units_from, units_to):
354     """Convert units using NCAR UDUNITS-2
355
356     Convert data to another unit using UDUNITS-2 API.
357
358     :Parameters:
359        val : scalar or list of scalars, numpy.array
360          Data to be converted
361        units_from : string
362          Units from which the values to be converted
363        units_to : string
364          Units to which the values will be converted
365
366     :Returns:
367        val_to : float scalar, list, or numpy.array
368          Data that is converted to new units
369        units_to : string
370          Units to which the data are now converted
371
372     Files
373     -----
374     XML file that can be edited to change and add new conversions
375     /usr/local/share/udunits/udunits-common.xml
376
377     Not recommended to edit but useful info on UDUNITS-2
378     udunits2-accepted.xml
379     udunits2-base.xml
380     udunits2-derived.xml
381     udunits2-prefixes.xml
382     udunits2.xml
383     
384     """
385     import udunits
386     cnv = udunits.udunits(units_from, units_to)
387
388     if cnv[0]==0:
389         val_to = val*cnv[1] + cnv[2]
390         # if val_to > 99:
391         #     val_to_str = '%.4g (%s)' % (val_to, valunits_to)
392         # else:
393         #     val_to_str = '%.2g (%s)' % (val_to, valunits_to)
394     else:
395         print cnv
396         return (None, None)
397
398     # TO DO: Need to handle errors in a better fashion
399     # [-1, 'Unable to parse from', 'NTU', -3, 'Conversion not possible']
400     # [-2, 'Unable to parse to', 'NTU', -3, 'Conversion not possible']
401     # [-3, 'Conversion not possible']
402     return (val_to, units_to)
403
404 # the following to be deprecated by udunits2 API
405 def meters2feet(meters):
406     """Convert meters to feet: <feet> = <meters>*3.28084 """
407     return meters*3.28084
408        
409 def feet2meters(feet):
410     """Convert feet to meters: <meters> = <feet>*0.3048 """
411     return feet*0.3048
412        
413 def millibar2inches_Hg(millibar):
414     """Convert millibars to inches Hg: <inches_Hg> = <millibar>*0.0295301 """
415     return millibar*0.0295301
416
417 def celsius2fahrenheit(celsius):
418     """Convert deg Celsius to deg Fahrenheit: <fahrenheit> = ((1.8*<celsius>)+32) """
419     return (1.8*celsius)+32
420
421 def millimeters2inches(millimeters):
422     """ Convert millimeter to inches: <inches> = <millimeters>*0.0393700787) """
423     return millimeters*0.0393700787
424
425 def inches2millimeters(inches):
426     """ Convert <mm> = <inches>*25.4 """
427     return inches*25.4
428
429 def meters_sec2knots(meters_sec):
430     """ Convert m/s to knots: <knots> = <meters_sec>*1.94384449) """
431     return meters_sec*1.94384449
432
433 def wind_vector2u(wind_speed, wind_from_direction):
434     """ Convert wind vector to U (east) component: <u> = <wind_speed>*sine(<wind_from_direction>*pi/180) """
435     return wind_speed*math.sin(wind_from_direction*math.pi/180)
436
437 def wind_vector2v(wind_speed, wind_from_direction):
438     """ Convert wind vector to V (north) component: <v> = <wind_speed>*cosine(<wind_from_direction>*pi/180) """
439     return wind_speed*math.cos(wind_from_direction*math.pi/180)
440
441 def proc2latest(pi, si, yyyy_mm):
442     """Select specific variables and times from current monthly netCDF
443     and post as latest data.  TEST MODE.
444
445     For each active config file, load specific variables from NCCOOS
446     monthly netCDF, make any necessary changes to data or attributes
447     conform to SEACOOS Data Model, subset data (last 48 hours), and
448     create new netCDF file in latest netCDF directory.
449
450     NOTE: In test mode right now. See auto() function for similar action.
451
452     """
453    
454     platform = pi['id']
455     package = si['id']
456     # input file
457     si['proc_filename'] = '%s_%s_%s.nc' % (platform, package, yyyy_mm)
458     ifn = os.path.join(si['proc_dir'], si['proc_filename'])
459     # output file
460     si['latest_filename'] = 'nccoos-%s-%s-latest.nc' % (platform, package)
461     ofn = os.path.join(si['latest_dir'], si['latest_filename'])
462     if os.path.exists(ifn):
463         print ' ... ... latest : %s ' % (ofn,)
464         # get dt from current month file
465         (es, units) = nc_get_time(ifn)
466         dt = [es2dt(e) for e in es]
467         last_dt = dt[-1]
468     else:
469         # no input then remove output if exists and exit
470         print " ... ... latest: NO latest file created"
471         if os.path.exists(ofn):
472             os.remove(ofn)
473         return
474
475     # determine which index of data is within the specified timeframe (last 2 days)
476     n = len(dt)
477     idx = numpy.array([False for i in range(n)])
478     for i, val in enumerate(dt):
479         if val>last_dt-timedelta(days=2) and val<=last_dt+timedelta(seconds=360):
480             idx[i] = True
481     dt = numpy.array(dt)
482     dt = dt[idx]
483
484     # read in data and unpack tuple
485     d = nc_load(ifn, si['latest_vars'])
486     global_atts, var_atts, dim_inits, var_inits, var_data = d
487     list_of_record_vars = nc_find_record_vars(ifn)
488
489     # turn off unlimited dimension (SH NOTE: As of pycdf-0.6-3b cannot
490     # delete a dimension or reset unlimited to limited within either
491     # CDF or CDFDim class, so doing it manually here by setting list
492     # 'dim_init' before creating of new netcdf.)
493     dim_inits = list(dim_inits)
494     for i in range(len(dim_inits)):
495         if dim_inits[i][1]==0:
496             dim_inits[i] = ('ntime', len(dt))       
497     dim_inits = tuple(dim_inits)
498
499     # subset data
500     varNames = [vn for vn, vt, vd in var_inits]
501     var_data = list(var_data)
502     for i in range(len(varNames)):
503         vn, vd = var_data[i]
504
505         if vn in list_of_record_vars:
506             var_data[i]=(vn, vd[idx])
507     var_data = tuple(var_data)
508
509     global_atts['start_date'] = dt[0].strftime('%Y-%m-%d %H:%M:%S')
510     d = (global_atts, var_atts, dim_inits, var_inits, var_data)
511
512     # write latest data
513     nc_create(ofn, d)
514
515     # quick way to rename dimensions
516     nc_rename_dimension(ofn, 'ntime', 'time')
517     nc_rename_dimension(ofn, 'nlat', 'lat')
518     nc_rename_dimension(ofn, 'nlon', 'lon')
519     nc_rename_dimension(ofn, 'nz', 'z')
520
521     # global replace _FillValue
522     nc_replace_fillvalue(ofn, -99999.0)
523
524 def proc2csv(pi, si, yyyy_mm):
525     """Select specific variables and times from current monthly netCDF
526     and post file of csv data.  TEST MODE.
527
528     For each active config file, load specific variables from NCCOOS
529     monthly netCDF, make any necessary changes to data or attributes
530     conform to CSV output, subset data, and
531     create new file in csv directory.
532
533     NOTE: See auto() function for similar action.
534
535     """
536    
537     platform = pi['id']
538     package = si['id']
539     # input file
540     si['proc_filename'] = '%s_%s_%s.nc' % (platform, package, yyyy_mm)
541     ifn = os.path.join(si['proc_dir'], si['proc_filename'])
542     # output file
543     si['csv_filename'] = 'nccoos_%s_%s_latest.csv' % (platform, package)
544     ofn = os.path.join(si['csv_dir'], si['csv_filename'])
545     f = open(ofn, 'w')
546    
547     if os.path.exists(ifn):
548         print ' ... ... csv : %s ' % (ofn,)
549         # get dt from current month file
550         (es, units) = nc_get_time(ifn)
551         dt = [es2dt(e) for e in es]
552         last_dt = dt[-1]
553     else:
554         # no input then report fact csv file
555         print ' ... ... csv: NO csv data reported '
556         f.write('"No DATA REPORTED", " \\- ", " \\- "\n')
557         f.close()
558         return
559    
560     # determine which index of data is within the specified timeframe (last 2 days)
561     n = len(dt)
562     idx = numpy.array([False for i in range(n)])
563     for i, val in enumerate(dt):
564         if val>last_dt-timedelta(days=1) and val<=last_dt+timedelta(seconds=360):
565             idx[i] = True
566     dt = numpy.array(dt)
567     dt = dt[idx]
568
569     # read in data and unpack tuple
570     d = nc_load(ifn, si['csv_vars'])
571     global_atts, var_atts, dim_inits, var_inits, var_data = d
572
573     # dts = es2dt(dt[-1])
574     # set timezone info to UTC (since data from level1 should be in UTC!!)
575     last_dt = last_dt.replace(tzinfo=tzutc())
576     # return new datetime based on computer local
577     last_dt_local = last_dt.astimezone(tzlocal())
578    
579     diff = abs(last_dt - last_dt_local)
580     if diff.days>0:
581         last_dt_str = last_dt.strftime("%H:%M %Z on %b %d, %Y") + \
582                       ' (' + last_dt_local.strftime("%H:%M %Z, %b %d") + ')'
583     else:
584         last_dt_str = last_dt.strftime("%H:%M %Z") + \
585                       ' (' + last_dt_local.strftime("%H:%M %Z") + ')' \
586                       + last_dt.strftime(" on %b %d, %Y")
587
588     # uses dateutil.tz.tzutc() from dateutil
589     now_utc_dt = datetime.now(tzutc())
590     now_utc_dt = now_utc_dt.replace(second=0, microsecond=0)
591     # uses dateutil.tz.tzlocal() from dateutil to get timezone settings as known by the operating system
592     now_local_dt = datetime.now(tzlocal())
593     now_local_dt = now_local_dt.replace(second=0, microsecond=0)
594     # if more than a day difference between local time and UTC, specify dates for each
595     # otherwise date for one is sufficient (cuts down on clutter)
596     diff = abs(now_local_dt - now_utc_dt)
597     if diff.days>0:
598         now_str = now_utc_dt.strftime("%H:%M %Z on %b %d, %Y") + \
599                   ' (' + now_local_dt.strftime("%H:%M %Z, %b %d") + ')'
600     else:
601         now_str = now_utc_dt.strftime("%H:%M %Z") + \
602                   ' (' + now_local_dt.strftime("%H:%M %Z") + ')' \
603                   + now_utc_dt.strftime(" on %b %d, %Y")
604
605     # how old is the data
606     stale_diff = abs(now_utc_dt - last_dt)
607     if stale_diff.days>0 or stale_diff.seconds>=8*60*60:
608         stale_str = display_time_diff(stale_diff)
609     else:
610         stale_str = '' # use empty string to keep background white
611             
612     varNames = [vn for vn, vt, vd in var_inits]
613     var_data = list(var_data)
614     for i in range(len(varNames)):
615         vn, vd = var_data[i]
616         vd = vd[idx]
617
618         # (1) var name and units (first td)
619         var_name_str = '%s (%s)' % (var_atts[vn]['long_name'], var_atts[vn]['short_name'])
620         valunits = var_atts[vn]['units']
621         if vn=='rain':
622             val = vd.sum()
623             var_name_str = 'Rain Total (24 hrs)'
624         else:
625             val = vd[-1]
626
627         # if can take the length of val
628         # probably a list, tuple
629         # there will be more than one value of which we want a mean (ignoring NaN')
630         if bool('__len__' in dir(val)):
631             val = numpy.mean(numpy.ma.masked_where(numpy.isnan(val), val))               
632
633         # to metric
634         import udunits
635
636         sn = var_atts[vn]['standard_name']
637         valunits_from = valunits
638         if 'temperature' in sn or sn in ('wind_chill', 'dew_point'):           
639             if valunits_from == 'degrees Celsius':
640                 valunits_from = 'degC'
641             valunits_to = 'degC'
642         elif 'velocity' in sn:
643             valunits_to = 'm s-1'
644         elif 'flux' in sn or sn in ('discharge',):
645             if valunits_from == 'cfs':
646                 valunits_from = 'ft^3/sec'
647             valunits_to = 'm^3/s'
648         elif 'rain' in sn:
649             valunits_to = 'mm'
650         elif 'level' in sn or 'height' in sn or 'depth' in sn:
651             valunits_to = 'm'
652         else:
653             # can't find a conversion we want so convert to itself
654             valunits_to = valunits_from
655            
656         cnv = udunits.udunits(valunits_from, valunits_to)
657
658         if cnv[0]==0:
659             val_to = val*cnv[1] + cnv[2]
660             if val_to > 99:
661                 metric_str = '%.4g (%s)' % (val_to, valunits_to)
662             else:
663                 metric_str = '%.2g (%s)' % (val_to, valunits_to)
664         # handle errors
665         # [-1, 'Unable to parse from', 'NTU', -3, 'Conversion not possible']
666         # [-2, 'Unable to parse to', 'NTU', -3, 'Conversion not possible']
667         # [-3, 'Conversion not possible']
668         elif cnv[0]==-1 or cnv[0]==-2:
669             if val > 99:
670                 metric_str = '%.4g (%s)' % (val, valunits)
671             else:
672                 metric_str = '%.2g (%s)' % (val, valunits)
673         else:
674             metric_str = '\-'
675                
676         # to english units
677         if 'temperature' in sn or sn in ('wind_chill', 'dew_point'):           
678             if valunits_from == 'degrees Celsius':
679                 valunits_from = 'degC'
680             valunits_to = 'degF'
681         elif 'velocity' in sn:
682             valunits_to = 'knots'
683         elif 'flux' in sn or sn in ('discharge',):
684             if valunits_from == 'cfs':
685                 valunits_from = 'ft^3/sec'
686             valunits_to = 'ft^3/s'
687         elif 'rain' in sn:
688             valunits_to = 'in'
689         elif 'level' in sn or 'height' in sn or 'depth' in sn:
690             valunits_to = 'ft'
691         else:
692             valunits_to = valunits_from
693         #
694         cnv = udunits.udunits(valunits_from, valunits_to)
695         if cnv[0]==0:
696             val_to = val*cnv[1] + cnv[2]           
697             if val > 99:
698                 english_str ='%.4g (%s)' % (val_to, valunits_to)
699             else:
700                 english_str = '%.2g (%s)' % (val_to, valunits_to)
701         # handle errors
702         # [-1, 'Unable to parse from', 'NTU', -3, 'Conversion not possible']
703         # [-2, 'Unable to parse to', 'NTU', -3, 'Conversion not possible']
704         # [-3, 'Conversion not possible']
705         elif cnv[0]==-1 or cnv[0]==-2:
706             if val > 99:
707                 english_str = '%.4g (%s)' % (val, valunits)
708             else:
709                 english_str = '%.2g (%s)' % (val, valunits)
710         else:
711             english_str = '\-'
712
713         if metric_str == english_str:
714             english_str = '\-'
715        
716         if vn=='time':
717             f.write('"**%s:** %s", ""\n' % ('Sample Time', last_dt_str))
718         else:
719             f.write('"%s", "%s", "%s"\n' % (var_name_str, metric_str, english_str))
720
721     f.close()
722
723
724
725
Note: See TracBrowser for help on using the browser.