[183] | 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 | |
---|