1 | """ |
---|
2 | repo of useful functions for visualizing stuff |
---|
3 | """ |
---|
4 | import os |
---|
5 | import sys |
---|
6 | |
---|
7 | # the syntax to import nio depends on its version. We try all options from the |
---|
8 | # newest to the oldest |
---|
9 | try: |
---|
10 | import Nio as nio |
---|
11 | except: |
---|
12 | try: |
---|
13 | import PyNGL.Nio as nio |
---|
14 | except: |
---|
15 | import PyNGL_numpy.Nio as nio |
---|
16 | |
---|
17 | # The following is only needed for hn3.its.monash.edu.au |
---|
18 | # Unfortunately we must get rid of spurious entries in the sys.path that can |
---|
19 | # lead to the loading of conflicting packages |
---|
20 | for dir_idx in range(len(sys.path)-1,-1,-1): |
---|
21 | if 'python2.4' in sys.path[dir_idx]: |
---|
22 | sys.path.pop(dir_idx) |
---|
23 | |
---|
24 | import time |
---|
25 | import pylab as p |
---|
26 | import matplotlib.numerix.ma as ma |
---|
27 | import numpy as n |
---|
28 | from matplotlib.toolkits.basemap import Basemap |
---|
29 | import gc |
---|
30 | |
---|
31 | a_small_number = 1e-8 |
---|
32 | |
---|
33 | def get_long_name(var): |
---|
34 | try: |
---|
35 | long_name = var.long_name |
---|
36 | except AttributeError: |
---|
37 | try: |
---|
38 | long_name = var.description |
---|
39 | except AttributeError: |
---|
40 | long_name = 'N/A' |
---|
41 | if long_name == '': |
---|
42 | long_name = 'N/A' |
---|
43 | return long_name |
---|
44 | |
---|
45 | def peek(file_name, return_pointers=False, show_vars=True): |
---|
46 | #print file_name |
---|
47 | file = nio.open_file(file_name) |
---|
48 | vars = file.variables |
---|
49 | if show_vars: |
---|
50 | keys = vars.keys() |
---|
51 | keys.sort() |
---|
52 | page_idx = 0 |
---|
53 | max = 0 |
---|
54 | lines_in_a_page = 50 |
---|
55 | if len(keys) > lines_in_a_page: |
---|
56 | while max < len(keys): |
---|
57 | min = page_idx * lines_in_a_page |
---|
58 | max = (page_idx + 1) * lines_in_a_page |
---|
59 | if max > len(keys): |
---|
60 | max = len(keys) |
---|
61 | print min, max, type(min), type(max) |
---|
62 | for key in keys[min:max]: |
---|
63 | long_name = get_long_name(vars[key]) |
---|
64 | if len(key) < 7: |
---|
65 | print '\t', key, '\t\t->\t', long_name |
---|
66 | else: |
---|
67 | print '\t', key, '\t->\t', long_name |
---|
68 | page_idx += 1 |
---|
69 | raw_input('press enter to continue...') |
---|
70 | else: |
---|
71 | for key in keys: |
---|
72 | long_name = get_long_name(vars[key]) |
---|
73 | if len(key) < 7: |
---|
74 | print '\t', key, '\t\t->\t', long_name |
---|
75 | else: |
---|
76 | print '\t', key, '\t->\t', long_name |
---|
77 | if return_pointers: |
---|
78 | return file, vars |
---|
79 | else: |
---|
80 | return |
---|
81 | |
---|
82 | def cardinal_2_month(cardinal): |
---|
83 | if cardinal == 1: |
---|
84 | return 'Jan' |
---|
85 | elif cardinal == 2: |
---|
86 | return 'Feb' |
---|
87 | elif cardinal == 3: |
---|
88 | return 'Mar' |
---|
89 | elif cardinal == 4: |
---|
90 | return 'Apr' |
---|
91 | elif cardinal == 5: |
---|
92 | return 'May' |
---|
93 | elif cardinal == 6: |
---|
94 | return 'Jun' |
---|
95 | elif cardinal == 7: |
---|
96 | return 'Jul' |
---|
97 | elif cardinal == 8: |
---|
98 | return 'Aug' |
---|
99 | elif cardinal == 9: |
---|
100 | return 'Sep' |
---|
101 | elif cardinal == 10: |
---|
102 | return 'Oct' |
---|
103 | elif cardinal == 11: |
---|
104 | return 'Nov' |
---|
105 | elif cardinal == 12: |
---|
106 | return 'Dec' |
---|
107 | else: |
---|
108 | return 'gibberish' |
---|
109 | |
---|
110 | def set_default_basemap(lon, lat, frame_width=5.): |
---|
111 | test = lon < 0. |
---|
112 | if True in test: |
---|
113 | # matplotlib expects 0-360 while WRF for example uses -180-180 |
---|
114 | delta = n.ones(lon.shape) |
---|
115 | delta *= 360 |
---|
116 | delta = ma.masked_where(lon > 0., delta) |
---|
117 | lon += delta.filled(fill_value=0) |
---|
118 | llcrnrlon=lon.min() - frame_width |
---|
119 | urcrnrlon=lon.max() + frame_width |
---|
120 | llcrnrlat=lat.min() - frame_width |
---|
121 | urcrnrlat=lat.max() + frame_width |
---|
122 | lon_0 = llcrnrlon + (urcrnrlon - llcrnrlon) / 2. |
---|
123 | lat_0 = llcrnrlat + (urcrnrlat - llcrnrlat) / 2. |
---|
124 | |
---|
125 | map = Basemap( |
---|
126 | llcrnrlon=llcrnrlon, |
---|
127 | llcrnrlat=llcrnrlat, |
---|
128 | urcrnrlon=urcrnrlon, |
---|
129 | urcrnrlat=urcrnrlat, |
---|
130 | resolution='l', |
---|
131 | projection='cyl', |
---|
132 | lon_0=lon_0, |
---|
133 | lat_0=lat_0 |
---|
134 | ) |
---|
135 | return map |
---|
136 | |
---|
137 | def set_lvl_string(lvl, lvl_type): |
---|
138 | if lvl_type == 'sigma': |
---|
139 | pass |
---|
140 | if lvl_type == 'press': |
---|
141 | pass |
---|
142 | if lvl_type == 'soil': |
---|
143 | pass |
---|
144 | return lvl_string |
---|
145 | |
---|
146 | def set_title_string( |
---|
147 | long_name, |
---|
148 | units, |
---|
149 | time_string, |
---|
150 | lvl_string, |
---|
151 | prefix='', |
---|
152 | postfix=''): |
---|
153 | title_string = \ |
---|
154 | prefix + ' '\ |
---|
155 | + lvl_string + ' ' \ |
---|
156 | + long_name + ' (' + units + ')\n' \ |
---|
157 | + ' valid at ' \ |
---|
158 | + time_string \ |
---|
159 | + postfix |
---|
160 | return title_string |
---|
161 | |
---|
162 | def set_time_string(model, time): |
---|
163 | if model == 'WRF': |
---|
164 | time_string = time.tostring() |
---|
165 | year = time_string[:4] |
---|
166 | month = cardinal_2_month(int(time_string[5:7])) |
---|
167 | day = time_string[8:10] |
---|
168 | hour = time_string[11:13] |
---|
169 | minute = time_string[14:16] |
---|
170 | second = time_string[17:] |
---|
171 | time_string = day + ' ' \ |
---|
172 | + month + ' ' \ |
---|
173 | + year + ' ' \ |
---|
174 | + hour + ':' + minute + ' UTC' |
---|
175 | return time_string |
---|
176 | elif model == 'LAPS': |
---|
177 | valid_date = str(time[0]) |
---|
178 | valid_time = str(time[1]).zfill(4) |
---|
179 | time_string = valid_date[6:] + ' ' \ |
---|
180 | + cardinal_2_month(int(valid_date[4:6])) + ' ' \ |
---|
181 | + valid_date[0:4] \ |
---|
182 | + ' ' + valid_time[:2] + ':' \ |
---|
183 | + valid_time[2:] + ' UTC' |
---|
184 | return time_string |
---|
185 | elif model == 'manual': |
---|
186 | # expects list of the form |
---|
187 | # [year, month, day, hour, minute] |
---|
188 | time_string = (str(time[2]) + ' ') |
---|
189 | time_string += (cardinal_2_month(time[1]) + ' ') |
---|
190 | time_string += (str(time[0]) + ' ') |
---|
191 | time_string += (str(time[3]).zfill(2) + ':') |
---|
192 | time_string += (str(time[4]).zfill(2) + ' ') |
---|
193 | time_string += 'UTC' |
---|
194 | return time_string |
---|
195 | else: |
---|
196 | print 'set_time_string has done nothing' |
---|
197 | return |
---|
198 | |
---|
199 | def plot_grid(lon,lat, |
---|
200 | title_string = 'N/A', |
---|
201 | meridians_delta = 15, |
---|
202 | parallels_delta = 15, |
---|
203 | same_figure = False, |
---|
204 | figsize = None, |
---|
205 | file_name = None, |
---|
206 | dpi = 80, |
---|
207 | skip = 5, |
---|
208 | return_map = False, |
---|
209 | marker = '+' |
---|
210 | ): |
---|
211 | """Function to plot grids similar to those generated by WPS |
---|
212 | |
---|
213 | Modified 27/01/08: Minor mod to plot the boundaries of the domain |
---|
214 | Comments: There is a chunk of code that gets repeated in a lot of places... the |
---|
215 | stuff about plotting meridians and parallels. Maybe we could put this in a seperate |
---|
216 | funtion? |
---|
217 | |
---|
218 | """ |
---|
219 | map = set_default_basemap(lon,lat) |
---|
220 | # let's make sure the lat and lon arrays are 2D |
---|
221 | if len(lon.shape) < 2: |
---|
222 | lon, lat = p.meshgrid(lon,lat) |
---|
223 | if not same_figure: |
---|
224 | if not figsize: |
---|
225 | p.figure() |
---|
226 | else: |
---|
227 | p.figure(figsize=figsize) |
---|
228 | map.plot(lon[::skip,::skip], lat[::skip,::skip], marker=marker, linestyle='None') |
---|
229 | |
---|
230 | corners_lon = n.array([lon[0,0], lon[0,-1], lon[-1,-1], lon[-1,0]]) |
---|
231 | corners_lat = n.array([lat[0,0], lat[0,-1], lat[-1,-1], lat[-1,0]]) |
---|
232 | |
---|
233 | map.plot(corners_lon,corners_lat, 'ro') |
---|
234 | |
---|
235 | # Here it is =) |
---|
236 | map.plot(lon[0,:],lat[0,:],'k-') |
---|
237 | map.plot(lon[:,-1],lat[:,-1],'k-') |
---|
238 | map.plot(lon[-1,:],lat[-1,:],'k-') |
---|
239 | map.plot(lon[:,0],lat[:,0],'k-') |
---|
240 | |
---|
241 | # Thom: I've taken out the plot canberra option for generality |
---|
242 | # canberra_lon = [149 + 8./60] |
---|
243 | # canberra_lat = [-35 - 17./60] |
---|
244 | # map.plot(canberra_lon,canberra_lat, 'gs') |
---|
245 | |
---|
246 | if not same_figure: |
---|
247 | map.drawcoastlines() |
---|
248 | # These blocks seem to get repeated... |
---|
249 | meridians = n.arange(int(round(lon.min(),0)), |
---|
250 | lon.max() + a_small_number, meridians_delta) |
---|
251 | meridians = n.array(meridians) |
---|
252 | map.drawmeridians(meridians, labels=[1,0,0,1]) |
---|
253 | |
---|
254 | parallels = n.arange(int(round(lat.min(),0)), |
---|
255 | lat.max() + a_small_number, parallels_delta) |
---|
256 | parallels = n.array(parallels) |
---|
257 | map.drawparallels(parallels, labels=[1,0,0,1]) |
---|
258 | |
---|
259 | p.title(title_string) |
---|
260 | if file_name: |
---|
261 | p.savefig(file_name,dpi=dpi) |
---|
262 | if return_map: |
---|
263 | return map |
---|
264 | else: |
---|
265 | return |
---|
266 | |
---|
267 | def plot_slab(lon, lat, slab, |
---|
268 | map = None, |
---|
269 | figsize = None, |
---|
270 | cntr_lvl = None, |
---|
271 | title_string = 'N/A', |
---|
272 | meridians_delta = 15, |
---|
273 | parallels_delta = 15, |
---|
274 | file_name = None, |
---|
275 | dpi = 80, |
---|
276 | wind_vector =None, |
---|
277 | quiv_skip = 5, |
---|
278 | frame_width = 5, |
---|
279 | significant_digits = 0, |
---|
280 | colorbar = False, |
---|
281 | contour_labels = True, |
---|
282 | monochrome = False, |
---|
283 | quiverkey_length = 10, |
---|
284 | return_map = False |
---|
285 | ): |
---|
286 | from matplotlib.cm import gist_ncar as cmap |
---|
287 | # let's make sure the lat and lon arrays are 2D |
---|
288 | if len(lon.shape) < 2: |
---|
289 | lon, lat = p.meshgrid(lon,lat) |
---|
290 | if map is None: |
---|
291 | map = set_default_basemap(lon,lat,frame_width) |
---|
292 | if not figsize: |
---|
293 | fig = p.figure() |
---|
294 | else: |
---|
295 | fig = p.figure(figsize=figsize) |
---|
296 | if cntr_lvl is not None: |
---|
297 | if monochrome: |
---|
298 | cset = map.contour(lon, lat, slab, cntr_lvl, colors='black') |
---|
299 | else: |
---|
300 | csetf = map.contourf(lon, lat, slab, |
---|
301 | cntr_lvl, |
---|
302 | cmap=cmap) |
---|
303 | if wind_vector is None: |
---|
304 | cset = map.contour(lon, lat, slab, cntr_lvl, colors='lightslategray') |
---|
305 | else: |
---|
306 | if monochrome: |
---|
307 | cset = map.contour(lon, lat, slab, colors='black') |
---|
308 | else: |
---|
309 | csetf = map.contourf(lon, lat, slab, cmap=cmap) |
---|
310 | if wind_vector is None: |
---|
311 | cset = map.contour(lon, lat, slab, colors='lightslategray') |
---|
312 | if wind_vector is not None: |
---|
313 | quiv = map.quiver(lon[::quiv_skip,::quiv_skip], |
---|
314 | lat[::quiv_skip,::quiv_skip], |
---|
315 | wind_vector[0][::quiv_skip,::quiv_skip], |
---|
316 | wind_vector[1][::quiv_skip,::quiv_skip]) |
---|
317 | from scipy.stats import median |
---|
318 | p.quiverkey(quiv, |
---|
319 | 0.855, |
---|
320 | 0.115, |
---|
321 | quiverkey_length, |
---|
322 | str(int(quiverkey_length)) + ' m/s', |
---|
323 | labelpos='S', |
---|
324 | coordinates='figure') |
---|
325 | # plot grid outline |
---|
326 | map.plot(lon[0,:],lat[0,:],color='lightslategray') |
---|
327 | map.plot(lon[-1,:],lat[-1,:],color='lightslategray') |
---|
328 | map.plot(lon[:,0],lat[:,0],color='lightslategray') |
---|
329 | map.plot(lon[:,-1],lat[:,-1],color='lightslategray') |
---|
330 | if monochrome: |
---|
331 | map.fillcontinents(color='0.95') |
---|
332 | |
---|
333 | map.drawcoastlines() |
---|
334 | |
---|
335 | # todo |
---|
336 | # the +5 is a hack in case one uses the default map it should be made an |
---|
337 | # an explicit parameter that is passed around... |
---|
338 | meridians = n.arange(int(round(lon.min(),0)), |
---|
339 | #int(round(lon.max(),0)) + a_small_number, meridians_delta) |
---|
340 | int(round(lon.max(),0)) + 5 + a_small_number, meridians_delta) |
---|
341 | meridians = n.array(meridians) |
---|
342 | map.drawmeridians(meridians, labels=[1,0,0,1]) |
---|
343 | |
---|
344 | parallels = n.arange(int(round(lat.min(),0)), |
---|
345 | #int(round(lat.max(),0)) + a_small_number, parallels_delta) |
---|
346 | int(round(lat.max(),0)) + 5 + a_small_number, parallels_delta) |
---|
347 | parallels = n.array(parallels) |
---|
348 | map.drawparallels(parallels, labels=[1,0,0,1]) |
---|
349 | |
---|
350 | #plot canberra |
---|
351 | if 1: |
---|
352 | map.plot([149.133],[-35.283],'bo', ms=3) |
---|
353 | |
---|
354 | if contour_labels: |
---|
355 | p.clabel(cset, cset.levels[::2], |
---|
356 | colors='k', fontsize=8, fmt='%i') |
---|
357 | |
---|
358 | if colorbar: |
---|
359 | format = '%.'+ str(significant_digits) + 'f' |
---|
360 | p.colorbar(csetf, orientation='horizontal', shrink=0.7, |
---|
361 | fraction=0.02, pad=0.07, aspect=70, |
---|
362 | format = format) |
---|
363 | |
---|
364 | p.title(title_string) |
---|
365 | if file_name: |
---|
366 | p.savefig(file_name,dpi=dpi) |
---|
367 | p.close(fig) |
---|
368 | del fig |
---|
369 | if not monochrome: |
---|
370 | del csetf |
---|
371 | if wind_vector is None: |
---|
372 | del cset |
---|
373 | if wind_vector is not None: |
---|
374 | del quiv |
---|
375 | gc.collect() |
---|
376 | if return_map: |
---|
377 | return map |
---|
378 | else: |
---|
379 | del map |
---|
380 | gc.collect() |
---|
381 | return |
---|
382 | |
---|
383 | def flip_yaxis(slab): |
---|
384 | shape = slab.shape |
---|
385 | # assuming (lat, lon) ordering |
---|
386 | lat_idx_max = shape[0] - 1 |
---|
387 | flipped_slab = n.zeros(shape) |
---|
388 | for lat_idx in range(lat_idx_max, -1, -1): |
---|
389 | flipped_idx = abs(lat_idx - lat_idx_max) |
---|
390 | flipped_slab[flipped_idx] = slab[lat_idx] |
---|
391 | return flipped_slab |
---|
392 | |
---|
393 | |
---|
394 | def set_cntr_lvl(data, |
---|
395 | intervals=12, |
---|
396 | only_positive=False, |
---|
397 | significant_digits=0, |
---|
398 | forced_max=None, |
---|
399 | forced_min=None): |
---|
400 | |
---|
401 | import math as m |
---|
402 | dummy = data * 10**significant_digits |
---|
403 | min = dummy.min() |
---|
404 | max = dummy.max() |
---|
405 | floor = m.floor(min) |
---|
406 | ceil = m.ceil(max) |
---|
407 | if forced_max: |
---|
408 | ceil = forced_max * 10**significant_digits |
---|
409 | if forced_min: |
---|
410 | floor = forced_min * 10**significant_digits |
---|
411 | if only_positive: |
---|
412 | floor = 0 |
---|
413 | extent = float(ceil - floor) |
---|
414 | delta = extent / intervals |
---|
415 | #cntr_lvl = n.arange(floor, ceil + a_small_number, delta) |
---|
416 | cntr_lvl = n.arange(floor - a_small_number, ceil + a_small_number, delta) |
---|
417 | return cntr_lvl / 10**significant_digits |
---|
418 | |
---|
419 | def lin_interp(xa, xb, ya, yb, x): |
---|
420 | """linear interpolation in two dimensions |
---|
421 | |
---|
422 | USAGE |
---|
423 | xa = original x-grid |
---|
424 | ya = original y-grid |
---|
425 | xb = new x-grid |
---|
426 | yb = new y-grid |
---|
427 | x = data in xa, ya |
---|
428 | |
---|
429 | """ |
---|
430 | slope = (yb - ya) / (xb - xa) |
---|
431 | y = ya + slope * (x - xa) |
---|
432 | return y |
---|
433 | |
---|
434 | def plot_slice( |
---|
435 | ordinate, |
---|
436 | data, |
---|
437 | abscissa = None, |
---|
438 | abscissa_label = None, |
---|
439 | land_mask = None, |
---|
440 | cntr_lvl = None, |
---|
441 | wind_vector = None, |
---|
442 | log_scale = False, |
---|
443 | pressure = True, |
---|
444 | ordinate_quiv_skip = 1, |
---|
445 | abscissa_quiv_skip = 3, |
---|
446 | xmin = None, |
---|
447 | xmax = None, |
---|
448 | ymin = None, |
---|
449 | ymax = None, |
---|
450 | significant_digits = 0, |
---|
451 | title_string = 'N/A', |
---|
452 | file_name = None, |
---|
453 | dpi = 100 |
---|
454 | ): |
---|
455 | ''' |
---|
456 | plot a slice |
---|
457 | Usage: |
---|
458 | >>> plot_vertical_slice(ordinate, data, abscissa, wind_vector) |
---|
459 | where |
---|
460 | ordinate is either pressure or height. By default pressure is assumed |
---|
461 | data is a vertical slice |
---|
462 | abscissa is either 1D or 2D |
---|
463 | ''' |
---|
464 | if log_scale: |
---|
465 | ordinate = n.log10(ordinate) |
---|
466 | # if the abscissa is not supplied than simply use the record numbers |
---|
467 | if abscissa is None: |
---|
468 | x = n.arange(1,data.shape[1]+1) |
---|
469 | abscissa = n.zeros(data.shape) |
---|
470 | for y_idx in range(data.shape[0]): |
---|
471 | abscissa[y_idx] = x |
---|
472 | if cntr_lvl is not None: |
---|
473 | cset = p.contourf(abscissa, ordinate, data, cntr_lvl) |
---|
474 | else: |
---|
475 | cset = p.contourf(abscissa, ordinate, data) |
---|
476 | p.xlabel('record number') |
---|
477 | else: |
---|
478 | # let's handle 1D abscissa arrays |
---|
479 | if len(abscissa.shape) == 1: |
---|
480 | dummy = n.zeros(data.shape) |
---|
481 | for lvl_idx in range(data.shape[0]): |
---|
482 | dummy[lvl_idx] = abscissa |
---|
483 | abscissa = dummy |
---|
484 | del dummy |
---|
485 | if cntr_lvl is not None: |
---|
486 | cset = p.contourf(abscissa, ordinate, data, cntr_lvl) |
---|
487 | else: |
---|
488 | cset = p.contourf(abscissa, ordinate, data) |
---|
489 | if abscissa_label: |
---|
490 | p.xlabel(abscissa_label) |
---|
491 | else: |
---|
492 | p.xlabel('N/A') |
---|
493 | if wind_vector: |
---|
494 | p.quiver(abscissa[::ordinate_quiv_skip,::abscissa_quiv_skip], |
---|
495 | ordinate[::ordinate_quiv_skip,::abscissa_quiv_skip], |
---|
496 | wind_vector[0][::ordinate_quiv_skip,::abscissa_quiv_skip], |
---|
497 | wind_vector[1][::ordinate_quiv_skip,::abscissa_quiv_skip]) |
---|
498 | if land_mask is not None: |
---|
499 | land = ma.masked_where(land_mask == 2,ordinate[0]) |
---|
500 | p.plot(abscissa[0], land, color=(0.59,0.29,0.0), linewidth=2.) |
---|
501 | # if you also want to plot the ocean uncomment the following lines |
---|
502 | #if log_scale: |
---|
503 | # ocean = ma.masked_where(land_mask == 1, n.log10(1013.25)) |
---|
504 | #else: |
---|
505 | # ocean = ma.masked_where(land_mask == 1, 1013.25) |
---|
506 | #p.plot(abscissa[0], ocean, color=(0.0,0.0,1.0), linewidth=2.) |
---|
507 | if pressure: |
---|
508 | # I am assuming pressure will be expressed in hPa |
---|
509 | yticks_location = n.arange(1000.,99.,-100.) |
---|
510 | if log_scale: |
---|
511 | p.yticks(n.log10(yticks_location), |
---|
512 | [str(int(e)) for e in yticks_location]) |
---|
513 | p.ylabel('log10 of pressure') |
---|
514 | for e in n.log10(yticks_location): |
---|
515 | p.axhline(e,linestyle='--',color=(0.7,0.7,0.7)) |
---|
516 | # the -5. is there to create a bit of a top margin |
---|
517 | if ordinate.max() > n.log10(1013.25): |
---|
518 | cheat_ymin = ordinate.max() |
---|
519 | else: |
---|
520 | cheat_ymin = n.log10(1013.25 + 5.) |
---|
521 | p.ylim(ymin=cheat_ymin, |
---|
522 | ymax=n.log10(10**ordinate.min() - 5.)) |
---|
523 | else: |
---|
524 | p.yticks( yticks_location, |
---|
525 | [str(int(e)) for e in yticks_location]) |
---|
526 | p.ylabel('pressure (hPa)') |
---|
527 | for e in yticks_location: |
---|
528 | p.axhline(e,linestyle='--',color=(0.7,0.7,0.7)) |
---|
529 | # the -25. is there to create a bit of a top margin |
---|
530 | if ordinate.max() > 1013.25: |
---|
531 | cheat_ymin = ordinate.max() |
---|
532 | else: |
---|
533 | cheat_ymin = 1013.25 + 10. |
---|
534 | p.ylim(ymin=cheat_ymin, ymax=ordinate.min() - 25.) |
---|
535 | # p.ylim(ymin=ordinate.max(), ymax=ordinate.min() - 25.) |
---|
536 | # if any of the axes boundaries have been given explicitly, we'll |
---|
537 | # them |
---|
538 | if log_scale: |
---|
539 | if xmin is not None: |
---|
540 | p.xlim(xmin=n.log10(xmin)) |
---|
541 | if xmax is not None: |
---|
542 | p.xlim(xmax=n.log10(xmax)) |
---|
543 | if ymin is not None: |
---|
544 | p.ylim(ymin=n.log10(ymin)) |
---|
545 | if ymax is not None: |
---|
546 | p.ylim(ymax=n.log10(ymax)) |
---|
547 | else: |
---|
548 | if xmin is not None: |
---|
549 | p.xlim(xmin=xmin) |
---|
550 | if xmax is not None: |
---|
551 | p.xlim(xmax=xmax) |
---|
552 | if ymin is not None: |
---|
553 | p.ylim(ymin=ymin) |
---|
554 | if ymax is not None: |
---|
555 | p.ylim(ymax=ymax) |
---|
556 | |
---|
557 | else: |
---|
558 | print 'I assume you are trying to plot in z coordinate: ' \ |
---|
559 | + 'sorry not implemented yet' |
---|
560 | format = '%.'+ str(significant_digits) + 'f' |
---|
561 | p.colorbar(cset, orientation='horizontal', shrink=0.7, |
---|
562 | #fraction=0.02, pad=0.095, aspect=70, |
---|
563 | fraction=0.02, pad=0.1, aspect=70, |
---|
564 | format = format) |
---|
565 | p.title(title_string) |
---|
566 | if file_name: |
---|
567 | p.savefig(file_name,dpi=dpi) |
---|
568 | p.close() |
---|
569 | |
---|
570 | def write_to_log_file(log_file, message): |
---|
571 | ''' |
---|
572 | This functions opens, writes to it, |
---|
573 | and closes the log file so that tools like tail -f |
---|
574 | will work as intended. |
---|
575 | It also prepends a time stamp to each entry. |
---|
576 | It is assumed that the output_dir and log_file are defined in the |
---|
577 | namespace from which the function is called. |
---|
578 | ''' |
---|
579 | log_file = open(log_file, 'a') |
---|
580 | log_file.write(time.ctime(time.time()) + ' -> ' + message + '\n') |
---|
581 | log_file.close() |
---|
582 | |
---|
583 | def generate_output_file_name(output_dir, prefix, timetuple): |
---|
584 | """Returns the output file name built by joining the output directory |
---|
585 | path with the supplied prefix and the timetuple which is used to |
---|
586 | construct the suffix/timestamp |
---|
587 | """ |
---|
588 | output_file_name = prefix |
---|
589 | output_file_name += str(timetuple[0]) |
---|
590 | output_file_name += str(timetuple[1]).zfill(2) |
---|
591 | output_file_name += str(timetuple[2]).zfill(2) |
---|
592 | output_file_name += str(timetuple[3]).zfill(2) |
---|
593 | output_file_name += str(timetuple[4]).zfill(2) |
---|
594 | output_file_name += str(timetuple[5]).zfill(2) |
---|
595 | output_file_name = os.path.join(output_dir, output_file_name) |
---|
596 | return output_file_name |
---|
597 | |
---|
598 | def process_command_line_arguments(argv): |
---|
599 | """ |
---|
600 | Process command line arguments in a consistent fashion for the |
---|
601 | plot_wrfout elementary scripts. |
---|
602 | """ |
---|
603 | |
---|
604 | def process_time_window_argument(argument): |
---|
605 | """ |
---|
606 | Process the time window argument and return a tuple of datetime objects |
---|
607 | (start_time, end_time) |
---|
608 | """ |
---|
609 | import datetime |
---|
610 | try: |
---|
611 | dummy = (argument).split('-') |
---|
612 | start_time = dummy[0].split('/') |
---|
613 | start_time = [int(e) for e in start_time] |
---|
614 | except: |
---|
615 | sys.exit('\nERROR:\tI cannot make sense of the time window ' |
---|
616 | + argument + '\n\tI need something like ' |
---|
617 | + 'yyyy/mm/dd/hh/mm/ss-yyyy/mm/dd/hh/mm/ss but will also ' |
---|
618 | + 'cope without hours, minutes or seconds.') |
---|
619 | # this is only intended to work if we have missing hour, minute, or |
---|
620 | # seconds. At least the date ought to be complete! |
---|
621 | while len(start_time) < 6: |
---|
622 | start_time.append(0) |
---|
623 | year, month, day, hour, minute, second = start_time |
---|
624 | start_time = \ |
---|
625 | datetime.datetime(year, month, day, hour, minute, second) |
---|
626 | end_time = dummy[1].split('/') |
---|
627 | end_time = [int(e) for e in end_time] |
---|
628 | # this is only intended to work if we have missing hour, minute, or |
---|
629 | # seconds. At least the date ought to be complete! |
---|
630 | while len(end_time) < 6: |
---|
631 | end_time.append(0) |
---|
632 | year, month, day, hour, minute, second = end_time |
---|
633 | end_time = \ |
---|
634 | datetime.datetime(year, month, day, hour, minute, second) |
---|
635 | #print start_time.ctime(), end_time.ctime() |
---|
636 | return (start_time, end_time) |
---|
637 | |
---|
638 | if len(argv) < 2: |
---|
639 | sys.exit( '\nERROR:\tI need (at least) one file to extract the data from...' |
---|
640 | + '\n\tPlease try something like:\n\tpython ' + argv[0] \ |
---|
641 | + ' wrfout_dpp_yyyy-mm-dd_hh:mm:ss') |
---|
642 | else: |
---|
643 | # Let's store the name of the calling script and considered it |
---|
644 | # processed |
---|
645 | caller = argv.pop(0) |
---|
646 | # let's start with checking that the the first argument is a legit wrfout |
---|
647 | # file name and that it exists |
---|
648 | input_file = argv.pop(0) |
---|
649 | # this should be more stringent, but I don't have time |
---|
650 | if 'wrfout' not in input_file: |
---|
651 | sys.exit( |
---|
652 | '\nERROR:\t' + input_file + ' is not a standard wrf output file' |
---|
653 | + 'name.\n\tI need something like ' |
---|
654 | + 'wrfout_dpp_yyyy-mm-dd_hh:mm:ss') |
---|
655 | if not os.path.isfile(input_file): |
---|
656 | sys.exit( '\nERROR:\tfile ' + input_file + ' does not exist!') |
---|
657 | output_dir = None |
---|
658 | time_window = None |
---|
659 | # let's have a look at the arguments left, if any. |
---|
660 | # every command line argument that is not the input file will come as a |
---|
661 | # pair flag/value to avoid confusion. If the program cannot make sense of |
---|
662 | # the input arguments it will bail out with a (possibly helpful) error |
---|
663 | # message |
---|
664 | if '--output_dir' in argv: |
---|
665 | flag_idx = argv.index('--output_dir') |
---|
666 | value_idx = flag_idx + 1 |
---|
667 | # the order of the following two statements is important not to |
---|
668 | # break the way we use pop |
---|
669 | output_dir = argv.pop(value_idx) |
---|
670 | flag = argv.pop(flag_idx) |
---|
671 | if not os.path.isdir(output_dir): |
---|
672 | sys.exit('\nERROR:\t' + output_dir + 'is not a directory!') |
---|
673 | if '-o' in argv: |
---|
674 | flag_idx = argv.index('-o') |
---|
675 | value_idx = flag_idx + 1 |
---|
676 | # the order of the following two statements is important not to |
---|
677 | # break the way we use pop |
---|
678 | output_dir = argv.pop(value_idx) |
---|
679 | flag = argv.pop(flag_idx) |
---|
680 | if not os.path.isdir(output_dir): |
---|
681 | sys.exit('\nERROR:\t' + output_dir + 'is not a directory!') |
---|
682 | if '--time_window' in argv: |
---|
683 | flag_idx = argv.index('--time_window') |
---|
684 | value_idx = flag_idx + 1 |
---|
685 | # the order of the following two statements is important not to |
---|
686 | # break the way we use pop |
---|
687 | time_argument = argv.pop(value_idx) |
---|
688 | flag = argv.pop(flag_idx) |
---|
689 | time_window = process_time_window_argument(time_argument) |
---|
690 | if '-t' in argv: |
---|
691 | flag_idx = argv.index('-t') |
---|
692 | value_idx = flag_idx + 1 |
---|
693 | # the order of the following two statements is important not to |
---|
694 | # break the way we use pop |
---|
695 | time_argument = argv.pop(value_idx) |
---|
696 | flag = argv.pop(flag_idx) |
---|
697 | time_window = process_time_window_argument(time_argument) |
---|
698 | if len(argv) != 0: |
---|
699 | unknown_arguments = '' |
---|
700 | for argument in argv: |
---|
701 | unknown_arguments += argument + '\n\t' |
---|
702 | sys.exit('\nERROR:\tI do not know how to process the following ' |
---|
703 | + 'arguments:\n\t' + unknown_arguments) |
---|
704 | |
---|
705 | return input_file, output_dir, time_window |
---|
706 | |
---|