def test_plot_symbol_fontsize(): """Test changing fontsize in plotting of symbols.""" fig = plt.figure(figsize=(3, 3)) ax = plt.subplot(1, 1, 1) sp = StationPlot(ax, [0], [0], fontsize=8, spacing=32) sp.plot_symbol('E', [92], current_weather) sp.plot_symbol('W', [96], current_weather, fontsize=100) return fig
def test_symbol_pandas_timeseries(): """Test the usage of Pandas DatetimeIndex as a valid `x` input into StationPlot.""" pd.plotting.register_matplotlib_converters() rng = pd.date_range('12/1/2017', periods=5, freq='D') sc = [1, 2, 3, 4, 5] ts = pd.Series(sc, index=rng) fig, ax = plt.subplots() y = np.ones(len(ts.index)) stationplot = StationPlot(ax, ts.index, y, fontsize=12) stationplot.plot_symbol('C', ts, sky_cover) ax.xaxis.set_major_locator(matplotlib.dates.DayLocator()) ax.xaxis.set_major_formatter(matplotlib.dates.DateFormatter('%-d')) return fig
def create_ornaments(n_ornaments,color='k'): """create ornaments and return the artist.""" ornaments_y=np.random.triangular(3,3.5,24.5,n_ornaments) bounds=get_x_bounds(ornaments_y) ornaments_x=np.random.rand(n_ornaments)*2*bounds-bounds stationplot=StationPlot(ax,ornaments_x,ornaments_y,clip_on=True,fontsize=24) symbols=np.array(list(wx_code_map.values())) wx=symbols[np.random.randint(0,102,n_ornaments)] return stationplot.plot_symbol('c',wx,current_weather,color=color)
def test_stationplot_clipping(): """Test the that clipping can be enabled as a default parameter.""" fig = plt.figure(figsize=(9, 9)) # testing data x = np.array([1, 5]) y = np.array([2, 4]) # Make the plot sp = StationPlot(fig.add_subplot(1, 1, 1), x, y, fontsize=16, clip_on=True) sp.plot_barb([20, 0], [0, -50]) sp.plot_text('E', ['KOKC', 'ICT'], color='blue') sp.plot_parameter('NW', [10.5, 15] * units.degC, color='red') sp.plot_symbol('S', [5, 7], high_clouds, color='green') sp.ax.set_xlim(1, 5) sp.ax.set_ylim(1.75, 4.25) return fig
def test_stationplot_api(): """Test the StationPlot API.""" fig = plt.figure(figsize=(9, 9)) # testing data x = np.array([1, 5]) y = np.array([2, 4]) # Make the plot sp = StationPlot(fig.add_subplot(1, 1, 1), x, y, fontsize=16) sp.plot_barb([20, 0], [0, -50]) sp.plot_text('E', ['KOKC', 'ICT'], color='blue') sp.plot_parameter('NW', [10.5, 15] * units.degC, color='red') sp.plot_symbol('S', [5, 7], high_clouds, color='green') sp.ax.set_xlim(0, 6) sp.ax.set_ylim(0, 6) return fig
def test_stationplot_api(): """Test the StationPlot API.""" fig = plt.figure(figsize=(9, 9)) # testing data x = np.array([1, 5]) y = np.array([2, 4]) # Make the plot sp = StationPlot(fig.add_subplot(1, 1, 1), x, y, fontsize=16) sp.plot_barb([20, 0], [0, -50]) sp.plot_text("E", ["KOKC", "ICT"], color="blue") sp.plot_parameter("NW", [10.5, 15], color="red") sp.plot_symbol("S", [5, 7], high_clouds, color="green") sp.ax.set_xlim(0, 6) sp.ax.set_ylim(0, 6) return fig
def plot_map_temperature(proj, point_locs, df_t, area='EU', west=-5.5, east=32, south=42, north=62, fonts=14, cm='gist_ncar', path=None, SLP=False): if path is None: # set up the paths and test for existence path = expanduser('~') + '/Documents/Metar_plots' try: os.listdir(path) except FileNotFoundError: os.mkdir(path) else: path = path df = df_t plt.rcParams['savefig.dpi'] = 300 # ========================================================================= # Create the figure and an axes set to the projection. fig = plt.figure(figsize=(20, 16)) ax = fig.add_subplot(1, 1, 1, projection=proj) if area == 'Antarctica': df = df.loc[df['latitude'] < north] ax.set_extent([-180, 180, -90, -60], ccrs.PlateCarree()) theta = np.linspace(0, 2 * np.pi, 100) center, radius = [0.5, 0.5], 0.5 verts = np.vstack([np.sin(theta), np.cos(theta)]).T circle = mpath.Path(verts * radius + center) ax.set_boundary(circle, transform=ax.transAxes) elif area == 'Arctic': df = df.loc[df['latitude'] > south] ax.set_extent([-180, 180, 60, 90], ccrs.PlateCarree()) theta = np.linspace(0, 2 * np.pi, 100) center, radius = [0.5, 0.5], 0.5 verts = np.vstack([np.sin(theta), np.cos(theta)]).T circle = mpath.Path(verts * radius + center) ax.set_boundary(circle, transform=ax.transAxes) else: ax.set_extent((west, east, south, north)) # Set up a cartopy feature for state borders. state_boundaries = feat.NaturalEarthFeature(category='cultural', name='admin_0_countries', scale='10m', facecolor='#d8dcd6', alpha=0.5) ax.coastlines(resolution='10m', zorder=1, color='black') ax.add_feature(state_boundaries, zorder=1, edgecolor='black') # ax.add_feature(cartopy.feature.OCEAN, zorder=0) # Set plot bounds # reset index for easier loop df = df.dropna(how='any', subset=['TT']) df = df.reset_index() cmap = matplotlib.cm.get_cmap(cm) norm = matplotlib.colors.Normalize(vmin=-30.0, vmax=30.0) # Start the station plot by specifying the axes to draw on, as well as the # lon/lat of the stations (with transform). We also the fontsize to 12 pt. index = 0 a = np.arange(-30, 30, 1) for x in a: if index == 0: df_min = df.loc[df['TT'] < min(a)] df_max = df.loc[df['TT'] > max(a)] j = 0 list_ex = [min(a) - 5, max(a) + 5] for arr in [df_min, df_max]: stationplot = StationPlot(ax, arr['longitude'], arr['latitude'], clip_on=True, transform=ccrs.PlateCarree(), fontsize=fonts) Temp = stationplot.plot_parameter('NW', arr['TT'], color=cmap(norm(list_ex[j]))) try: Temp.set_path_effects([ path_effects.Stroke(linewidth=1.5, foreground='black'), path_effects.Normal() ]) except AttributeError: pass j += 1 # slice out values between x and x+1 df_cur = df.loc[(df['TT'] < x + 1) & (df['TT'] >= x)] stationplot = StationPlot(ax, df_cur['longitude'], df_cur['latitude'], clip_on=True, transform=ccrs.PlateCarree(), fontsize=fonts) # plot the sliced values with a different color for each loop Temp = stationplot.plot_parameter('NW', df_cur['TT'], color=cmap(norm(x + 0.5))) try: Temp.set_path_effects([ path_effects.Stroke(linewidth=1.5, foreground='black'), path_effects.Normal() ]) except AttributeError: pass print('x={} done correctly '.format(x)) index += 1 # fontweight = 'bold' # More complex ex. uses custom formatter to control how sea-level pressure # values are plotted. This uses the standard trailing 3-digits of # the pressure value in tenths of millibars. stationplot = StationPlot(ax, df['longitude'].values, df['latitude'].values, clip_on=True, transform=ccrs.PlateCarree(), fontsize=fonts) try: u, v = wind_components(((df['ff'].values) * units('knots')), (df['dd'].values * units.degree)) cloud_frac = df['cloud_cover'] if area != 'Arctic': stationplot.plot_barb(u, v, zorder=1000, linewidth=2) stationplot.plot_symbol('C', cloud_frac, sky_cover) # stationplot.plot_text((2, 0), df['Station']) for val in range(0, 2): wx = df[['ww', 'StationType']] if val == 0: # mask all the unmanned stations wx['ww'].loc[wx['StationType'] > 3] = np.nan wx2 = wx['ww'].fillna(00).astype(int).values.tolist() stationplot.plot_symbol('W', wx2, current_weather, zorder=2000) else: # mask all the manned stations wx['ww'].loc[(wx['StationType'] <= 3)] = np.nan # mask all reports smaller than 9 # =7 is an empty symbol! wx['ww'].loc[wx['ww'] <= 9] = 7 wx2 = wx['ww'].fillna(7).astype(int).values.tolist() stationplot.plot_symbol('W', wx2, current_weather_auto, zorder=2000) # print(u, v) except (ValueError, TypeError) as error: pass if SLP is True: lon = df['longitude'].loc[(df.PressureDefId == 'mean sea level') & (df.Hp <= 750)].values lat = df['latitude'].loc[(df.PressureDefId == 'mean sea level') & (df.Hp <= 750)].values xp, yp, _ = proj.transform_points(ccrs.PlateCarree(), lon, lat).T sea_levelp = df['SLP'].loc[(df.PressureDefId == 'mean sea level') & (df.Hp <= 750)] x_masked, y_masked, pres = remove_nan_observations( xp, yp, sea_levelp.values) slpgridx, slpgridy, slp = interpolate_to_grid(x_masked, y_masked, pres, interp_type='cressman', search_radius=400000, rbf_func='quintic', minimum_neighbors=1, hres=100000, rbf_smooth=100000) Splot_main = ax.contour(slpgridx, slpgridy, slp, colors='k', linewidths=2, extent=(west, east, south, north), levels=list(range(950, 1050, 10))) plt.clabel(Splot_main, inline=1, fontsize=12, fmt='%i') Splot = ax.contour(slpgridx, slpgridy, slp, colors='k', linewidths=1, linestyles='--', extent=(west, east, south, north), levels=[ x for x in range(950, 1050, 1) if x not in list(range(950, 1050, 10)) ]) plt.clabel(Splot, inline=1, fontsize=10, fmt='%i') # stationplot.plot_text((2, 0), df['Station']) # Also plot the actual text of the station id. Instead of cardinal # directions, plot further out by specifying a location of 2 increments # in x and 0 in y.stationplot.plot_text((2, 0), df['station']) if (area == 'Antarctica' or area == 'Arctic'): plt.savefig(path + '/CURR_SYNOP_color_' + area + '.png', bbox_inches='tight', pad_inches=0) else: plt.savefig(path + '/CURR_SYNOP_color_' + area + '.png', bbox_inches='tight', transparent="True", pad_inches=0)
transform=ccrs.PlateCarree(), fontsize=12) # Plot the temperature and dew point to the upper and lower left, respectively, of # the center point. Each one uses a different color. stationplot.plot_parameter('NW', data['air_temperature'], color='red') stationplot.plot_parameter('SW', data['dew_point_temperature'], color='darkgreen') # A more complex example uses a custom formatter to control how the sea-level pressure # values are plotted. This uses the standard trailing 3-digits of the pressure value # in tenths of millibars. stationplot.plot_parameter('NE', data['slp'], formatter=lambda v: format(10 * v, '.0f')[-3:]) # Plot the cloud cover symbols in the center location. This uses the codes made above and # uses the `sky_cover` mapper to convert these values to font codes for the # weather symbol font. stationplot.plot_symbol('C', cloud_frac, sky_cover) # Same this time, but plot current weather to the left of center, using the # `current_weather` mapper to convert symbols to the right glyphs. stationplot.plot_symbol('W', wx, current_weather) # Add wind barbs stationplot.plot_barb(u, v) # Also plot the actual text of the station id. Instead of cardinal directions, # plot further out by specifying a location of 2 increments in x and 0 in y. stationplot.plot_text((2, 0), data['stid']) plt.show()
def plot_station_data(date, site, sitetitle, df): '''Given site station ID, the title of that site, and a dataframe of ASOS observation data from the last 3 days, returns a plot of the last 3-days of weather at that site. Parameters: site (str): string of ASOS station ID sitetitle (str): string of ASOS station full name df (dataframe): dataframe containing last 72 hours (3 days) of ASOS station data Returns: None *saves plots to plot_dir listed near top of script* ''' #if isinstance(df, int): #Returns if the station is not reporting # return if df.empty: print('dataframe is empty -- go to next plot') return lower_site = site.lower() timestamp_end = str(df.index[-1].strftime('%Y%m%d%H%M')) dt = df.index[:] dt_array = np.array(dt.values) graphtimestamp_start = dt[0].strftime("%m/%d/%y") graphtimestamp = dt[-1].strftime("%m/%d/%y") #now = datetime.datetime.utcnow() now = datetime.strptime(date, '%Y%m%d') today_date = dt[-1].strftime('%Y%m%d') markersize = 1.5 linewidth = 1.0 #make figure and axes fig = plt.figure() fig.set_size_inches(18, 10) if 'snow_depth_set_1' in df.keys(): #six axes if snow depth ax1 = fig.add_subplot(6, 1, 1) ax2 = fig.add_subplot(6, 1, 2, sharex=ax1) ax3 = fig.add_subplot(6, 1, 3, sharex=ax1) ax4 = fig.add_subplot(6, 1, 4, sharex=ax1) ax5 = fig.add_subplot(6, 1, 5, sharex=ax1) ax6 = fig.add_subplot(6, 1, 6, sharex=ax1) ax6.set_xlabel('Time (UTC)') else: ax1 = fig.add_subplot(5, 1, 1) #five axes if no snow depth ax2 = fig.add_subplot(5, 1, 2, sharex=ax1) ax3 = fig.add_subplot(5, 1, 3, sharex=ax1) ax4 = fig.add_subplot(5, 1, 4, sharex=ax1) ax5 = fig.add_subplot(5, 1, 5, sharex=ax1) ax5.set_xlabel('Time (UTC)') #ax1.set_title(site+' '+sitetitle+' '+graphtimestamp_start+' - '+graphtimestamp+' '+now.strftime("%H:%MZ")) ax1.set_title(site + ' ' + sitetitle + ' ' + graphtimestamp_start + ' - ' + graphtimestamp) #------------------ #plot airT and dewT #------------------ if 'tmpc' in df.keys(): airT = df['tmpc'] airT_new = airT.dropna() airT_list = list(airT_new.values) airT_dt_list = [] for i in range(0, len(airT)): if pd.isnull(airT[i]) == False: airT_dt_list.append(dt[i]) #ax1.plot_date(airT_dt_list,airT_list,'o-',label="Temp",color="blue",linewidth=linewidth,markersize=markersize) ax1.plot_date(airT_dt_list, airT_list, linestyle='solid', label="Temp", color="blue", linewidth=linewidth, marker='None') #ax1.plot_date(dt,airT,'-',label="Temp",color="blue",linewidth=linewidth) if 'dwpc' in df.keys(): dewT = df['dwpc'] dewT_new = dewT.dropna() dewT_list = list(dewT_new.values) dewT_dt_list = [] for i in range(0, len(dewT)): if pd.isnull(dewT[i]) == False: dewT_dt_list.append(dt[i]) #ax1.plot_date(dewT_dt_list,dewT_list,'o-',label="Dew Point",color="black",linewidth=linewidth,markersize=markersize) ax1.plot_date(dewT_dt_list, dewT_list, linestyle='solid', label="Dew Point", color="black", linewidth=linewidth, marker='None') if ax1.get_ylim()[0] < 0 < ax1.get_ylim()[1]: ax1.axhline(0, linestyle='-', linewidth=1.0, color='deepskyblue') trans = transforms.blended_transform_factory( ax1.get_yticklabels()[0].get_transform(), ax1.transData) ax1.text(0, 0, '0C', color="deepskyblue", transform=trans, ha="right", va="center") #light blue line at 0 degrees C ax1.set_ylabel('Temp ($^\circ$C)') ax1.legend(loc='best', ncol=2) axes = [ax1] #begin axes #---------------------------- #plotting wind speed and gust #---------------------------- if 'sknt' in df.keys(): wnd_spd = df['sknt'] #ax2.plot_date(dt,wnd_spd,'o-',label='Speed',color="forestgreen",linewidth=linewidth,markersize=markersize) ax2.plot_date(dt, wnd_spd, linestyle='solid', label='Speed', color="forestgreen", linewidth=linewidth, marker='None') if 'gust' in df.keys(): wnd_gst = df['gust'] max_wnd_gst = wnd_gst.max(skipna=True) ax2.plot_date(dt, wnd_gst, 'o-', label='Gust (Max ' + str(round(max_wnd_gst, 1)) + 'kt)', color="red", linewidth=0.0, markersize=markersize) ax2.set_ylabel('Wind (kt)') ax2.legend(loc='best', ncol=2) axes.append(ax2) #----------------------- #plotting wind direction #----------------------- if 'drct' in df.keys(): wnd_dir = df['drct'] wnd_dir_new = wnd_dir.dropna() wnd_dir_list = list(wnd_dir_new.values) wnd_dir_dt_list = [] for i in range(0, len(wnd_dir)): if pd.isnull(wnd_dir[i]) == False: wnd_dir_dt_list.append(dt[i]) #ax3.plot_date(dt,wnd_dir,'o-',label='Direction',color="purple",linewidth=0.2, markersize=markersize) #ax3.plot_date(wnd_dir_dt_list,wnd_dir_list,'o-',label='Direction',color="purple",linewidth=0.2, markersize=markersize) ax3.plot_date(wnd_dir_dt_list, wnd_dir_list, linestyle='solid', label='Direction', color="purple", linewidth=linewidth, marker='None') ax3.set_ylim(-10, 370) ax3.set_ylabel('Wind Direction') ax3.set_yticks([0, 90, 180, 270, 360]) axes.append(ax3) #------------- #plotting MSLP #------------- if 'mslp' in df.keys(): mslp = df['mslp'] mslp_new = mslp.dropna() mslp_list = list(mslp_new.values) mslp_dt_list = [] for i in range(0, len(mslp)): if pd.isnull(mslp[i]) == False: mslp_dt_list.append(dt[i]) max_mslp = mslp.max(skipna=True) min_mslp = mslp.min(skipna=True) labelname = 'Min ' + str(round(min_mslp, 1)) + 'hPa, Max ' + str( round(max_mslp, 2)) + 'hPa' #ax4.plot_date(mslp_dt_list,mslp_list,'o-',label=labelname,color='darkorange',linewidth=linewidth,markersize=markersize) ax4.plot_date(mslp_dt_list, mslp_list, linestyle='solid', label=labelname, color='darkorange', linewidth=linewidth, marker='None') ax4.legend(loc='best') ax4.set_ylabel('MSLP (hPa)') ax4.set_xlabel('Time (UTC)') axes.append(ax4) #------------------------------------------- #plotting precip accumulation & precip types #------------------------------------------- # Move date_time from index to column df = df.reset_index() if 'p01m' in df.keys(): df['p01m'] = df['p01m'].fillna(0) last_val = df.iloc[0]['p01m'] last_time = df.iloc[0]['time'] last_hour = last_time.strftime('%H') last_minute = last_time.strftime('%M') precip_inc = [last_val] precip_accum = 0.0 precip_accum_list = [last_val] num_ge_55_for_curr_hour = 0 for index in range(1, len(df)): #for index in range(1,12): val = df.iloc[index]['p01m'] time = df.iloc[index]['time'] hour = time.strftime('%H') minute = time.strftime('%M') #print('LAST: val=',last_val,' hour=',last_hour,' minute=',last_minute) #print('CURR: val=',val,' hour=',hour,' minute=',minute) if hour != last_hour: num_ge_55_for_curr_hour = 0 #if val == last_val: # increment = 0.0 #else: # #if last_minute == '53': # if last_minute > '50' and last_minute < '55': # increment = val # else: # increment = val-last_val if minute >= '55': if num_ge_55_for_curr_hour == 0: increment = val else: if val > last_val: increment = val - last_val else: increment = 0 num_ge_55_for_curr_hour = num_ge_55_for_curr_hour + 1 else: if val == last_val: increment = 0 else: if val > last_val: increment = val - last_val else: increment = 0 precip_accum = precip_accum + increment precip_accum_list.append(precip_accum) precip_inc.append(increment) last_val = val last_hour = hour last_minute = minute #df['p01m_mod'] = precip_inc max_precip = sum(precip_inc) # max_precip is also precip_accum_list[-1] #p01m_mod = list(df['p01m_mod'].values) p01m_mod_dt = list(df['time'].values) #max_precip = max(precip_accum_list) labelname = 'Precip (' + str(round(max_precip, 2)) + 'mm)' #ax5.plot_date(p01m_mod_dt,precip_accum_list,'o-',label=labelname,color='navy',linewidth=linewidth,markersize=markersize) ax5.plot_date(p01m_mod_dt, precip_accum_list, linestyle='solid', label=labelname, color='navy', linewidth=linewidth, marker='None') if max_precip > 0: ax5.set_ylim(-0.1 * max_precip, max_precip + max_precip * 0.2) else: ax5.set_ylim(-0.1, 0.5) # Add weather_code info to plot if 'wxcodes' in df.keys(): df['wxcodes'] = df['wxcodes'].fillna('') wxcodes_wto = [] for index in range(0, len(df)): time = df.iloc[index]['time'] minute = time.strftime('%M') #if minute == '53': if minute > '50' and minute < '55': # convert alphanumeric code to wto code number wxcodes = df.iloc[index]['wxcodes'] # Added in a check in case of unexpected weather codes # Used to resolve case when code set to -FZRAGR instead of -FZRA GR # Unidata says this will work as well: # wxcode_num = wx_code_map.get(wxcodes.split()[0], 0) # Here is reference: https://docs.python.org/3/library/stdtypes.html#dict.get if len(wxcodes) > 0 and wxcodes.split()[0] in wx_codes.keys(): wxcode_num = wx_code_map[wxcodes.split()[0]] else: wxcode_num = 0 else: wxcode_num = 0 wxcodes_wto.append(wxcode_num) #df['wxcodes_wto'] = wxcode_wto #wxcodes_wto = list(df['wxcodes_wto'].values) wxcodes_wto_dt = list(df['time'].values) if max_precip > 0: dummy_y_vals = np.ones(len(wxcodes_wto)) * (0.10 * max_precip) else: dummy_y_vals = np.ones(len(wxcodes_wto)) * (0.10 * 0.5) sp = StationPlot(ax5, wxcodes_wto_dt, dummy_y_vals) #ax.plot(dates, temps) sp.plot_symbol('C', wxcodes_wto, current_weather, fontsize=16, color='red') #sp.plot_symbol('C', wxcodes_wto, current_weather, fontsize=14, color='red') ax5.legend(loc='best') ax5.set_ylabel('Precip (mm)') axes.append(ax5) # Axes formatting for ax in axes: ax.spines["top"].set_visible( False) #darker borders on the grids of each subplot ax.spines["right"].set_visible(False) ax.spines["left"].set_visible(False) ax.spines["bottom"].set_visible(False) ax.tick_params(axis='x', which='both', bottom='on', top='off') #add ticks at labeled times ax.tick_params(axis='y', which='both', left='on', right='off') ax.xaxis.set_major_locator(DayLocator()) ax.xaxis.set_major_formatter(DateFormatter('%b-%d')) ax.xaxis.set_minor_locator(HourLocator(np.linspace(6, 18, 3))) ax.xaxis.set_minor_formatter(DateFormatter('%H')) ax.fmt_xdata = DateFormatter('Y%m%d%H%M%S') ax.yaxis.grid(linestyle='--') ax.get_yaxis().set_label_coords(-0.06, 0.5) # Write plot to file plot_path = plot_dir + '/' + today_date if not os.path.exists(plot_path): os.makedirs(plot_path) try: catalogName = 'surface.Meteogram.' + timestamp_end + '.ASOS_' + asos_sites[ lower_site] + '.png' #plt.savefig(plot_path+'/ops.asos.'+timestamp_end+'.'+lower_site+'.png',bbox_inches='tight') plt.savefig(plot_path + '/' + catalogName, bbox_inches='tight') except: print("Problem saving figure for %s. Usually a maxticks problem" % site) plt.close() # DON'T NEED THIS CHECK FOR RT PROCESSING # ftp plot if in asos_for_cat list #if lower_site in asos_for_cat: # Open ftp connection if test: catalogFTP = FTP(ftpCatalogServer, ftpCatalogUser, ftpCatalogPassword) catalogFTP.cwd(catalogDestDir) else: catalogFTP = FTP(ftpCatalogServer, ftpCatalogUser) catalogFTP.cwd(catalogDestDir) catalogFTP.set_pasv(False) # ftp image ftpFile = open(os.path.join(plot_path, catalogName), 'rb') catalogFTP.storbinary('STOR ' + catalogName, ftpFile) ftpFile.close() # Close ftp connection catalogFTP.quit()
def plot_station_data(date,site,sitetitle,df): '''Given site station ID, the title of that site, and a dataframe of ASOS observation data from the last 3 days, returns a plot of the last 3-days of weather at that site. Parameters: site (str): string of ASOS station ID sitetitle (str): string of ASOS station full name df (dataframe): dataframe containing last 72 hours (3 days) of ASOS station data Returns: None *saves plots to plot_dir listed near top of script* ''' if isinstance(df, int): #Returns if the station is not reporting return lower_site = site.lower() timestamp_end=str(df.index[-1].strftime('%Y%m%d%H%M')) dt = df.index[:] dt_array = np.array(dt.values) graphtimestamp_start=dt[0].strftime("%m/%d/%y") graphtimestamp=dt[-1].strftime("%m/%d/%y") #now = datetime.datetime.utcnow() now = datetime.strptime(date,'%Y%m%d') today_date = dt[-1].strftime('%Y%m%d') markersize = 1.5 linewidth = 1.0 #make figure and axes fig = plt.figure() fig.set_size_inches(18,10) if 'snow_depth_set_1' in df.keys(): #six axes if snow depth ax1 = fig.add_subplot(6,1,1) ax2 = fig.add_subplot(6,1,2,sharex=ax1) ax3 = fig.add_subplot(6,1,3,sharex=ax1) ax4 = fig.add_subplot(6,1,4,sharex=ax1) ax5 = fig.add_subplot(6,1,5,sharex=ax1) ax6 = fig.add_subplot(6,1,6,sharex=ax1) ax6.set_xlabel('Time (UTC)') else: ax1 = fig.add_subplot(5,1,1) #five axes if no snow depth ax2 = fig.add_subplot(5,1,2,sharex=ax1) ax3 = fig.add_subplot(5,1,3,sharex=ax1) ax4 = fig.add_subplot(5,1,4,sharex=ax1) ax5 = fig.add_subplot(5,1,5,sharex=ax1) ax5.set_xlabel('Time (UTC)') #ax1.set_title(site+' '+sitetitle+' '+graphtimestamp_start+' - '+graphtimestamp+' '+now.strftime("%H:%MZ")) ax1.set_title(site+' '+sitetitle+' '+graphtimestamp_start+' - '+graphtimestamp) #------------------ #plot airT and dewT #------------------ if 'tmpc' in df.keys(): airT = df['tmpc'] airT_new = airT.dropna() airT_list = list(airT_new.values) airT_dt_list = [] for i in range(0,len(airT)): if pd.isnull(airT[i]) == False: airT_dt_list.append(dt[i]) ax1.plot_date(airT_dt_list,airT_list,'o-',label="Temp",color="blue",linewidth=linewidth,markersize=markersize) #ax1.plot_date(dt,airT,'-',label="Temp",color="blue",linewidth=linewidth) if 'dwpc' in df.keys(): dewT = df['dwpc'] dewT_new = dewT.dropna() dewT_list = list(dewT_new.values) dewT_dt_list = [] for i in range(0,len(dewT)): if pd.isnull(dewT[i]) == False: dewT_dt_list.append(dt[i]) ax1.plot_date(dewT_dt_list,dewT_list,'o-',label="Dew Point",color="black",linewidth=linewidth,markersize=markersize) if ax1.get_ylim()[0] < 0 < ax1.get_ylim()[1]: ax1.axhline(0, linestyle='-', linewidth = 1.0, color='deepskyblue') trans = transforms.blended_transform_factory(ax1.get_yticklabels()[0].get_transform(), ax1.transData) ax1.text(0,0,'0C', color="deepskyblue", transform=trans, ha="right", va="center") #light blue line at 0 degrees C ax1.set_ylabel('Temp ($^\circ$C)') ax1.legend(loc='best',ncol=2) axes = [ax1] #begin axes #---------------------------- #plotting wind speed and gust #---------------------------- if 'sknt' in df.keys(): wnd_spd = df['sknt'] ax2.plot_date(dt,wnd_spd,'o-',label='Speed',color="forestgreen",linewidth=linewidth,markersize=markersize) if 'gust' in df.keys(): wnd_gst = df['gust'] max_wnd_gst = wnd_gst.max(skipna=True) ax2.plot_date(dt,wnd_gst,'o-',label='Gust (Max ' + str(round(max_wnd_gst,1)) + 'kt)',color="red",linewidth=0.0,markersize=markersize) ax2.set_ylabel('Wind (kt)') ax2.legend(loc='best',ncol=2) axes.append(ax2) #----------------------- #plotting wind direction #----------------------- if 'drct' in df.keys(): wnd_dir = df['drct'] ax3.plot_date(dt,wnd_dir,'o-',label='Direction',color="purple",linewidth=0.2, markersize=markersize) ax3.set_ylim(-10,370) ax3.set_ylabel('Wind Direction') ax3.set_yticks([0,90,180,270,360]) axes.append(ax3) #------------- #plotting MSLP #------------- if 'mslp' in df.keys(): mslp = df['mslp'] mslp_new = mslp.dropna() mslp_list = list(mslp_new.values) mslp_dt_list = [] for i in range(0,len(mslp)): if pd.isnull(mslp[i]) == False: mslp_dt_list.append(dt[i]) max_mslp = mslp.max(skipna=True) min_mslp = mslp.min(skipna=True) labelname = 'Min ' + str(round(min_mslp,1)) + 'hPa, Max ' + str(round(max_mslp,2)) + 'hPa' ax4.plot_date(mslp_dt_list,mslp_list,'o-',label=labelname,color='darkorange',linewidth=linewidth,markersize=markersize) ax4.legend(loc='best') ax4.set_ylabel('MSLP (hPa)') ax4.set_xlabel('Time (UTC)') axes.append(ax4) #------------------------------------------- #plotting precip accumulation & precip types #------------------------------------------- # Move date_time from index to column df = df.reset_index() # MODIFY THIS SO THAT WHEN READINGS COME IN WE KEEP THEM INSTEAD OF JUST ACCEPTING HOURLY ACCUMS # WILL MAKE PLOT LESS CHOPPY # Plot precip time series (use only values at minute 53) if 'p01m' in df.keys(): df['p01m'] = df['p01m'].fillna(0) precip_inc = list(df['p01m'].values) precip_inc_dt = list(df['date_time'].values) precip_accum = 0.0 precip_accum_list = [] precip_dt_list = [] precip_accum_indices = [] for i in range(0,len(precip_inc)): time_obj = pd.to_datetime(precip_inc_dt[i]) minutes = time_obj.strftime('%M') if minutes == '53': precip_accum = precip_accum + precip_inc[i] precip_accum_list.append(precip_accum) precip_dt_list.append(precip_inc_dt[i]) precip_accum_indices.append(i) times = precip_dt_list precip_accums = precip_accum_list """ precip_accum = 0.0 precip_accum_list = [] last_increment = df.loc[0].p01m precip_accum_indices = [] for index in range(1,len(df)): if df.loc[index].p01m < last_increment: precip_accum = precip_accum + last_increment precip_accum_list.append(precip_accum) precip_accum_indices.append(index) else: precip_accum_list.append(precip_accum) last_increment = df.loc[index].p01m # append last element in dataframe in case there's some precip there precip_accum = precip_accum + last_increment precip_accum_list.append(precip_accum) # values to use for the plot times = [precip_dt_list[i] for i in precip_accum_indices] precip_accums = [precip_accum_list[i] for i in precip_accum_indices] """ max_precip = max(precip_accum_list) labelname = 'Precip (' + str(round(max_precip,2)) + 'mm)' ax5.plot_date(precip_dt_list,precip_accum_list,'o-',label=labelname,color='navy',linewidth=linewidth,markersize=markersize) if max_precip > 0: ax5.set_ylim(-0.1*max_precip,max_precip+max_precip*0.2) else: ax5.set_ylim(-0.5,5) # Add weather_code info to plot if 'wxcodes' in df.keys(): df['wxcodes'] = df['wxcodes'].fillna('') wxcodes_list = list(df['wxcodes'].values) #wxcodes_dt_list = list(df['date_time'].values) wxcodes_num_list = [] for i in range(0,len(wxcodes_list)): wxcodes = wxcodes_list[i] if len(wxcodes) > 0: wxcode_wto = wx_code_map[wxcodes.split()[0]] else: wxcode_wto = 0 wxcodes_num_list.append(wxcode_wto) wxcodes = [wxcodes_num_list[i] for i in precip_accum_indices] # Set y values for weather symbols on plot dummy_y_vals = np.ones(len(wxcodes)) * (0.10*max_precip) sp = StationPlot(ax5, times, dummy_y_vals) #ax.plot(dates, temps) #sp.plot_symbol('C', wxcodes, current_weather, fontsize=16, color='red') sp.plot_symbol('C', wxcodes, current_weather, fontsize=14, color='red') """ wxcodes_num_list_hrly = [] wxcodes_dt_list_hrly = [] for i in range(0,len(wxcodes_num_list)): time_obj = pd.to_datetime(wxcodes_dt_list[i]) minutes = time_obj.strftime('%M') if minutes == '00': wxcodes_num_list_hrly.append(wxcodes_num_list[i]) wxcodes_dt_list_hrly.append(wxcodes_dt_list[i]) dummy_y_vals = np.zeros(len(wxcodes_num_list_hrly)) # only to place on plot sp = StationPlot(ax5, wxcodes_dt_list_hrly, dummy_y_vals) #ax5.plot(precip_dt_list,precip_accum_list) sp.plot_symbol('C', wxcodes_num_list_hrly, current_weather, fontsize=16, color='red') """ ax5.legend(loc='best') ax5.set_ylabel('Precip (mm)') axes.append(ax5) """ #------------------- #plotting snow depth #------------------- if 'snow_depth_set_1' in df.keys(): snow_depth = df['snow_depth_set_1'] snow_depth_new = snow_depth.dropna() snow_depth_dt_list = [] for i in range(0,len(snow_depth)): if pd.isnull(snow_depth[i]) == False: snow_depth_dt_list.append(dt[i]) max_snow_depth = snow_depth.max(skipna=True) min_snow_depth = snow_depth.min(skipna=True) labelname = 'Min Depth ' + str(round(min_snow_depth,2)) + 'mm, Max Depth ' + str(round(max_snow_depth,2)) + 'mm' ax6.plot_date(snow_depth_dt_list,snow_depth_new,'o-',label=labelname,color='deepskyblue',linewidth=linewidth,markersize=markersize) if max_snow_depth > 0: ax6.set_ylim(-0.1*max_snow_depth,max_snow_depth+max_snow_depth*0.2) else: ax6.set_ylim(-0.5,5) ax6.legend(loc='best') ax6.set_ylabel('Snow Depth (mm)') axes.append(ax6) """ # Axes formatting for ax in axes: ax.spines["top"].set_visible(False) #darker borders on the grids of each subplot ax.spines["right"].set_visible(False) ax.spines["left"].set_visible(False) ax.spines["bottom"].set_visible(False) ax.tick_params(axis='x',which='both',bottom='on',top='off') #add ticks at labeled times ax.tick_params(axis='y',which='both',left='on',right='off') ax.xaxis.set_major_locator( DayLocator() ) ax.xaxis.set_major_formatter( DateFormatter('%b-%d') ) ax.xaxis.set_minor_locator( HourLocator(np.linspace(6,18,3)) ) ax.xaxis.set_minor_formatter( DateFormatter('%H') ) ax.fmt_xdata = DateFormatter('Y%m%d%H%M%S') ax.yaxis.grid(linestyle = '--') ax.get_yaxis().set_label_coords(-0.06,0.5) # Write plot to file plot_path = plot_dir+'/'+today_date if not os.path.exists(plot_path): os.makedirs(plot_path) try: plt.savefig(plot_path+'/ops.asos.'+timestamp_end+'.'+lower_site+'.png',bbox_inches='tight') except: print("Problem saving figure for %s. Usually a maxticks problem" %site) plt.close()
# Plot the temperature and dew point to the upper and lower left, respectively, of # the center point. Each one uses a different color. stationplot.plot_parameter('NW', data['air_temperature'].values, color='red') stationplot.plot_parameter('SW', data['dew_point_temperature'].values, color='darkgreen') # A more complex example uses a custom formatter to control how the sea-level pressure # values are plotted. This uses the standard trailing 3-digits of the pressure value # in tenths of millibars. stationplot.plot_parameter('NE', data['air_pressure_at_sea_level'].values, formatter=lambda v: format(10 * v, '.0f')[-3:]) # Plot the cloud cover symbols in the center location. This uses the codes made above and # uses the `sky_cover` mapper to convert these values to font codes for the # weather symbol font. stationplot.plot_symbol('C', data['cloud_coverage'].values, sky_cover) # Same this time, but plot current weather to the left of center, using the # `current_weather` mapper to convert symbols to the right glyphs. stationplot.plot_symbol('W', data['present_weather'].values, current_weather) # Add wind barbs stationplot.plot_barb(data['eastward_wind'].values, data['northward_wind'].values) # Also plot the actual text of the station id. Instead of cardinal directions, # plot further out by specifying a location of 2 increments in x and 0 in y. stationplot.plot_text((2, 0), data['station_id'].values) plt.show()
def makeStationPlot(plotTitle, plotFileName, maxDataAge, maxLat, minLat, maxLon, minLon, stationDensity, textSize, figX, figY, dpi, showCountryBorders, showStateBorders, showCountyBorders): # # Data Polling # Get data from AWC TDS dataURL = "https://www.aviationweather.gov/adds/dataserver_current/httpparam?dataSource=metars&requestType=retrieve&format=csv&minLat=" + str( minLat) + "&minLon=" + str(minLon) + "&maxLat=" + str( maxLat) + "&maxLon=" + str(maxLon) + "&hoursBeforeNow=" + str( maxDataAge) # First read in the data. We use pandas because it simplifies a lot of tasks, like dealing # with strings data = pd.read_csv(dataURL, header=5, usecols=(1, 3, 4, 5, 6, 7, 8, 12, 21, 22), names=[ 'stid', 'lat', 'lon', 'air_temperature', 'dew_point_temperature', 'wind_dir', 'wind_speed', 'slp', 'weather', 'cloud_fraction' ], na_values=-99999) # # Data Handling # convert T and Td from °C to °F data['air_temperature'] = (data['air_temperature'] * (9 / 5.0)) + 32 data['dew_point_temperature'] = (data['dew_point_temperature'] * (9 / 5.0)) + 32 # change sky category to % data['cloud_fraction'] = data['cloud_fraction'].replace( 'SKC', 0.0).replace('CLR', 0.0).replace('CAVOK', 0.0).replace( 'FEW', 0.1875).replace('SCT', 0.4375).replace('BKN', 0.750).replace( 'OVC', 1.000).replace('OVX', 1.000) # Drop rows with missing winds data = data.dropna(how='any', subset=['wind_dir', 'wind_speed']) # Set up the map projection proj = ccrs.LambertConformal( central_longitude=(minLon + (maxLon - minLon) / 2), central_latitude=(minLat + (maxLat - minLat) / 2)) # Set station density, in x meter radius point_locs = proj.transform_points(ccrs.PlateCarree(), data['lon'].values, data['lat'].values) data = data[reduce_point_density(point_locs, stationDensity * 1000)] # Get the wind components, converting from m/s to knots as will be appropriate u, v = wind_components( (data['wind_speed'].values * units('m/s')).to('knots'), data['wind_dir'].values * units.degree) # Convert the fraction value into a code of 0-8 and compensate for NaN values cloud_frac = (8 * data['cloud_fraction']) cloud_frac[np.isnan(cloud_frac)] = 10 cloud_frac = cloud_frac.astype(int) # Map weather strings to WMO codes. Only use the first symbol if there are multiple wx = [ wx_code_map[s.split()[0] if ' ' in s else s] for s in data['weather'].fillna('') ] # # Plot Setup # Set DPI of the resulting figure plt.rcParams['savefig.dpi'] = dpi # Create the figure and an axes set to the projection. fig = plt.figure(figsize=(figX, figY)) ax = fig.add_subplot(1, 1, 1, projection=proj) # Set plot bounds ax.set_extent((minLon, maxLon, minLat, maxLat)) # Add geographic features if showCountyBorders: ax.add_feature(USCOUNTIES.with_scale('500k'), edgecolor='gray', linewidth=0.25) if showStateBorders: state_borders = cfeature.NaturalEarthFeature( category='cultural', name='admin_1_states_provinces_lakes', scale='50m', facecolor='none') ax.add_feature(state_borders, edgecolor='gray', linewidth=0.5) if showCountryBorders: country_borders = cfeature.NaturalEarthFeature( category='cultural', name='admin_0_countries', scale='50m', facecolor='none') ax.add_feature(country_borders, edgecolor='black', linewidth=0.7) # # Create Station Plots # Set station location, setup plot stationplot = StationPlot(ax, data['lon'].values, data['lat'].values, clip_on=True, transform=ccrs.PlateCarree(), fontsize=textSize) # Plot the temperature and dew point stationplot.plot_parameter('NW', data['air_temperature'], color='red') stationplot.plot_parameter('SW', data['dew_point_temperature'], color='darkgreen') # Plot pressure data stationplot.plot_parameter('NE', data['slp'], formatter=lambda v: format(10 * v, '.0f')[-3:]) # Plot cloud cover stationplot.plot_symbol('C', cloud_frac, sky_cover) # Plot current weather stationplot.plot_symbol('W', wx, current_weather) # Add wind barbs stationplot.plot_barb(u, v) # Plot station id stationplot.plot_text((2, 0), data['stid']) # Set a title and show the plot ax.set_title(plotTitle) # Export fig fig.savefig('/home/CarterHumphreys/bin/send2web/' + datetime.utcnow().strftime("%Y%m%d-%H00") + '_' + plotFileName + '.png', bbox_inches='tight') fig.savefig('/home/CarterHumphreys/bin/send2web/' + plotFileName + '.png', bbox_inches='tight')
# the center point. Each one uses a different color. stationplot.plot_parameter('NW', data['air_temperature'], color='red') stationplot.plot_parameter('SW', data['dew_point_temperature'], color='darkgreen') # A more complex example uses a custom formatter to control how the sea-level pressure # values are plotted. This uses the standard trailing 3-digits of the pressure value # in tenths of millibars. stationplot.plot_parameter('NE', data['slp'], formatter=lambda v: format(10 * v, '.0f')[-3:]) # Plot the cloud cover symbols in the center location. This uses the codes made above and # uses the `sky_cover` mapper to convert these values to font codes for the # weather symbol font. stationplot.plot_symbol('C', cloud_frac, sky_cover) # Same this time, but plot current weather to the left of center, using the # `current_weather` mapper to convert symbols to the right glyphs. stationplot.plot_symbol('W', wx, current_weather) # Add wind barbs stationplot.plot_barb(u, v) # Also plot the actual text of the station id. Instead of cardinal directions, # plot further out by specifying a location of 2 increments in x and 0 in y. stationplot.plot_text((2, 0), data['stid']) plt.show()
def main(): ### START OF USER SETTINGS BLOCK ### # FILE/DATA SETTINGS # file path to input file datafile = '/home/jgodwin/python/sfc_observations/surface_observations.txt' timefile = '/home/jgodwin/python/sfc_observations/validtime.txt' # file path to county shapefile ctyshppath = '/home/jgodwin/python/sfc_observations/shapefiles/counties/countyl010g.shp' # file path to ICAO list icaopath = '/home/jgodwin/python/sfc_observations/icao_list.csv' icaodf = pd.read_csv(icaopath, index_col='STATION') # MAP SETTINGS # map names (doesn't go anywhere (yet), just for tracking purposes) maps = ['CONUS', 'Texas', 'Tropical Atlantic'] # minimum radius allowed between points (in km) radius = [100.0, 50.0, 75.0] # map boundaries (longitude/latitude degrees) west = [-122, -108, -100] east = [-73, -93, -60] south = [23, 25, 10] north = [50, 38, 35] restart_projection = [True, False, True] # use county map? (True/False): warning, counties load slow! usecounties = [False, False, False] # OUTPUT SETTINGS # save directory for output savedir = '/var/www/html/images/' # filenames for output savenames = ['conus.png', 'texas.png', 'atlantic.png'] # TEST MODE SETTINGS test = False # True/False testnum = 5 # which map are you testing? corresponds to index in "maps" above ### END OF USER SETTING SECTION ### # if there are any missing weather codes, add them here wx_code_map.update({ '-RADZ': 59, '-TS': 17, 'VCTSSH': 80, '-SGSN': 77, 'SHUP': 76, 'FZBR': 48, 'FZUP': 76 }) ### READ IN DATA / SETUP MAP ### # read in the valid time file vt = open(timefile).read() # read in the data for i in range(len(maps)): if test and i != testnum: continue with open(datafile) as f: data = pd.read_csv(f,header=0,names=['siteID','lat','lon','elev','slp','temp','sky','dpt','wx','wdr',\ 'wsp'],na_values=-99999) # drop rows with missing winds data = data.dropna(how='any', subset=['wdr', 'wsp']) # remove data not within our domain data = data[(data['lat'] >= south[i]-2.0) & (data['lat'] <= north[i]+2.0) \ & (data['lon'] >= west[i]-2.0) & (data['lon'] <= east[i]+2.0)] # filter data (there seems to be one site always reporting a really anomalous temperature data = data[data['temp'] <= 50] print("Working on %s" % maps[i]) # set up the map projection central longitude/latitude and the standard parallels cenlon = (west[i] + east[i]) / 2.0 cenlat = (south[i] + north[i]) / 2.0 sparallel = cenlat if cenlat > 0: cutoff = -30 flip = False elif cenlat < 0: cutoff = 30 flip = True # create the projection if restart_projection: proj = ccrs.LambertConformal(central_longitude=cenlon, central_latitude=cenlat, standard_parallels=[sparallel], cutoff=cutoff) point_locs = proj.transform_points(ccrs.PlateCarree(), data['lon'].values, data['lat'].values) data = data[reduce_point_density(point_locs, radius[i] * 1000)] # state borders state_boundaries = cfeature.NaturalEarthFeature(category='cultural',\ name='admin_1_states_provinces_lines',scale='50m',facecolor='none') # county boundaries if usecounties[i]: county_reader = shpreader.Reader(ctyshppath) counties = list(county_reader.geometries()) COUNTIES = cfeature.ShapelyFeature(counties, ccrs.PlateCarree()) ### DO SOME CONVERSIONS ### # get the wind components u, v = wind_components(data['wsp'].values * units('knots'), data['wdr'].values * units.degree) # convert temperature from Celsius to Fahrenheit data['temp'] = cToF(data['temp']) data['dpt'] = cToF(data['dpt']) # convert the cloud fraction value into a code of 0-8 (oktas) and compenate for NaN values cloud_frac = (8 * data['sky']) cloud_frac[np.isnan(cloud_frac)] = 10 cloud_frac = cloud_frac.astype(int) # map weather strings to WMO codes (only use first symbol if multiple are present data['wx'] = data.wx.str.split('/').str[0] + '' wx = [ wx_code_map[s.split()[0] if ' ' in s else s] for s in data['wx'].fillna('') ] # get the minimum and maximum temperatures in domain searchdata = data[(data['lat'] >= south[i]) & (data['lat'] <= north[i]) \ & (data['lon'] >= west[i]) & (data['lon'] <= east[i])] min_temp = searchdata.loc[searchdata['temp'].idxmin()] max_temp = searchdata.loc[searchdata['temp'].idxmax()] max_dewp = searchdata.loc[searchdata['dpt'].idxmax()] # look up the site names for the min/max temp locations min_temp_loc = icaoLookup(min_temp['siteID'], icaodf) max_temp_loc = icaoLookup(max_temp['siteID'], icaodf) max_dewp_loc = icaoLookup(max_dewp['siteID'], icaodf) text_str = "Min temp: %.0f F at %s (%s)\nMax temp: %.0f F at %s (%s)\nMax dewpoint: %.0f F at %s (%s)"\ % (min_temp['temp'],min_temp['siteID'],min_temp_loc,\ max_temp['temp'],max_temp['siteID'],max_temp_loc,\ max_dewp['dpt'],max_dewp['siteID'],max_dewp_loc) ### PLOTTING SECTION ### # change the DPI to increase the resolution plt.rcParams['savefig.dpi'] = 255 # create the figure and an axes set to the projection fig = plt.figure(figsize=(20, 10)) ax = fig.add_subplot(1, 1, 1, projection=proj) # add various map elements ax.add_feature(cfeature.LAND, zorder=-1) ax.add_feature(cfeature.OCEAN, zorder=-1) ax.add_feature(cfeature.LAKES, zorder=-1) ax.add_feature(cfeature.COASTLINE, zorder=2, edgecolor='black') ax.add_feature(state_boundaries, edgecolor='black') if usecounties[i]: ax.add_feature(COUNTIES, facecolor='none', edgecolor='gray', zorder=-1) ax.add_feature(cfeature.BORDERS, linewidth=2, edgecolor='black') # set plot bounds ax.set_extent((west[i], east[i], south[i], north[i])) ### CREATE STATION PLOTS ### # lat/lon of the station plots stationplot = StationPlot(ax,data['lon'].values,data['lat'].values,clip_on=True,\ transform=ccrs.PlateCarree(),fontsize=6) # plot the temperature and dewpoint stationplot.plot_parameter('NW', data['temp'], color='red') stationplot.plot_parameter('SW', data['dpt'], color='darkgreen') # plot the SLP using the standard trailing three digits stationplot.plot_parameter( 'NE', data['slp'], formatter=lambda v: format(10 * v, '.0f')[-3:]) # plot the sky condition stationplot.plot_symbol('C', cloud_frac, sky_cover) # plot the present weather stationplot.plot_symbol('W', wx, current_weather) # plot the wind barbs stationplot.plot_barb(u, v, flip_barb=flip) # plot the text of the station ID stationplot.plot_text((2, 0), data['siteID']) # plot the valid time plt.title('Surface Observations valid %s' % vt) # plot the min/max temperature info and draw circle around warmest and coldest obs props = dict(boxstyle='round', facecolor='wheat', alpha=0.5) plt.text(west[i], south[i], text_str, fontsize=12, verticalalignment='top', bbox=props, transform=ccrs.Geodetic()) projx1, projy1 = proj.transform_point(min_temp['lon'], min_temp['lat'], ccrs.Geodetic()) ax.add_patch( matplotlib.patches.Circle(xy=[projx1, projy1], radius=50000, facecolor="None", edgecolor='blue', linewidth=3, transform=proj)) projx2, projy2 = proj.transform_point(max_temp['lon'], max_temp['lat'], ccrs.Geodetic()) ax.add_patch( matplotlib.patches.Circle(xy=[projx2, projy2], radius=50000, facecolor="None", edgecolor='red', linewidth=3, transform=proj)) projx3, projy3 = proj.transform_point(max_dewp['lon'], max_dewp['lat'], ccrs.Geodetic()) ax.add_patch( matplotlib.patches.Circle(xy=[projx3, projy3], radius=30000, facecolor="None", edgecolor='green', linewidth=3, transform=proj)) # save the figure outfile_name = savedir + savenames[i] plt.savefig(outfile_name, bbox_inches='tight') # clear and close everything fig.clear() ax.clear() plt.close(fig) f.close() print("Script finished.")
def plot_map_standard(proj, point_locs, df_t, area='EU', west=-9.5, east=28, south=35, north=62, fonts=14, path=None, SLP=False, gust=False): if path == None: # set up the paths and test for existence path = expanduser('~') + '/Documents/Metar_plots' try: os.listdir(path) except FileNotFoundError: os.mkdir(path) else: path = path df = df_t.loc[(df_t['longitude'] >= west - 4) & (df_t['longitude'] <= east + 4) & (df_t['latitude'] <= north + 4) & (df_t['latitude'] >= south - 4)] plt.rcParams['savefig.dpi'] = 300 # ========================================================================= # Create the figure and an axes set to the projection. fig = plt.figure(figsize=(20, 16)) ax = fig.add_subplot(1, 1, 1, projection=proj) if area == 'Antarctica': df = df.loc[df['latitude'] < north] ax.set_extent([-180, 180, -90, -60], ccrs.PlateCarree()) theta = np.linspace(0, 2 * np.pi, 100) center, radius = [0.5, 0.5], 0.5 verts = np.vstack([np.sin(theta), np.cos(theta)]).T circle = mpath.Path(verts * radius + center) ax.set_boundary(circle, transform=ax.transAxes) elif area == 'Arctic': df = df.loc[df['latitude'] > south] ax.set_extent([-180, 180, 60, 90], ccrs.PlateCarree()) theta = np.linspace(0, 2 * np.pi, 100) center, radius = [0.5, 0.5], 0.5 verts = np.vstack([np.sin(theta), np.cos(theta)]).T circle = mpath.Path(verts * radius + center) ax.set_boundary(circle, transform=ax.transAxes) else: ax.set_extent((west, east, south, north)) # Get the wind components, converting from m/s to knots as will # be appropriate for the station plot. df['dd'][df['dd'] > 360] = np.nan u, v = wind_components(df['ff'].values * units('knots'), df['dd'].values * units('deg')) cloud_frac = df['cloud_cover'] # Change the DPI of the resulting figure. Higher DPI drastically improves # look of the text rendering. # Set up a cartopy feature for state borders. # state_boundaries = feat.NaturalEarthFeature(category='cultural', # name='admin_0_countries', # scale='10m', # facecolor='#d8dcd6', # alpha=0.5) # ax.coastlines(resolution='10m', zorder=0, color='black') # ax.add_feature(feat.LAND) ax.add_feature(feat.COASTLINE.with_scale('10m'), zorder=2, edgecolor='black') ax.add_feature(feat.OCEAN.with_scale('50m'), zorder=0) ax.add_feature(feat.STATES.with_scale('10m'), zorder=1, facecolor='white', edgecolor='#5e819d') # ax.add_feature(cartopy.feature.OCEAN, zorder=0) # Set plot bounds # Start the station plot by specifying the axes to draw on, as well as the # lon/lat of the stations (with transform). We also the fontsize to 12 pt. stationplot = StationPlot(ax, df['longitude'].values, df['latitude'].values, clip_on=True, transform=ccrs.PlateCarree(), fontsize=fonts) # Plot the temperature and dew point to the upper and lower left, # respectively, of the center point. Each one uses a different color. Temp = stationplot.plot_parameter('NW', df['TT'], color='#fd3c06', fontweight='bold', zorder=3) Td = stationplot.plot_parameter('SW', df['TD'], color='#01ff07') if gust == True: maxff = stationplot.plot_parameter('SE', df['max_gust'], color='#cb416b', fontweight='bold', zorder=3) maxff.set_path_effects([ path_effects.Stroke(linewidth=1.5, foreground='black'), path_effects.Normal() ]) # fontweight = 'bold' # More complex ex. uses custom formatter to control how sea-level pressure # values are plotted. This uses the standard trailing 3-digits of # the pressure value in tenths of millibars. if (area != 'Antarctica' and area != 'Arctic'): p = stationplot.plot_parameter( 'NE', df['SLP'], formatter=lambda v: format(10 * v, '.0f')[-3:], color="#a2cffe") for x in [Temp, Td, p]: x.set_path_effects([ path_effects.Stroke(linewidth=1.5, foreground='black'), path_effects.Normal() ]) else: for x in [Temp, Td]: x.set_path_effects([ path_effects.Stroke(linewidth=1.5, foreground='black'), path_effects.Normal() ]) # Add wind barbs stationplot.plot_barb(u, v, zorder=3, linewidth=2) # Plot the cloud cover symbols in the center location. This uses the codes # made above and uses the `sky_cover` mapper to convert these values to # font codes for the weather symbol font. stationplot.plot_symbol('C', cloud_frac, sky_cover) # Same this time, but plot current weather to the left of center, using the # `current_weather` mapper to convert symbols to the right glyphs. for val in range(0, 2): wx = df[['ww', 'StationType']] if val == 0: # mask all the unmanned stations wx['ww'].loc[wx['StationType'] > 3] = np.nan wx2 = wx['ww'].fillna(00).astype(int).values.tolist() stationplot.plot_symbol('W', wx2, current_weather, zorder=4) else: # mask all the manned stations wx['ww'].loc[(wx['StationType'] <= 3)] = np.nan # mask all reports smaller than 9 # =7 is an empty symbol! wx['ww'].loc[wx['ww'] <= 9] = 7 wx2 = wx['ww'].fillna(7).astype(int).values.tolist() stationplot.plot_symbol('W', wx2, current_weather_auto, zorder=4) if SLP == True: lon = df['longitude'].loc[(df.PressureDefId == 'mean sea level') & (df.Hp <= 750)].values lat = df['latitude'].loc[(df.PressureDefId == 'mean sea level') & (df.Hp <= 750)].values xp, yp, _ = proj.transform_points(ccrs.PlateCarree(), lon, lat).T sea_levelp = df['SLP'].loc[(df.PressureDefId == 'mean sea level') & (df.Hp <= 750)] x_masked, y_masked, pres = remove_nan_observations( xp, yp, sea_levelp.values) slpgridx, slpgridy, slp = interpolate_to_grid(x_masked, y_masked, pres, interp_type='cressman', search_radius=400000, rbf_func='quintic', minimum_neighbors=1, hres=100000, rbf_smooth=100000) Splot_main = ax.contour(slpgridx, slpgridy, slp, colors='k', linewidths=2, extent=(west, east, south, north), levels=list(range(950, 1050, 10))) plt.clabel(Splot_main, inline=1, fontsize=12, fmt='%i') Splot = ax.contour(slpgridx, slpgridy, slp, colors='k', linewidths=1, linestyles='--', extent=(west, east, south, north), levels=[ x for x in range(950, 1050, 1) if x not in list(range(950, 1050, 10)) ]) plt.clabel(Splot, inline=1, fontsize=10, fmt='%i') # stationplot.plot_text((2, 0), df['Station']) # Also plot the actual text of the station id. Instead of cardinal # directions, plot further out by specifying a location of 2 increments # in x and 0 in y.stationplot.plot_text((2, 0), df['station']) if (area == 'Antarctica' or area == 'Arctic'): plt.savefig(path + '/CURR_SYNOP_' + area + '.png', bbox_inches='tight', pad_inches=0) else: plt.savefig(path + '/CURR_SYNOP_' + area + '.png', bbox_inches='tight', transparent="True", pad_inches=0)