cb = figs[locName][0].colorbar(radar, cax=cax, orientation='horizontal') cb.set_label( 'Simulated Radar Reflectivity (dBZ)\n\nBarbs: Half=2.5 m/s, Full=5 m/s, Flag=25 m/s' ) # # Add nearby MesoWest MW_date = TS_temp['DATETIME'][hh] b = get_mesowest_radius(MW_date, 15, extra='&radius=%s,%s,60' % (l['latitude'], l['longitude']), variables='wind_speed,wind_direction') if len(b['NAME']) > 0: MW_u, MW_v = wind_spddir_to_uv(b['wind_speed'], b['wind_direction']) MWx, MWy = maps[locName](b['LON'], b['LAT']) MW_barbs = figs[locName][1].barbs(MWx, MWy, MW_u, MW_v, color='r', barb_increments=dict(half=2.5, full=5, flag=25)) # # Wind Barbs # Overlay wind barbs (need to trim this array before we plot it) # First need to trim the array barbs = figs[locName][1].barbs(trim_X, trim_Y,
# Get SLC sounding for Salt Lake City balloon = get_wyoming_sounding(DATE) # Get WRF from TS list profile data station = 'KSLC' wrf_dir = '/uufs/chpc.utah.edu/common/home/horel-group4/model/bblaylock/WRF3.8.1_MYNN/DATA/' tsfile = station + '.d02.TS' model_start = datetime(2015, 6, 14, 0) wrf = get_vert_data(wrf_dir+tsfile,model_start,DATE) # Get HRRR analysis profile from *modified* BUFR file # (modified so all data was on one row for easy reading) hrrr_file = 'kslc_' + DATE.strftime('%Y%m%d%H') + '.txt' HRRR = np.genfromtxt(hrrr_file, delimiter=' ', names=True) HRRR_theta = TempPress_to_PotTemp(HRRR['TMPC'], HRRR['PRES']) HRRR_u, HRRR_v = wind_calcs.wind_spddir_to_uv(HRRR['SKNT']*0.51444, HRRR['DRCT']) HRRR_mixr = DwptPress_to_MixRatio(HRRR['DWPC'], HRRR['PRES']) # Get HRRR 1-hour forecast f = 'F01' hrrr_file = 'kslc_'+datetime(2015,6,18,23).strftime('%Y%m%d%H')+'_'+f+'.txt' HRRR_f01 = np.genfromtxt(hrrr_file, delimiter=' ', names=True) HRRR_f01_theta = TempPress_to_PotTemp(HRRR_f01['TMPC'], HRRR_f01['PRES']) HRRR_f01_u, HRRR_f01_v = wind_calcs.wind_spddir_to_uv(HRRR_f01['SKNT']*0.51444, HRRR_f01['DRCT']) HRRR_f01_mixr = DwptPress_to_MixRatio(HRRR_f01['DWPC'], HRRR_f01['PRES']) """ Create 3 subplots for 1. Potential Temperature 2. Mixing Ratio 3. Vector Wind
def plot_pm25_map(mobile, other_mobile=False, auto_map_boundaries=True, background='WRF'): print "--plot map--" """ makes a map of the helicopter observations. Also plots MesoWest input: mobile=ksl observations from the get moblie function other_mobile: if set to true will plot truck and trax data if available auto_map_boundaries: will find the location of the helicopter and will set auto boundaries else, will map the salt lake valley or you can change the lat/lon manually background: 'WRF' will use a wrf file to contour the boundary 'sat' will use an old satellite image 'topo' will use a topographical image return: a string of the URL the figure is saved """ plt.cla() plt.clf() plt.close() width = 4 height = 4.5 fig, (ax1) = plt.subplots(1, 1, figsize=(width, height)) ## ---- Draw map top_right_lat = 40.9 + .1 top_right_lon = -111.60 bot_left_lat = 40.4 + .05 bot_left_lon = -112.19785 - .05 # Might need to make the map bigger if the chopper flew out of the valley try: if np.nanmax(mobile['latitude']) > top_right_lat: top_right_lat = np.nanmax(mobile['latitude']) + .01 if np.nanmin( mobile['latitude'][mobile['latitude'] > -9999]) < bot_left_lat: bot_left_lat = np.nanmin( mobile['latitude'][mobile['latitude'] > -9999]) - .01 if np.nanmax(mobile['longitude']) > top_right_lon: top_right_lon = np.nanmax(mobile['longitude']) + .01 if np.nanmin(mobile['longitude'][ mobile['longitude'] > -9999]) < bot_left_lon: bot_left_lon = np.nanmin( mobile['longitude'][mobile['longitude'] > -9999]) - .01 except: # something funny with the lat/lon data not having a max or min # must have ozone or pm25 data but no lat lon data pass ## Map in cylindrical projection (data points may apear skewed) m = Basemap(resolution='i',projection='cyl',\ llcrnrlon=bot_left_lon,llcrnrlat=bot_left_lat,\ urcrnrlon=top_right_lon,urcrnrlat=top_right_lat,) # -----Set Background images if background == 'WRF': ## ---- Grab terrain data directory = '/uufs/chpc.utah.edu/common/home/horel-group4/model/bblaylock/WRF3.7_spinup/DATA/FULL_RUN_June14-19/' spat = 'auxhist23_d02_2015-06-14_00:00:00' nc_spatial = netcdf.netcdf_file(directory + spat, 'r') # This sets the standard grid point structure at full resolution # Converts WRF lat and long to the maps x an y coordinate XLONG = nc_spatial.variables['XLONG'][0] XLAT = nc_spatial.variables['XLAT'][0] HGT = nc_spatial.variables['HGT'][0, :, :] #topography landmask = nc_spatial.variables['LANDMASK'][0, :, :] # Plot Terrain X, Y = m(XLONG, XLAT) plt.contourf(X, Y, HGT, levels=np.arange(1000, 4000, 500), cmap='binary') # Draw major roads from shapefile m.readshapefile( '/uufs/chpc.utah.edu/common/home/u0553130/shape_files/tl_2015_UtahRoads_prisecroads/tl_2015_49_prisecroads', 'roads', linewidth=.2) ## Contour Lake Outline plt.contour(X, Y, landmask, [0, 1], linewidths=1, colors="b") maps = [ 'ESRI_Imagery_World_2D', # 0 'ESRI_StreetMap_World_2D', # 1 'NatGeo_World_Map', # 2 'NGS_Topo_US_2D', # 3 'Ocean_Basemap', # 4 'USA_Topo_Maps', # 5 'World_Imagery', # 6 'World_Physical_Map', # 7 'World_Shaded_Relief', # 8 'World_Street_Map', # 9 'World_Terrain_Base', # 10 'World_Topo_Map' # 11 ] if background == 'sat': ## Instead of using WRF terrain fields you can get a high resolution image from ESRI m.arcgisimage(service=maps[0], xpixels=1000, verbose=True) if background == 'topo': ## Instead of using WRF terrain fields you can get a high resolution image from ESRI m.arcgisimage(service=maps[8], xpixels=1000, verbose=True) # Draw major roads from shapefile m.readshapefile( '/uufs/chpc.utah.edu/common/home/u0553130/shape_files/tl_2015_UtahRoads_prisecroads/tl_2015_49_prisecroads', 'roads', linewidth=.2) # can use any other map background from maps list # Plot Other Mobile data if other_mobile == True: # Plot Mobile Ozone points (TRAX01) try: x, y = m(trax_mobile['longitude'], trax_mobile['latitude']) m.scatter(x[:], y[:], c=trax_mobile['pm25'][:], vmax=45, vmin=0., lw=.3, marker='d', s=30, cmap=plt.cm.get_cmap('jet'), zorder=50) except: print 'no TRAX01 data' # Plot Mobile Truck points (UUTK3) try: x, y = m(trk3_mobile['longitude'], trk3_mobile['latitude']) m.scatter(x[:], y[:], c=trk3_mobile['pm25'][:], vmax=45, vmin=0., lw=.3, marker='p', s=30, cmap=plt.cm.get_cmap('jet'), zorder=50) except: print 'no UUTK3 data' # Plot Mobile Truck points (UUTK1) try: x, y = m(trk1_mobile['longitude'], trk1_mobile['latitude']) m.scatter(x[:], y[:], c=trk1_mobile['pm25'][:], vmax=45, vmin=0., lw=.3, marker='p', s=30, cmap=plt.cm.get_cmap('jet'), zorder=50) except: print 'no UUTK1 data' ## Plot MesoWest (top of the hour, +/- 10 mins) # Plot MesoWest wind data mesowest_time = str(request_time.year).zfill(2) + str( request_time.month).zfill(2) + str(request_time.day).zfill(2) + str( request_time.hour).zfill(2) + '00' print 'plotting mesowest observations within 10 minutes of top of the hour', mesowest_time, ' because the request time is:', request_time a = get_mesowest_radius(request_time, '10') u, v = wind_spddir_to_uv(a['wind_speed'], a['wind_direction']) m.barbs(a['LON'], a['LAT'], u, v, length=4.5, linewidth=.5, barb_increments=dict(half=1, full=2, flag=10), sizes=dict(spacing=0.15, height=0.3, emptybarb=.1)) # ozone m.scatter(a['LON'], a['LAT'], c=a['pm25'], vmax=45, vmin=0., lw=.3, s=20, marker='s', cmap=plt.cm.get_cmap('jet'), zorder=50) ## plot KSL on top of everything becuase it is flying # Plot Mobile Ozone points (KSL) x, y = m(mobile['longitude'], mobile['latitude']) # plot every 30 seconds rather than every 10 seconds (thus the [::3] indexes) m.scatter(x[::3], y[::3], c=ksl_mobile['pm25'][::3], vmax=45, vmin=0., lw=.3, s=30, cmap=plt.cm.get_cmap('jet'), zorder=50) cbar = plt.colorbar(orientation='horizontal', extend='both', shrink=.8, pad=.03) cbar.ax.set_xlabel('PM 2.5 (micrograms m-3)', fontsize=10) cbar.ax.tick_params(labelsize=8) cbar.set_ticks([0, 5, 10, 15, 20, 25, 30, 35, 40, 45]) # Manually plot other Sensor locations by lat/lon """ m.scatter(-111.93,40.9669, s=75, c='w',zorder=40) m.scatter(-112.01448,40.71152, s=75, c='b',zorder=40) # NAA m.scatter(-111.93072,40.95733, s=75, c='darkorange',zorder=40) # O3S02 m.scatter(-111.828211,40.766573, s=75, c='r',zorder=40) # MTMET m.scatter(-111.8717,40.7335, s=75, c='darkgreen',zorder=40) # QHW m.scatter(-111.96503,40.77069, s=75, c='w',zorder=40) # SLC m.scatter(-112.34551,40.89068 , s=75, c='w',zorder=40) # GSLBY """ ## Sidebar Text # is trax_in_yard, is trax available? if trax_mobile == 'No Data Available from TRX01 at this time': trax_time_range = 'n/a' trax_ozone_text = 'TRAX \n n/a' else: trax_in_yard = '' if trax_mobile['longitude'].max() == -9999: trax_in_yard = ' -Yard-' trax_time_range = trax_mobile['DATES'][0].strftime( '%H:%M - ') + trax_mobile['DATES'][-1].strftime('%H:%M UTC') trax_ozone_text = 'TRAX' + trax_in_yard + '\n Max: %.2f ug m-3\n Mean: %.2f ug m-3\n Min: %.2f ug m-3' % ( np.nanmax(trax_mobile['pm25']), np.mean(trax_mobile['pm25'][trax_mobile['pm25'] > 0]), np.nanmin(trax_mobile['pm25'])) obs_day = mobile['DATES'][-1].strftime('%d %b %Y') ksl_time_range = mobile['DATES'][0].strftime( '%H:%M - ') + mobile['DATES'][-1].strftime('%H:%M UTC') mesowest_time_range = datetime.strptime(mesowest_time, "%Y%m%d%H%M").strftime('%H:%M UTC') date_text = '%s\nKSL: %s\nTRAX: %s\nMesoWest: %s' % ( obs_day, ksl_time_range, trax_time_range, mesowest_time_range) ksl_ozone_text = 'KSL \n Max: %.2f ug m-3\n Mean: %.2f ug m-3\n Min: %.2f ug m-3' % ( np.nanmax(mobile['pm25']), np.mean( mobile['pm25'][mobile['pm25'] > 0]), np.nanmin(mobile['pm25'])) mesowest_ozone_text = '' for i in range(0, len(a['pm25'][a['pm25'] > 0])): station_obs = '%s: %.2f\n' % (a['STID'][a['pm25'] > 0][i], a['pm25'][a['pm25'] > 0][i]) mesowest_ozone_text += station_obs all_text = date_text + '\n\n' + ksl_ozone_text + '\n\n' + trax_ozone_text + '\n\n' + mesowest_ozone_text fig.text(.93, .8, all_text, fontname='monospace', va='top', backgroundcolor='white', fontsize=7) plt.savefig(FIGDIR + string_time + '_KSL5_map_pm25.png', dpi=500, bbox_inches="tight") print 'saved map', FIGDIR + string_time + '_KSL5_map_pm25.png' print "" return 'http://home.chpc.utah.edu/~u0553130/oper/KSL_daily/' + string_time + '_KSL5_map_pm25.png\n'
def get_wyoming_sounding(request_date, station='slc'): """ June 13, 2016 Download a sounding file from University of Wyoming Site and return a dictinary of the values Input: request_date - a datetime object in UTC. Hour must be either 0 or 12 station - defaults to slc, the Salt Lake City. Alternativley use a number for the station identier. Return: a dictionary of the data """ if station == 'slc': stn = '72572' # this is the id number for KSLC else: stn = str(station) year = str(request_date.year).zfill(4) month = str(request_date.month).zfill(2) day = str(request_date.day).zfill(2) # hour in UTC, 00 and 12 z usually available hour = str(request_date.hour).zfill(2) # Download, process and add to plot the Wyoming Data # 1) # Wyoming URL to download Sounding from url = 'http://weather.uwyo.edu/cgi-bin/sounding?' \ + 'region=naconf&TYPE=TEXT%3ALIST' \ + '&YEAR=' + year \ + '&MONTH=' + month \ + '&FROM=' + day + hour \ + '&TO=' + day + hour \ + '&STNM=' + stn content = urllib2.urlopen(url).read() # 2) # Remove the html tags soup = BeautifulSoup(content, "html.parser") data_text = soup.get_text() # 3) # Split the content by new line. splitted = data_text.split("\n", data_text.count("\n")) # 4) # Save the processed data as a .txt file to be read in by the skewt module. # See more here: https://pypi.python.org/pypi/SkewT Sounding_dir = './' Sounding_filename = str(stn) + '.' + str(year) + \ str(month) + str(day) + str(hour) + '.txt' f = open(Sounding_dir + Sounding_filename, 'w') for line in splitted[4:]: f.write(line + '\n') f.close() # 5) Read the observed sounding file # Figure out where the footer is so we can skip it in np.genfromtxt # This is the line the data ends f = open(Sounding_filename, 'r') ls = f.readlines() f.close() header = 6 # There are at least six header rows data_in_row = [len(i.split()) for i in ls] for i in data_in_row[header:]: # Add a header row if 11 data points don't exist. if i != 11: header += 1 else: break data_rows = 0 # count number of data rows for i in data_in_row[header:]: # as long as there are 11 data points in the row, add to data_rows if i == 11: data_rows += 1 else: break # calculate number of lines in the footer last_line = len(ls) foot = last_line - header - data_rows sounding = np.genfromtxt(Sounding_filename, skip_header=header, skip_footer=foot) obs_press = sounding[:, 0] # hPa obs_hght = sounding[:, 1] # m obs_temp = sounding[:, 2] # C obs_dwpt = sounding[:, 3] # C obs_rh = sounding[:, 4] # % obs_mixing = sounding[:, 5] / 1000 # kg/kg obs_wdir = sounding[:, 6] # degrees obs_wspd = sounding[:, 7] * 0.51444 # m/s obs_theta = sounding[:, 8] # K obs_u, obs_v = wind_calcs.wind_spddir_to_uv(obs_wspd, obs_wdir) # m/s # 6) # Would be nice to return a diction of the station information from the # sounding file such as the Station identified, latitude, longitude, # calculated indexes, etc. data = { 'url': url, # URL the data is retrived from 'file': Sounding_filename, # Path to the file we created 'DATE': request_date, # Date requested (datetime object) 'station': str(station), # Requested station 'press': obs_press, 'height': obs_hght, 'temp': obs_temp, 'dwpt': obs_dwpt, 'rh': obs_rh, 'mixing ratio': obs_mixing, 'wdir': obs_wdir, 'wspd': obs_wspd, 'theta': obs_theta, 'u': obs_u, 'v': obs_v } return data