def plot_sounding(date, station): p, T, Td, u, v, windspeed = get_sounding_data(date, station) lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) lfc_pressure, lfc_temperature = mpcalc.lfc(p, T, Td) parcel_path = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') # Create a new figure. The dimensions here give a good aspect ratio fig = plt.figure(figsize=(8, 8)) skew = SkewT(fig) # Plot the data temperature_line, = skew.plot(p, T, color='tab:red') dewpoint_line, = skew.plot(p, Td, color='blue') cursor = mplcursors.cursor([temperature_line, dewpoint_line]) # Plot thermodynamic parameters and parcel path skew.plot(p, parcel_path, color='black') if lcl_pressure: skew.ax.axhline(lcl_pressure, color='black') if lfc_pressure: skew.ax.axhline(lfc_pressure, color='0.7') # Add the relevant special lines skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() # Shade areas representing CAPE and CIN skew.shade_cin(p, T, parcel_path) skew.shade_cape(p, T, parcel_path) # Add wind barbs skew.plot_barbs(p, u, v) # Add an axes to the plot ax_hod = inset_axes(skew.ax, '30%', '30%', loc=1, borderpad=3) # Plot the hodograph h = Hodograph(ax_hod, component_range=100.) # Grid the hodograph h.add_grid(increment=20) # Plot the data on the hodograph mask = (p >= 100 * units.mbar) h.plot_colormapped(u[mask], v[mask], windspeed[mask]) # Plot a line colored by wind speed # Set some sensible axis limits skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) return fig, skew
def test_skewt_shade_cape_cin(test_profile): """Test shading CAPE and CIN on a SkewT plot.""" p, t, tp = test_profile fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig) skew.plot(p, t, 'r') skew.plot(p, tp, 'k') skew.shade_cape(p, t, tp) skew.shade_cin(p, t, tp) return fig
def make_skewt(): # Get the data date = request.args.get('date') time = request.args.get('time') region = request.args.get('region') station = request.args.get('station') date = datetime.strptime(date, '%Y%m%d') date = datetime(date.year, date.month, date.day, int(time)) df = get_sounding_data(date, region, station) p = df['pressure'].values * units(df.units['pressure']) T = df['temperature'].values * units(df.units['temperature']) Td = df['dewpoint'].values * units(df.units['dewpoint']) u = df['u_wind'].values * units(df.units['u_wind']) v = df['v_wind'].values * units(df.units['v_wind']) # Make the Skew-T fig = plt.figure(figsize=(9, 9)) add_metpy_logo(fig, 115, 100) skew = SkewT(fig, rotation=45) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.plot(p, T, 'tab:red') skew.plot(p, Td, 'tab:green') skew.plot_barbs(p, u, v) skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) # Calculate LCL height and plot as black dot lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Calculate full parcel profile and add to plot as black line prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') skew.plot(p, prof, 'k', linewidth=2) # Shade areas of CAPE and CIN skew.shade_cin(p, T, prof) skew.shade_cape(p, T, prof) # An example of a slanted line at constant T -- in this case the 0 # isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() canvas = FigureCanvas(fig) img = BytesIO() fig.savefig(img) img.seek(0) return send_file(img, mimetype='image/png')
def test_skewt_shade_cape_cin(test_profile): """Test shading CAPE and CIN on a SkewT plot.""" p, t, tp = test_profile fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig) skew.plot(p, t, 'r') skew.plot(p, tp, 'k') skew.shade_cape(p, t, tp) skew.shade_cin(p, t, tp) skew.ax.set_xlim(-50, 50) return fig
def plot_skewt(self, station_data): """ :param adjusted_data: receives the post processed dataframe :param valid: :return: """ # We will pull the data out of the example dataset into individual variables # and assign units. p = station_data['pressure'].values * units.hPa T = station_data['Temperature_isobaric'].values * units.degC Td = station_data['Dewpoint'].replace(np.nan, 0.0000001).values * units.degC u = station_data['u-component_of_wind_isobaric'].values * \ units('meters / second').to('knots') v = station_data['v-component_of_wind_isobaric'].values * \ units('meters / second').to('knots') # Create a new figure. The dimensions here give a good aspect ratio. fig = plt.figure(figsize=(12, 9)) skew = SkewT(fig, rotation=45) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.plot(p, T, 'r') skew.plot(p, Td, 'g') skew.plot_barbs(p, u, v) skew.ax.set_ylim(1020, 100) skew.ax.set_xlim(-40, 60) # Calculate LCL height and plot as black dot lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Calculate full parcel profile and add to plot as black line prof = mpcalc.parcel_profile(p, T[0], Td[0]) skew.plot(p, prof, 'k', linewidth=2) # An example of a slanted line at constant T -- in this case the 0 # isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() skew.shade_cape(p, T, prof) skew.shade_cin(p, T, prof) return skew
def test_skewt_shade_cape_cin(test_profile): """Test shading CAPE and CIN on a SkewT plot.""" p, t, tp = test_profile with matplotlib.rc_context({'axes.autolimit_mode': 'data'}): fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig) skew.plot(p, t, 'r') skew.plot(p, tp, 'k') skew.shade_cape(p, t, tp) skew.shade_cin(p, t, tp) skew.ax.set_xlim(-50, 50) return fig
def core(p, T, Td, u, v, **kwargs): # Calculate the LCL lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) #print('LCL p, t:', int(lcl_pressure), int(lcl_temperature)) # Calculate the parcel profile. parcel_prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') # Create a new figure. The dimensions here give a good aspect ratio fig = plt.figure(figsize=(8, 8)) skew = SkewT(fig, rotation=45) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot #skew.plot(p, T, 'k-') skew.plot(p, T, 'r.-', ms=5, lw=2, label='mean T') skew.plot(p, Td, 'g.-', ms=5, lw=2, label='mean Td') skew.plot_barbs(p, u, v) skew.ax.set_ylim(1000, 180) skew.ax.set_xlim(-20, 40) # Plot LCL temperature as black dot skew.plot(lcl_pressure, lcl_temperature, 'k.', markerfacecolor='black') # Plot the parcel profile as a black line skew.plot(p, parcel_prof, 'k', linewidth=2) # Shade areas of CAPE and CIN skew.shade_cin(p, T, parcel_prof) skew.shade_cape(p, T, parcel_prof) # Plot a zero degree isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats(lw=.5) skew.plot_moist_adiabats(lw=.5) skew.plot_mixing_lines(lw=.5) # Show the plot #plt.show() #skew.ax.set_title(time_str) plt.legend(loc='lower left') plt.title(kwargs.get('title')) fname = kwargs.get('saveto', 'profile.png') fig.savefig(fname) print(fname, 'saved.') plt.close()
def plot_skewt(p, t, td, puv=None, u=None, v=None, title=None, outfile=None): # Create a new figure. The dimensions here give a good aspect ratio fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig, rotation=30) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.plot(p, t, 'r', linewidth=2) skew.plot(p, td, 'g', linewidth=2) if u is not None and v is not None: skew.plot_barbs(puv, u, v) skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) # Calculate the LCL lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], t[0], td[0]) # Calculate the parcel profile. parcel_prof = mpcalc.parcel_profile(p, t[0], td[0]).to('degC') # Plot LCL temperature as black dot skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Plot the parcel profile as a black line skew.plot(p, parcel_prof, 'k--', linewidth=1) # Shade areas of CAPE and CIN skew.shade_cin(p, t, parcel_prof) skew.shade_cape(p, t, parcel_prof) # Plot a zero degree isotherm #skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() if title is not None: plt.title(title) # Show the plot #plt.show() if outfile is None: outfile = 'skewt.png' fig.savefig(outfile, format='png')
def test_skewt_shade_cape_cin_no_limit(test_profile): """Test shading CIN without limits.""" p, t, _, tp = test_profile with matplotlib.rc_context({'axes.autolimit_mode': 'data'}): fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig, aspect='auto') skew.plot(p, t, 'r') skew.plot(p, tp, 'k') skew.shade_cape(p, t, tp) skew.shade_cin(p, t, tp) skew.ax.set_xlim(-50, 50) skew.ax.set_ylim(1000, 100) # This works around the fact that newer pint versions default to degrees_Celsius skew.ax.set_xlabel('degC') return fig
def Skew_T_diagram(T, z, wv): """ Plot a Skew-T diagram. Credits @ MetPy: https://unidata.github.io/MetPy/latest/index.html """ from metpy.plots import SkewT P = P_env(z) / 100 wvs = wv_sat(T, z) Td = T_dew(wv, z) env = T_env(z) zLCL = np.argmin(abs(T - Td)) P_LCL = P[:zLCL] Td = Td[:zLCL] fig = plt.figure(figsize=(11, 11)) skew = SkewT(fig, rotation=45) skew.plot(P_LCL, Td, 'b', linewidth=3, label='T_dew') skew.plot(P, T, 'r', linewidth=3, label='T_parc') skew.plot(P, env, 'g', linewidth=3, label='T_env') skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() skew.ax.set_ylim(1010, 100) skew.plot(P[zLCL], T[zLCL], '.k', markersize=15, label='LCL = %.1f km' % np.round(z[zLCL] / 1000, 1)) skew.shade_cin(P[zLCL:], env[zLCL:], T[zLCL:], label='Level above LFC') skew.shade_cape(P[zLCL:], env[zLCL:], T[zLCL:], label='CAPE') plt.legend() skew.ax.set_xlim(-30, 40) skew.ax.set_xlabel('Temperature [C]') skew.ax.set_ylabel('Pressure [hPa]') skew.ax.set_title('Skew-T diagram Essen Sounding') return plt.show()
threekm_bulk_shear = mpcalc.get_wind_speed(u_threekm_bulk_shear, v_threekm_bulk_shear) u_onekm_bulk_shear, v_onekm_bulk_shear = mpcalc.bulk_shear(p, u, v, hgt, bottom = min(hgt), depth = 1000 * units.meter) onekm_bulk_shear = mpcalc.get_wind_speed(u_onekm_bulk_shear, v_onekm_bulk_shear) #shows the level of the LCL, LFC, and EL. skew.ax.text(T[0].magnitude, p[0].magnitude + 5, str(int(np.round(T[0].to('degF').magnitude))), fontsize = 'medium', horizontalalignment = 'left', verticalalignment = 'top', color = 'red') skew.ax.text(Td[0].magnitude, p[0].magnitude + 5, str(int(np.round(Td[0].to('degF').magnitude))), fontsize = 'medium', horizontalalignment = 'right', verticalalignment = 'top', color = 'green') skew.ax.text(lcl_temperature.magnitude + 5, lcl_pressure.magnitude, "---- LCL", fontsize = 'medium', verticalalignment = 'center') skew.ax.text(Td[0].magnitude - 10, p[0].magnitude, 'SFC: {}hPa ----'.format(p[0].magnitude), fontsize = 'medium', horizontalalignment = 'right', verticalalignment = 'center', color = 'black') if str(lfc_temperature.magnitude) != 'nan': #checks to see if LFC/EL exists. If not, skip. skew.ax.text(lfc_temperature.magnitude + 5, lfc_pressure.magnitude, "---- LFC", fontsize = 'medium', verticalalignment = 'center') skew.ax.text(el_temperature.magnitude + 5, el_pressure.magnitude, "---- EL", fontsize = 'medium', verticalalignment = 'center') skew.plot(p, prof, 'k-', linewidth=1) #plots parcel profile skew.shade_cape(p, T, prof) #shades cape #plots potential temperature pot_temp.plot(equiv_pot_temp, p) pot_temp.invert_yaxis() pot_temp.set_title('Equivalent Potential Temperature', fontsize = 'small') pot_temp.set_ylabel('') pot_temp.set_xlabel('') pot_temp.set_ylim(1075, 525, 200) pot_temp.set_xlim(min(equiv_pot_temp.magnitude) - 20, 390, 20) #shows variables fig.text(0.70, 0.42, "MUCAPE: %0.2f J/kg" % cape.magnitude, fontsize = 'large', color = '#ff9933') fig.text(0.70, 0.45, "SBCAPE: %0.2f J/kg" % cape_sfc.magnitude, fontsize = 'large', color = '#ff9933') fig.text(0.70, 0.48, "PWAT: %0.2f .in" % pwat.magnitude, fontsize = 'large', color = '#3399ff') fig.text(0.70, 0.51, "0-3km Shear: %0.0d knots" % np.round(threekm_bulk_shear.magnitude), fontsize = 'large', color = 'k')
# log scaling in Y, as dictated by the typical meteorological plot skew.plot(p, T, 'r') skew.plot(p, Td, 'g') skew.plot_barbs(p, u, v) skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) # Plot LCL temperature as black dot skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Plot the parcel profile as a black line skew.plot(p, parcel_prof, 'k', linewidth=2) # Shade areas of CAPE and CIN skew.shade_cin(p, T, parcel_prof) skew.shade_cape(p, T, parcel_prof) # Plot a zero degree isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() # Show the plot plt.show() ########################################################################## # Adding a Hodograph # ------------------
def plot_upper_air(station='11035', date=False): ''' ----------------------------- Default use of plot_upper_air: This will plot a SkewT sounding for station '11035' (Wien Hohe Warte) plot_upper_air(station='11035', date=False) ''' # sns.set(rc={'axes.facecolor':'#343837', 'figure.facecolor':'#343837', # 'grid.linestyle':'','axes.labelcolor':'#04d8b2','text.color':'#04d8b2', # 'xtick.color':'#04d8b2','ytick.color':'#04d8b2'}) # Get time in UTC station = str(station) if date is False: now = datetime.utcnow() # If morning then 0z sounding, otherwise 12z if now.hour < 12: hour = 0 else: hour = 12 date = datetime(now.year, now.month, now.day, hour) datestr = date.strftime('%Hz %Y-%m-%d') print('{}'.format(date)) else: year = int(input('Please specify the year: ')) month = int(input('Please specify the month: ')) day = int(input('Please specify the day: ')) hour = int(input('Please specify the hour: ')) if hour < 12: hour = 0 else: hour = 12 date = datetime(year, month, day, hour) datestr = date.strftime('%Hz %Y-%m-%d') print('You entered {}'.format(date)) # This requests the data 11035 is df = WyomingUpperAir.request_data(date, station) # Create single variables wih the right units p = df['pressure'].values * units.hPa T = df['temperature'].values * units.degC Td = df['dewpoint'].values * units.degC wind_speed = df['speed'].values * units.knots wind_dir = df['direction'].values * units.degrees wind_speed_6k = df['speed'][df.height <= 6000].values * units.knots wind_dir_6k = df['direction'][df.height <= 6000].values * units.degrees u, v = mpcalc.get_wind_components(wind_speed, wind_dir) u6, v6 = mpcalc.get_wind_components(wind_speed_6k, wind_dir_6k) # Calculate the LCL lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) print(lcl_pressure, lcl_temperature) # Calculate the parcel profile. parcel_prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') cape, cin = mpcalc.cape_cin(p, T, Td, parcel_prof) ############################# # Create a new figure. The dimensions here give a good aspect ratio fig = plt.figure(figsize=(9, 9)) gs = gridspec.GridSpec(3, 3) skew = SkewT(fig, rotation=45, subplot=gs[:, :2]) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.plot(p, T, 'r') skew.plot(p, Td, 'g') skew.plot_barbs(p, u, v) skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-45, 40) # Plot LCL as black dot skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Plot the parcel profile as a black line skew.plot(p, parcel_prof, 'k', linewidth=2) # Shade areas of CAPE and CIN skew.shade_cin(p, T, parcel_prof) skew.shade_cape(p, T, parcel_prof) # Plot a zero degree isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) skew.ax.set_title('Station: ' + str(station) + '\n' + datestr) # set title skew.ax.set_xlabel('Temperature (C)') skew.ax.set_ylabel('Pressure (hPa)') # Add the relevant special lines skew.plot_dry_adiabats(linewidth=0.7) skew.plot_moist_adiabats(linewidth=0.7) skew.plot_mixing_lines(linewidth=0.7) # Create a hodograph # Create an inset axes object that is 40% width and height of the # figure and put it in the upper right hand corner. # ax_hod = inset_axes(skew.ax, '40%', '40%', loc=1) ax = fig.add_subplot(gs[0, -1]) h = Hodograph(ax, component_range=60.) h.add_grid(increment=20) # Plot a line colored by windspeed h.plot_colormapped(u6, v6, wind_speed_6k) # add another subplot for the text of the indices # ax_t = fig.add_subplot(gs[1:,2]) skew2 = SkewT(fig, rotation=0, subplot=gs[1:, 2]) skew2.plot(p, T, 'r') skew2.plot(p, Td, 'g') # skew2.plot_barbs(p, u, v) skew2.ax.set_ylim(1000, 700) skew2.ax.set_xlim(-30, 10) # Show the plot plt.show() return cape
skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) # Calculate LCL height and plot as black dot. Because `p`'s first value is # ~1000 mb and its last value is ~250 mb, the `0` index is selected for # `p`, `T`, and `Td` to lift the parcel from the surface. If `p` was inverted, # i.e. start from low value, 250 mb, to a high value, 1000 mb, the `-1` index # should be selected. lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Calculate full parcel profile and add to plot as black line prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') skew.plot(p, prof, 'k', linewidth=2) # Shade areas of CAPE and CIN skew.shade_cin(p, T, prof) skew.shade_cape(p, T, prof) # An example of a slanted line at constant T -- in this case the 0 # isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() # Show the plot plt.show()
def skewt_plots(dt, station, p, T, Td, u, v, outdir, idxij, utch, z): # skewt_plots(dt,station,p,T,Td,u,v,outdir,idxij,utch) # Function to make the graphic ploting of the sounding. # ---INPUTS: # dt = "2016-10-26 13h CET (12 UTC)" # string of valid time # station = "Piedtahita" station name # p pressure colmn on coordinates # T temp colmn on coordinates # Td dew point colmn on coordinates # u,v wind vectors colmn on coordinates # outdir='plots/' #output directory # idxij matrix coordinates of the sounding # utch="1200" #string of utc hour for filename # z geopotential height # ---OUTPUTS: # plot in outdir p = p.interp(south_north=idxij[1], west_east=idxij[0]) lon = p.XLONG.values lat = p.XLAT.values dx = p.projection.dx dy = p.projection.dy p = p.values T = T.interp(south_north=idxij[1], west_east=idxij[0]).values Td = Td.interp(south_north=idxij[1], west_east=idxij[0]).values u = u.interp(south_north=idxij[1], west_east=idxij[0]).values v = v.interp(south_north=idxij[1], west_east=idxij[0]).values z = z.interp(south_north=idxij[1], west_east=idxij[0]).values ###################################################################### # Make Skew-T Plot # ---------------- # The code below makes a basic skew-T plot using the MetPy plot module # that contains a SkewT class. # Change default to be better for skew-T fig = plt.figure(figsize=(11, 9)) plt.rcParams.update({'font.size': 12}) # Initiate the skew-T plot type from MetPy class loaded earlier skew = SkewT(fig, rotation=45) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.plot(p, T, 'r') skew.plot(p, Td, 'g') skew.plot_barbs(p[::3], u[::3], v[::3], y_clip_radius=0.03) # Set some appropriate axes limits for x and y #skew.ax.set_xlim(-30, 40) skew.ax.set_ylim(1020, 200) skew.ax.set_ylabel('Pressure [hPa]') skew.ax.set_xlabel('Temperature [ºC]') heights = np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11]) * units.km std_pressures = mpcalc.height_to_pressure_std(heights) for height_tick, p_tick in zip(heights, std_pressures): trans, _, _ = skew.ax.get_yaxis_text1_transform(0) skew.ax.text(0.02, p_tick, '---{:~d}'.format(height_tick), transform=trans) # Calculate LCL height and plot as black dot. Because `p`'s first value is # ~1000 mb and its last value is ~250 mb, the `0` index is selected for # `p`, `T`, and `Td` to lift the parcel from the surface. If `p` was inverted, # i.e. start from low value, 250 mb, to a high value, 1000 mb, the `-1` index # should be selected. lcl_pressure, lcl_temperature = mpcalc.lcl(p[0] * units.hPa, T[0] * units.degC, Td[0] * units.degC) skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Calculate full parcel profile and add to plot as black line prof = mpcalc.parcel_profile(p * units.hPa, T[0] * units.degC, Td[0] * units.degC).to('degC') skew.plot(p, prof, 'k', linewidth=2) # Shade areas of CAPE and CIN skew.shade_cin(p * units.hPa, T * units.degC, prof) skew.shade_cape(p * units.hPa, T * units.degC, prof) # An example of a slanted line at constant T -- in this case the 0 # isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines to plot throughout the figure skew.plot_dry_adiabats(t0=np.arange(233, 533, 10) * units.K, alpha=0.6, color='orangered') skew.plot_moist_adiabats(t0=np.arange(233, 400, 5) * units.K, alpha=0.6, color='tab:green') #skew.plot_mixing_lines(p=np.arange(1000, 99, -20) * units.hPa, # linestyle='dotted', color='tab:blue') # Add some descriptive titles plt.title('Sounding ' + station + '\n(' + str('{0:.6f}'.format(lat)) + ' , ' + str('{0:.6f}'.format(lon)) + ') ', loc='left') plt.title('Valid for: ' + dt + '\n by RASPURI ', loc='right') UTC_time = time.gmtime() plt.figtext( 0.99, 0.01, time.strftime('Computed on %d/%m/%y %H:%M:%S UTC \n', UTC_time) + 'dx: ' + str(dx) + 'm dy: ' + str(dy) + 'm ', horizontalalignment='right', fontsize=10) #plt.figtext(0.01, 0.01, 'RASPURI by Oriol Cevrelló ', horizontalalignment='right') plt.tight_layout() filename = outdir + station + '_' + utch + '.png' #plt.show() plt.savefig(filename) plt.close()
parcel_prof = mpcalc.parcel_profile( sound_pres, sound_temps[0].data * units.degC, sound_dp[0] ) cape = mpcalc.cape_cin( sound_pres, sound_temps.data * units.degC, sound_dp, parcel_prof ) capeout = int(cape[0].m) cinout = int(cape[1].m) skew.plot(sound_pres, sound_dp, "g", linewidth=3) skew.plot(sound_pres, sound_temps, "r", linewidth=3) if capeout > capemin: # Shade areas of CAPE and CIN skew.shade_cin(sound_pres, sound_temps.data * units.degC, parcel_prof) skew.shade_cape(sound_pres, sound_temps.data * units.degC, parcel_prof) skew.plot(sound_pres, parcel_prof, color="fuchsia", linewidth=1) skew.ax.axvline(0, color="purple", linestyle="--", linewidth=3) skew.ax.set_ylim((1000, ptop)) skew.ax.axis("off") # skew.ax.text(0.1,0.1,str(soundlat)+' '+str(soundlon)) for i in range(0, 5): soundlat = 31 soundlon = 360 - (startlat + (londelt * i)) sound_temps = data["temperature"].interp(lat=soundlat, lon=soundlon) - 273.15 sound_rh = data["rh"].interp(lat=soundlat, lon=soundlon) sound_dp = mpcalc.dewpoint_from_relative_humidity( sound_temps.data * units.degC, sound_rh.data * units.percent )
skew.plot(p, Td, 'g') skew.plot_barbs(p, u, v) skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) skew.ax.set_xlabel('Temperature [°C]') skew.ax.set_ylabel('Pressure [hPa]') # Plot LCL temperature as black dot skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Plot the parcel profile as a black line skew.plot(p, parcel_prof, 'k', linewidth=2) # Shade areas of CAPE and CIN skew.shade_cin(p, T, parcel_prof) skew.shade_cape(p, T, parcel_prof) # Plot a zero degree isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() # Add a title plt.title(str(date) + ' ' + station + ' Punta Arenas') # Add Legend plt.legend(['Temperature', 'Dew Point', 'LCL', 'parcel profile'])
def plot_soundings(fig,ax,temp,rh,centerlat,centerlon,domainsize,cape): ''' This function will plot a bunch of little soundings onto a matplotlib fig,ax. temp is an xarray dataarray with temperature data on pressure levels at least between 1000 and 300mb (you can change the ylimits for other datasets) rh is an xarray dataarray with temperature data on pressure levels at least between 1000 and 300mb (you can change ) centerlat and centerlon are the coordinates around which you want your map to be centered. both are floats or integers and are in degrees of latitude and degrees of longitude west (i.e. 70W would be input as positive 70 here) domainsize is a string either 'local' for ~WFO-size domains or 'regional' for NE/SE/Mid-Atlantic-size domains (12 deg lat by 15 deg lon). More will be added soon. cape is a boolean to indicate whether you want to overlay parcel paths and shade CAPE/CIN on soundings with >100 J/kg of CAPE (this value can be changed) note that this function doesn't "return" anything but if you just call it and provide the right arguments, it works. for example: import soundingmaps as smap ... smap.plot_soundings(fig,ax1,data['temperature'],data['rh'],30.5,87.5,'local',cape=True) ''' r=5 if domainsize=='local': init_lat_delt = 1.625 init_lon_delt = 0.45 lat_delts = [.2,.7,1.2,1.75,2.25,2.8] londelt = 0.76 startlon = centerlon-2+0.45 elif domainsize=='regional': init_lat_delt = 6 init_lon_delt = 1.6 lat_delts = [0.6,2.5,4.5,6.4,8.4,10.25] londelt = 2.9 startlon = centerlon-7.5+1.6 startlat = centerlat-init_lat_delt startlon = centerlon-2+0.45 sound_lats=[] sound_lons=[] for i in range(0,6): lats = startlat+lat_delts[i] sound_lats.append(lats) for i in range(0,r): lons = -startlon-(londelt*i) sound_lons.append(lons) plot_elevs=[0.2,0.3,0.4,0.5,0.6,0.7] dashed_red_line = lines.Line2D([], [], linestyle='solid', color='r', label='Temperature') dashed_purple_line = lines.Line2D([],[],linestyle='dashed',color='purple',label='0C Isotherm') dashed_green_line = lines.Line2D([], [], linestyle='solid', color='g', label='Dew Point') grey_line = lines.Line2D([], [], color='darkgray', label='MSLP (hPa)') blue_line = lines.Line2D([], [], color='b',label='2m 0C Isotherm') pink_line = lines.Line2D([], [], color='fuchsia',label='Surface-Based Parcel Path') red = mpatches.Patch(color='tab:red',label='CAPE') blue = mpatches.Patch(color='tab:blue',label='CIN') if cape==True: for k in range(len(plot_elevs)): soundlat = sound_lats[k] plot_elev = plot_elevs[k] if k==0: s=1 else: s=0 for i in range(s,r): sound_pres = temp.lev soundlon = -(startlon+(londelt*i)) sound_temps = temp.interp(lat=soundlat,lon=soundlon)-273.15 sound_rh = rh.interp(lat=soundlat,lon=soundlon) sound_dp = mpcalc.dewpoint_from_relative_humidity(sound_temps.data*units.degC,sound_rh.data*units.percent) skew = SkewT(fig=fig,rect=(0.75-(0.15*i),plot_elev,.15,.1)) parcel_prof = mpcalc.parcel_profile(sound_pres,sound_temps[0].data*units.degC,sound_dp[0]) cape = mpcalc.cape_cin(sound_pres,sound_temps.data*units.degC,sound_dp,parcel_prof) capeout = int(cape[0].m) cinout = int(cape[1].m) skew.plot(sound_pres,sound_dp,'g',linewidth=3) skew.plot(sound_pres,sound_temps,'r',linewidth=3) if capeout >100: # Shade areas of CAPE and CIN print(sound_temps) print(parcel_prof) skew.shade_cin(sound_pres, sound_temps.data*units.degC, parcel_prof) skew.shade_cape(sound_pres, sound_temps.data*units.degC, parcel_prof) skew.plot(sound_pres,parcel_prof,color='fuchsia',linewidth=1) skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3) skew.ax.set_ylim((1000,300)) skew.ax.axis('off') leg = ax.legend(handles=[dashed_red_line,dashed_green_line,dashed_purple_line,pink_line,red,blue],title='Sounding Legend',loc=4,framealpha=1) else: for k in range(len(plot_elevs)): soundlat = sound_lats[k] plot_elev = plot_elevs[k] if k==0: s=1 else: s=0 for i in range(s,r): soundlon = -(startlon+(londelt*i)) sound_pres = temp.lev sound_temps = temp.interp(lat=soundlat,lon=soundlon)-273.15 sound_rh = rh.interp(lat=soundlat,lon=soundlon) sound_dp = mpcalc.dewpoint_from_relative_humidity(sound_temps.data*units.degC,sound_rh.data*units.percent) skew = SkewT(fig=fig,rect=(0.75-(0.15*i),plot_elev,.15,.1)) skew.plot(sound_pres,sound_dp,'g',linewidth=3) skew.plot(sound_pres,sound_temps,'r',linewidth=3) skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3) skew.ax.set_ylim((1000,300)) skew.ax.axis('off') leg = ax.legend(handles=[dashed_red_line,dashed_green_line,dashed_purple_line],title='Sounding Legend',loc=4,framealpha=1)
def draw_sta_skewT(p=None, T=None, Td=None, wind_speed=None, wind_dir=None, u=None, v=None, fcst_info=None, output_dir=None): fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig, rotation=45) plt.rcParams['font.sans-serif'] = ['SimHei'] # 步骤一(替换sans-serif字体) plt.rcParams['axes.unicode_minus'] = False # 步骤二(解决坐标轴负数的负号显示问题) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot. skew.plot(p, T, 'r') skew.plot(p, Td, 'g') skew.plot_barbs(p, u, v) skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) # Calculate LCL height and plot as black dot. Because `p`'s first value is # ~1000 mb and its last value is ~250 mb, the `0` index is selected for # `p`, `T`, and `Td` to lift the parcel from the surface. If `p` was inverted, # i.e. start from low value, 250 mb, to a high value, 1000 mb, the `-1` index # should be selected. lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Calculate full parcel profile and add to plot as black line prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') skew.plot(p, prof, 'k', linewidth=2) # Shade areas of CAPE and CIN skew.shade_cin(p, T, prof) skew.shade_cape(p, T, prof) # An example of a slanted line at constant T -- in this case the 0 # isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() #forecast information bax = plt.axes([0.12, 0.88, .25, .07], facecolor='#FFFFFFCC') bax.axis('off') bax.axis([0, 10, 0, 10]) initTime = pd.to_datetime(str( fcst_info['forecast_reference_time'].values)).replace( tzinfo=None).to_pydatetime() if (sys.platform[0:3] == 'lin'): locale.setlocale(locale.LC_CTYPE, 'zh_CN.utf8') if (sys.platform[0:3] == 'win'): locale.setlocale(locale.LC_CTYPE, 'chinese') plt.text(2.5, 7.5, '起报时间: ' + initTime.strftime("%Y年%m月%d日%H时"), size=11) plt.text(2.5, 5.0, '[' + str(fcst_info.attrs['model']) + '] ' + str(int(fcst_info['forecast_period'].values[0])) + '小时预报探空', size=11) plt.text(2.5, 2.5, '预报点: ' + str(fcst_info.attrs['points']['lon']) + ', ' + str(fcst_info.attrs['points']['lat']), size=11) plt.text(2.5, 0.5, 'www.nmc.cn', size=11) utl.add_logo_extra_in_axes(pos=[0.1, 0.88, .07, .07], which='nmc', size='Xlarge') # Show the plot if (output_dir != None): plt.savefig( output_dir + '时间剖面产品_起报时间_' + str(fcst_info['forecast_reference_time'].values)[0:13] + '_预报时效_' + str(int(fcst_info.attrs['forecast_period'].values)) + '.png', dpi=200, bbox_inches='tight') else: plt.show()
def main(): args = get_args() setup_logging(args['verbose']) # Define input file file = args['inputfile'] output = args['outputfile'] ds = xr.open_dataset(file) ds_sel = ds.isel({'sounding': 0}) ds_sel = ds_sel.sortby(ds_sel.pressure, ascending=False) p = ds_sel.pressure.values T = ds_sel.temperature.values Td = ds_sel.dewPoint.values wind_speed = ds_sel.windSpeed.values wind_dir = ds_sel.windDirection.values # Filter nans idx = np.where((np.isnan(T) + np.isnan(Td) + np.isnan(p) + np.isnan(wind_speed) + np.isnan(wind_dir)) == False, True, False) p = p[idx] T = T[idx] Td = Td[idx] wind_speed = wind_speed[idx] wind_dir = wind_dir[idx] # Add units p = p * units.hPa T = T * units.degC Td = Td * units.degC wind_speed = wind_speed * (units.meter / units.second) wind_dir = wind_dir * units.degrees u, v = mpcalc.wind_components(wind_speed, wind_dir) lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) parcel_prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') # Create a new figure. The dimensions here give a good aspect ratio fig = plt.figure(figsize=(9, 10)) skew = SkewT(fig, rotation=30) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.plot(p, T, 'r') skew.plot(p, Td, 'g') # Plot only specific barbs to increase visibility pressure_levels_barbs = np.logspace(0.1, 1, 50) * 100 def find_nearest(array, value): array = np.asarray(array) idx = (np.abs(array - value)).argmin() return array[idx] # Search for levels by providing pressures # (levels is the coordinate not pressure) pres_vals = ds_sel.pressure.values[idx] closest_pressure_levels = np.unique( [find_nearest(pres_vals, p_) for p_ in pressure_levels_barbs]) _, closest_pressure_levels_idx, _ = np.intersect1d(pres_vals, closest_pressure_levels, return_indices=True) p_barbs = ds_sel.pressure.isel({ 'levels': closest_pressure_levels_idx }).values * units.hPa wind_speed_barbs = ds_sel.windSpeed.isel({ 'levels': closest_pressure_levels_idx }).values * (units.meter / units.second) wind_dir_barbs = ds_sel.windDirection.isel({ 'levels': closest_pressure_levels_idx }).values * units.degrees u_barbs, v_barbs = mpcalc.wind_components(wind_speed_barbs, wind_dir_barbs) # Find nans in pressure # p_non_nan_idx = np.where(~np.isnan(pres_vals)) skew.plot_barbs(p_barbs, u_barbs, v_barbs, xloc=1.06) # set axes limits: skew.ax.set_ylim(1020, 100) skew.ax.set_xlim(-50, 40) # Plot LCL as black dot skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Plot the parcel profile as a black line skew.plot(pres_vals, parcel_prof, 'k', linewidth=1.6) # Shade areas of CAPE and CIN skew.shade_cin(pres_vals, T, parcel_prof) skew.shade_cape(pres_vals, T, parcel_prof) # Plot a zero degree isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() # Create a hodograph # Create an inset axes object that is 40% width and height of the # figure and put it in the upper right hand corner. ax_hod = inset_axes(skew.ax, '35%', '35%', loc=1) h = Hodograph(ax_hod, component_range=75.) h.add_grid(increment=20) h.plot_colormapped(u, v, wind_speed) # Plot a line colored by wind speed # Set title sounding_name = ds_sel.sounding.values sounding_name_str = str(sounding_name.astype('str')) skew.ax.set_title('{sounding}'.format(sounding=sounding_name_str)) if output is None: output = str(os.path.basename(file).split('.')[0]) + '.pdf' logging.info('Write output to {}'.format(output)) plt.savefig(output)
def core_ens(p, T, Td, u, v, p2, T2, Td2, **kwargs): #from IPython import embed; embed() T = T.to(units.K) Td = Td.to(units.K) Tmean = T.mean(axis=0) Tdmean = Td.mean(axis=0) pmean = p.mean(axis=0) umean, vmean = u.mean(axis=0), v.mean(axis=0) Tstd = np.std(T.data, axis=0) * units.K Tdstd = np.std(Td.data, axis=0) * units.K # Calculate the parcel profile. parcel_prof = mpcalc.parcel_profile(pmean, Tmean[0], Tdmean[0]).to('degC') # Create a new figure. The dimensions here give a good aspect ratio fig = plt.figure(figsize=(8, 8)) skew = SkewT(fig, rotation=45) # Plot a zero degree isotherm skew.ax.axvline(0, color='k', linestyle='--', linewidth=1) # Add the relevant special lines skew.plot_dry_adiabats(lw=.5) skew.plot_moist_adiabats(lw=.5) skew.plot_mixing_lines(lw=.5) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.shade_area(pmean, Tmean - Tstd, Tmean + Tstd, color='r', label='std$_{ens}$ mean$_{dom}$ T$_{fc}$') skew.shade_area(pmean, Tdmean - Tdstd, Tdmean + Tdstd, color='g', label='std$_{ens}$ mean$_{dom}$ Td$_{fc}$') #skew.plot(p, Tmean+np.std(T), '-', color='grey', lw=1, label='p.999(T)') skew.plot(pmean, Tmean, 'r:', ms=3, lw=1, label='mean$_{ens, dom}$ T$_{fc}$') skew.plot(pmean, Tdmean, 'g:', ms=3, lw=1, label='mean$_{ens, dom}$ Td$_{fc}$') skew.plot_barbs(pmean, umean, vmean) # Plot the parcel profile as a black line skew.plot(pmean, parcel_prof, 'k', linewidth=.5) # Shade areas of CAPE and CIN skew.shade_cin(pmean, Tmean, parcel_prof, alpha=0.2) skew.shade_cape(pmean, Tmean, parcel_prof, alpha=0.2) # nature skew.plot(p2, T2, 'r.-', ms=5, lw=2, label='mean$_{ens, dom}$ T$_{nature}$') skew.plot(p2, Td2, 'g.-', ms=5, lw=2, label='mean$_{ens, dom}$ Td$_{nature}$') skew.ax.set_ylim(1000, 180) skew.ax.set_xlim(-20, 40) plt.legend(loc='lower left') plt.title(kwargs.get('title')) fname = kwargs.get('saveto', 'profile.png') fig.savefig(fname) print(fname, 'saved.') plt.close()
def skewt_plot(p, tc, tdc, t0, date, u=None, v=None, fout='sounding.png', latlon='', title='', show=False): """ h: heights p: pressure tc: Temperature [C] tdc: Dew point [C] date: date of the forecast u,v: u,v wind components adapted from: https://geocat-examples.readthedocs.io/en/latest/gallery/Skew-T/NCL_skewt_3_2.html#sphx-glr-gallery-skew-t-ncl-skewt-3-2-py """ Pmax = 150 # XXX upper limit print('Checking units') if p.attrs['units'] != 'hPa': print('P wrong units') exit() if tc.attrs['units'] != 'degC': print('Tc wrong units') exit() if tdc.attrs['units'] != 'degC': print('Tdc wrong units') exit() if t0.attrs['units'] != 'degC': print('T0 wrong units') exit() if type(u) != type(None) and type(v) != type(None): if u.attrs['units'] != 'm s-1': print('Wind wrong units') exit() LG.info('Inside skewt plot') p = p.values tc = tc.values tdc = tdc.values t0 = t0.mean().values u = u.values * 3.6 # km/h v = v.values * 3.6 # km/h # Grid plot LG.info('creating figure') fig = plt.figure(figsize=(11, 12)) LG.info('created figure') LG.info('creating axis') gs = gridspec.GridSpec(1, 2, width_ratios=[4, 1]) fig.subplots_adjust(wspace=0., hspace=0.) # ax1 = plt.subplot(gs[1:-1,0]) # Adding the "rotation" kwarg will over-ride the default MetPy rotation of # 30 degrees for the 45 degree default found in NCL Skew-T plots LG.info('created axis') LG.info('Creatin SkewT') skew = SkewT(fig, rotation=45, subplot=gs[0, 0]) ax = skew.ax LG.info('Created SkewT') if len(latlon) > 0: ax.text(0, 1, latlon, va='top', ha='left', color='k', fontsize=12, bbox=dict(boxstyle="round", ec=None, fc=(1., 1., 1., 0.9)), zorder=100, transform=ax.transAxes) # Plot the data, T and Tdew vs pressure skew.plot(p, tc, 'C3') skew.plot(p, tdc, 'C0') LG.info('plotted dew point and sounding') # LCL lcl_pressure, lcl_temperature = mpcalc.lcl(p[0] * units.hPa, t0 * units.degC, tdc[0] * units.degC) skew.plot(lcl_pressure, lcl_temperature, 'k.') # Calculate the parcel profile #XXX units workaround parcel_prof = mpcalc.parcel_profile(p * units.hPa, t0 * units.degC, tdc[0] * units.degC).to('degC') # Plot cloud base ind_cross = np.argmin(np.abs(parcel_prof.magnitude - tc)) p_base = np.max([lcl_pressure.magnitude, p[ind_cross]]) t_base = np.max([lcl_temperature.magnitude, tc[ind_cross]]) m_base = mpcalc.pressure_to_height_std(np.array(p_base) * units.hPa) m_base = m_base.to('m').magnitude skew.ax.axhline(p_base, color=(0.5, 0.5, 0.5), ls='--') skew.plot(p_base, t_base, 'C3o', zorder=100) skew.ax.text(t_base, p_base, f'{m_base:.0f}m', ha='left') # Plot the parcel profile as a black line skew.plot(p, parcel_prof, 'k', linewidth=1) LG.info('plotted parcel profile') # shade CAPE and CIN skew.shade_cape(p * units.hPa, tc * units.degC, parcel_prof) skew.shade_cin(p * units.hPa, tc * units.degC, parcel_prof, tdc * units.degC) LG.info('plotted CAPE and CIN') if type(u) != type(None) and type(v) != type(None): LG.info('Plotting wind') ax2 = plt.subplot(gs[0, 1], sharey=ax, zorder=-1) # ax2.yaxis.set_visible(False) ax2.yaxis.tick_right() # ax2.xaxis.tick_top() wspd = np.sqrt(u * u + v * v) ax2.scatter(wspd, p, c=p, cmap=HEIGHTS, zorder=10) gnd = mpcalc.pressure_to_height_std(np.array(p[0]) * units.hPa) gnd = gnd.to('m') # Ground ax2.axhline(p[0], c='k', ls='--') ax2.text(56, p[0], f'{int(gnd.magnitude)}m', horizontalalignment='right') # Techo ax2.axhline(p_base, c=(0.5, 0.5, 0.5), ls='--') ### Background colors ## #for i,c in enumerate(WindSpeed.colors): # rect = Rectangle((i*4, 150), 4, 900, color=c, alpha=0.5,zorder=-1) # ax2.add_patch(rect) ######################### ax2.set_xlim(0, 56) ax2.set_xlabel('Wspeed (km/h)') ax2.grid() def p2h(x): """ x in hPa """ y = mpcalc.pressure_to_height_std(np.array(x) * units.hPa) # y = y.metpy.convert_units('m') y = y.to('m') return y.magnitude def h2p(x): """ x in m """ # x = x.values y = mpcalc.height_to_pressure_std(np.array(x) * units.m) # y = y.metpy.convert_units('hPa') y = y.to('hPa') return y.magnitude # print('------') # print(p[0]) # print('---') # print(p2h(p[0])) # print('---') # print(h2p(p2h(p[0]))) # print('------') # exit() ax2y = ax2.secondary_yaxis(1.02, functions=(p2h, h2p)) ax2y.set_ylabel('height (m)') # ax2y.set_yticks(np.array([1000,2000,3000,4000,6000,6000,10000])) # XXX Not working ax2y.yaxis.set_major_formatter(ScalarFormatter()) ax2y.yaxis.set_minor_formatter(ScalarFormatter()) ################# ax2y.set_color('red') ax2y.tick_params(colors='red', size=7, width=1, which='both') # 'both' refers to minor and major axes # ax2y.set_yticks([1,2,3,4]) #ax2.set_xticks([0,5,10,15,20,30,40,50]) #ax2.set_xticklabels(['0','','10','','20','30','40','50']) ax2.set_xticks([0, 4, 8, 12, 16, 20, 24, 32, 40, 48, 56]) ax2.set_xticklabels( ['0', '', '8', '', '16', '', '24', '32', '40', '48', '56'], fontsize=11, va='center') plt.setp(ax2.get_yticklabels(), visible=False) # Hodograph ax_hod = inset_axes(ax2, '110%', '30%', loc=1) ax_hod.set_yticklabels([]) ax_hod.set_xticklabels([]) L = 80 ax_hod.text(0, L - 5, 'N', horizontalalignment='center', verticalalignment='center') ax_hod.text(L - 5, 0, 'E', horizontalalignment='center', verticalalignment='center') ax_hod.text(-(L - 5), 0, 'W', horizontalalignment='center', verticalalignment='center') ax_hod.text(0, -(L - 5), 'S', horizontalalignment='center', verticalalignment='center') h = Hodograph(ax_hod, component_range=L) h.add_grid(increment=20) h.plot_colormapped( -u, -v, p, cmap=HEIGHTS ) #'viridis_r') # Plot a line colored by pressure (altitude) LG.info('Plotted wind') # # print('=-=-=-=-=-=-=') # # print(ax2.get_yscale()) # # print(ax2y.get_yscale()) # # print('=-=-=-=-=-=-=') # # ax2y.yaxis.tick_right() # # # ax2y.set_ylim(*reversed(ax.get_ylim())) # # ax2y.set_ylim(*ax.get_ylim()) # # # calc pressure to height # # locs = ax2.get_yticks() # # labels = [mpcalc.pressure_to_height_std(h*units.hPa) for h in locs] # # labels = [round(l.to('m'),1) for l in labels] # # for xp,xh in zip(locs,labels): # # print(xp,xh) # # ax2y.set_yticks(locs) # # ax2y.set_yticklabels([f'{int(l.magnitude)}' for l in labels]) # Plot only every n windbarb n = 4 inds, = np.where(p > Pmax) break_p = 25 inds_low = inds[:break_p] inds_high = inds[break_p:] inds = np.append(inds_low[::n], inds_high) skew.plot_barbs( pressure=p[inds], #[::n], # * units.hPa, u=u[inds], #[::n], v=v[inds], #[::n], xloc=0.985, # fill_empty=True, sizes=dict(emptybarb=0.075, width=0.1, height=0.2)) # # # Draw line underneath wind barbs # # line = mlines.Line2D([1.05, 1.05], [0, 1], color='gray', linewidth=0.5, # # transform=ax.transAxes, # # dash_joinstyle='round', # # clip_on=False, # # zorder=0) # # ax.add_line(line) # Add relevant special lines # Choose starting temperatures in Kelvin for the dry adiabats LG.info('Plot adiabats, and other grid lines') skew.ax.text(t0, p[0], f'{t0:.1f}°C', va='top') skew.ax.axvline(t0, color=(0.5, 0.5, 0.5), ls='--') t0 = units.K * np.arange(243.15, 473.15, 10) skew.plot_dry_adiabats(t0=t0, linestyles='solid', colors='gray', linewidth=1) # Choose temperatures for moist adiabats t0 = units.K * np.arange(281.15, 306.15, 4) msa = skew.plot_moist_adiabats(t0=t0, linestyles='solid', colors='lime', linewidths=.75) # Choose mixing ratios w = np.array([0.001, 0.002, 0.003, 0.005, 0.008, 0.012, 0.020]).reshape(-1, 1) # Choose the range of pressures that the mixing ratio lines are drawn over p_levs = units.hPa * np.linspace(1000, 400, 7) skew.plot_mixing_lines(mixing_ratio=w, pressure=p_levs, linestyle='dotted', linewidths=1, colors='lime') LG.info('Plotted adiabats, and other grid lines') skew.ax.set_ylim(1000, Pmax) skew.ax.set_xlim(-20, 43) # skew.ax.set_xlim(-30, 40) # XXX gvutil not installed # gvutil.set_titles_and_labels(ax, maintitle="ATS Rawinsonde: degC + Thin wind") # Set axes limits and ticks # gvutil.set_axes_limits_and_ticks(ax=ax, xlim=[-30, 50], # yticks=[1000, 850, 700, 500, 400, 300, 250, 200, 150, 100]) # Change the style of the gridlines ax.grid(True, which='major', axis='both', color='tan', linewidth=1.5, alpha=0.5) ax.set_xlabel("Temperature (C)") ax.set_ylabel("P (hPa)") if len(title) == 0: title = f"{(date).strftime('%d/%m/%Y-%H:%M')} (local time)" ax.set_title(title, fontsize=20) else: ax.set_title(title, fontsize=20) if show: plt.show() LG.info('saving') fig.savefig(fout, bbox_inches='tight', pad_inches=0.1, dpi=150, quality=90) LG.info('saved') plt.close('all')
def cape(filelist,storm,track,show): #Sort filelist. filelist=np.sort(filelist) # Get sampling periods (this will be a dictionary). See the toolbox print('Retrieving sampling periods') sampleperiods=getsamplingperiods(filelist,3.) # Iterate over all sampling periods. for sampindex,periodskey in enumerate(sampleperiods): #Allocate starting (stdt) and ending date (endt). Remeber dt is the convetional short-name for date. stdt=periodskey endt=sampleperiods[periodskey] # Define sampling period string period=str(stdt.hour)+'_'+str(stdt.day)+'-'+str(endt.hour)+'_'+str(endt.day) # Create new-empty lists. lats=[] lons=[] xs=[] ys=[] capes=[] cins=[] distfig = plt.figure(figsize=(13, 9)) ax=distfig.add_subplot(111) print('start filelist loop') # Iterate over all files. for filename in filelist: # Select end-name of file by inspecting filename string. Notice how filename can change how file is read. if 'radazm' in filename.split('/')[-1] or 'eol' in filename.split('/')[-1]: end='radazm' else: end='avp' # Obtain properties of file, i.e., launch time and location into a dictionary (dicc). dicc=findproperties(filename,end) # Condition to see if current file is in sampling period. # Notice how if structure is constructed, condition finds times outside of sampling period and # if found outside the sampling period, continue to next file. if dicc['Launch Time']<stdt or dicc['Launch Time'] > endt: continue nump=np.genfromtxt(filename,skip_header=16,skip_footer=0) temperature=clean1(nump[:,5]) pressure=clean1(nump[:,4]) Height=clean1(nump[:,13]) if np.nanmax(Height)<3500: continue #Clean for cape RelH=clean1(nump[:,7]) lon=clean1(nump[:,14]) lat=clean1(nump[:,15]) lon=clean1(lon) lat=clean1(lat) mlon=np.nanmean(lon) mlat=np.nanmean(lat) RH=RelH/100 T,P,rh,dz=cleanforcape(temperature,pressure,RH,Height) #Metpy set-up T=np.flip(T,0) rh=np.flip(rh,0) p=np.flip(P,0) dz=np.flip(dz,0) p=p*units.hPa T=T*units.celsius mixing=rh*mpcalc.saturation_mixing_ratio(p,T) epsilon=0.6219800858985514 Tv=mpcalc.virtual_temperature(T, mixing, molecular_weight_ratio=epsilon) dwpoint=mpcalc.dewpoint_rh(T, rh) blh_indx=np.where(dz<500) try: parcelprofile=mpcalc.parcel_profile(p,np.nanmean(T[blh_indx])*units.celsius,mpcalc.dewpoint_rh(np.nanmean(T[blh_indx])*units.celsius, np.nanmean(rh[blh_indx]))).to('degC') Tv_parcelprofile=mpcalc.virtual_temperature(parcelprofile, mixing, molecular_weight_ratio=epsilon) cape,cin=cape_cin(p,Tv,dwpoint,Tv_parcelprofile,dz,T) except: continue plotskewT=True if plotskewT==True: os.system('mkdir figs/skewt') fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig, rotation=45) skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) skew.plot(p, dwpoint, 'g',label=r'$T_{dp}$') skew.plot(p, Tv, 'r',label=r'$T_v$') plt.text(-120,120,str(np.around(cape,2)),fontsize=14,fontweight='bold') # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.plot(p,Tv_parcelprofile,'k',label=r'$T_{v env}$') skew.shade_cin(p, T, parcelprofile,label='CIN') skew.shade_cape(p, Tv, Tv_parcelprofile,label='CAPE') skew.plot_dry_adiabats() skew.plot_moist_adiabats() plt.legend() plt.title(storm + ' on' + period,fontsize=14) plt.savefig('figs/skewt/'+storm+str(dicc['Launch Time'].time())+'.png') #plt.show() plt.close() r,theta=cart_to_cylindr(mlon,mlat,track,dicc['Launch Time']) if not(np.isnan(r)) and not(np.isnan(theta)) and not(np.isnan(cape.magnitude)): xs.append(r*np.cos(theta)) ys.append(r*np.sin(theta)) capes.append(cape.magnitude) cins.append(cin) cs=ax.scatter(xs,ys,c=np.asarray(capes),cmap='jet') for i,xi in enumerate(xs): ax.text(xi,ys[i]+10,str(np.around(capes[i],1))) plt.colorbar(cs) ax.scatter(0,0,marker='v',s=100,color='black') ax.grid() ax.set_xlabel('X distance [km]') ax.set_ylabel('Y distance [km]') ax.set_title('CAPE distribution for '+storm+' on '+period,fontsize=14) distfig.savefig('figs/cape'+storm+period+'.png') if show: plt.show()
skew.ax.tick_params(axis="y", labelsize=14, pad=0.5) # Calculate full parcel profile and add to plot as black line prof_0 = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') skew.plot(p, prof_0, 'k', linewidth=1.5) # Shade areas of CAPE and CIN ''' Calculate the convective available potential energy (CAPE) and convective inhibition (CIN) of a given upper air profile and most unstable parcel path. CIN is integrated between the surface and LFC, CAPE is integrated between the LFC and EL (or top of sounding). Intersection points of the measured temperature profile and parcel profile are linearly interpolated. ''' skew.shade_cin(p, T, prof_0) skew.shade_cape(p, T, prof_0) # Calculate LCL height and plot as black dot lcl_pressure, lcl_temperature = mpcalc.lcl( p[0], T[0], Td[0]) skew.plot(lcl_pressure, lcl_temperature, 'ko', markersize=8, fillstyle='none', label='LCL') # Calculate LCF height and plot as purple dot LCF_pressure, LCF_temperature = mpcalc.lfc( p, T, Td, prof_0) skew.plot(LCF_pressure,
def main(): args = get_args() setup_logging(args["verbose"]) # Define input file file = args["inputfile"] output = args["outputfile"] ds = xr.open_dataset(file) ds_sel = ds.isel({"sounding": 0}) ds_sel = ds_sel.sortby(ds_sel.p, ascending=False) attrs = ds_sel.attrs ds_sel = ds_sel.metpy.quantify() p = ds_sel.p T = ds_sel.ta Td = ds_sel.dp wind_speed = ds_sel.wspd wind_dir = ds_sel.wdir ascend_rate = ds_sel.dz launch_time = attrs["time_of_launch_HHmmss"] platform = attrs["platform"] resolution = attrs["resolution"].replace(" ", "") # Filter nans idx = np.where( (np.isnan(T) + np.isnan(Td) + np.isnan(p) + np.isnan(wind_speed) + np.isnan(wind_dir)) is False, True, False, ) p = p[idx].metpy.convert_units("hPa") T = T[idx].metpy.convert_units("degC") Td = Td[idx].metpy.convert_units("degC") wind_speed = wind_speed[idx].metpy.convert_units("meter / second") wind_dir = wind_dir[idx] u, v = mpcalc.wind_components(wind_speed, wind_dir) lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) parcel_prof = mpcalc.parcel_profile(p, T[0], Td[0]) parcel_prof = parcel_prof.metpy.convert_units("degC") direction = find_direction_of_sounding(ascend_rate) # Create a new figure. The dimensions here give a good aspect ratio fig = plt.figure(figsize=(9, 10)) skew = SkewT(fig, rotation=30) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.plot(p, T, "r") skew.plot(p, Td, "g") # Plot only specific barbs to increase visibility pressure_levels_barbs = np.logspace(0.1, 1, 50) * 100 def find_nearest(array, value): array = np.asarray(array) idx = (np.abs(array - value)).argmin() return array[idx] # Search for levels by providing pressures # (levels is the coordinate not pressure) pres_vals = p.isel(level=idx) closest_pressure_levels = np.unique( [find_nearest(pres_vals, p_) for p_ in pressure_levels_barbs]) _, closest_pressure_levels_idx, _ = np.intersect1d(pres_vals, closest_pressure_levels, return_indices=True) p_barbs = p.isel({"level": closest_pressure_levels_idx}) wind_speed_barbs = wind_speed.isel({"level": closest_pressure_levels_idx}) wind_dir_barbs = wind_dir.isel({"level": closest_pressure_levels_idx}) u_barbs, v_barbs = mpcalc.wind_components(wind_speed_barbs, wind_dir_barbs) # Find nans in pressure skew.plot_barbs(p_barbs, u_barbs, v_barbs, xloc=1.06) skew.ax.set_ylim(1020, 100) skew.ax.set_xlim(-50, 40) # Plot LCL as black dot skew.plot(lcl_pressure, lcl_temperature, "ko", markerfacecolor="black") # Plot the parcel profile as a black line skew.plot(pres_vals, parcel_prof, "k", linewidth=1.6) # Shade areas of CAPE and CIN skew.shade_cin( pres_vals.metpy.convert_units("hPa").values, T.metpy.convert_units("degC").values, parcel_prof.metpy.convert_units("degC").values, ) skew.shade_cape( pres_vals.metpy.convert_units("hPa").values, T.metpy.convert_units("degC").values, parcel_prof.metpy.convert_units("degC").values, ) # Plot a zero degree isotherm skew.ax.axvline(0, color="c", linestyle="--", linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() # Create a hodograph # Create an inset axes object that is 40% width and height of the # figure and put it in the upper right hand corner. ax_hod = inset_axes(skew.ax, "35%", "35%", loc=1) h = Hodograph(ax_hod, component_range=75.0) h.add_grid(increment=20) h.plot_colormapped(u, v, wind_speed) # Plot a line colored by wind speed # Set title sounding_name = ds_sel.sounding.values sounding_name_str = str(sounding_name.astype("str")) skew.ax.set_title("{sounding}_{direction}".format( sounding=sounding_name_str, direction=direction)) if output is None: filename_fmt = "{platform}_SoundingProfile_skew_{launch_time}_{res}.png".format( platform=platform, res=resolution, launch_time=launch_time) # filename_fmt = launch_time.strftime(filename_fmt) output = filename_fmt else: output = output.format(platform=platform, direction=direction, resolution=resolution) # output = launch_time.strftime(output) logging.info("Write output to {}".format(output)) plt.savefig(output)
skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) # Calculate LCL height and plot as black dot. Because `p`'s first value is # ~1000 mb and its last value is ~250 mb, the `0` index is selected for # `p`, `T`, and `Td` to lift the parcel from the surface. If `p` was inverted, # i.e. start from low value, 250 mb, to a high value, 1000 mb, the `-1` index # should be selected. lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Calculate full parcel profile and add to plot as black line prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') skew.plot(p, prof, 'k', linewidth=2) # Shade areas of CAPE and CIN skew.shade_cin(p, T, prof, Td) skew.shade_cape(p, T, prof) # An example of a slanted line at constant T -- in this case the 0 # isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() # Show the plot plt.show()
def plot_skewt(self): """ :param adjusted_data: receives the post processed dataframe :param valid: :return: """ for area in self.airports: for airport in self.airports[area]: lon = self.airports[area][airport]['lon'] lat = self.airports[area][airport]['lat'] pressure_levels = self.levels * units.hPa tair = list( self.create_profile_variable(self.tair, lat, lon).values()) * units.degC dewp = list( self.create_profile_variable(self.dewp, lat, lon).values()) * units.degC u_wnd = list(self.create_profile_variable(self.u_wnd, lat, lon).values()) * \ units('meters / second').to('knots') v_wnd = list(self.create_profile_variable(self.v_wnd, lat, lon).values()) * \ units('meters / second').to('knots') # Create a new figure. The dimensions here give a good aspect ratio. fig = plt.figure(figsize=(12, 9)) skew = SkewT(fig, rotation=45) # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.plot(pressure_levels, tair, 'r') skew.plot(pressure_levels, dewp, 'g') skew.plot_barbs(pressure_levels, u_wnd, v_wnd) skew.ax.set_ylim(1020, 100) skew.ax.set_xlim(-40, 60) # Calculate LCL height and plot as black dot lcl_pressure, lcl_temperature = mpcalc.lcl( pressure_levels[0], tair[0], dewp[0]) skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='black') # Calculate full parcel profile and add to plot as black line prof = mpcalc.parcel_profile(pressure_levels, tair[0], dewp[0]) skew.plot(pressure_levels, prof, 'k', linewidth=2) # An example of a slanted line at constant T -- in this case the 0 # isotherm skew.ax.axvline(0, color='c', linestyle='--', linewidth=2) # Add the relevant special lines skew.plot_dry_adiabats() skew.plot_moist_adiabats() skew.plot_mixing_lines() skew.shade_cape(pressure_levels, tair, prof) skew.shade_cin(pressure_levels, tair, prof) plt.title( f'Perfil vertical (GFS) de {airport} valido para {self.time_stamp}', fontsize=16, ha='center') sounding_output_path = f'{self.output_path}/{area}/{airport}' Path(sounding_output_path).mkdir(parents=True, exist_ok=True) plt.savefig( f'{sounding_output_path}/sounding_{airport}_{self.time_step:02d}.jpg' ) return skew
def fmi2skewt(station, time, img_name): apikey = 'e72a2917-1e71-4d6f-8f29-ff4abfb8f290' url = 'http://data.fmi.fi/fmi-apikey/' + str( apikey ) + '/wfs?request=getFeature&storedquery_id=fmi::observations::weather::sounding::multipointcoverage&fmisid=' + str( station) + '&starttime=' + str(time) + '&endtime=' + str(time) + '&' req = requests.get(url) xmlstring = req.content tree = ET.ElementTree(ET.fromstring(xmlstring)) root = tree.getroot() #reading location and time data to "positions" from XML positions = "" for elem in root.getiterator( tag='{http://www.opengis.net/gmlcov/1.0}positions'): positions = elem.text #'positions' is string type variable #--> split positions into a list by " " #then remove empty chars and "\n" # from pos_split --> data into positions_data try: pos_split = positions.split(' ') except NameError: return "Sounding data not found: stationid " + station + " time " + time pos_split = positions.split(' ') positions_data = [] for i in range(0, len(pos_split)): if not (pos_split[i] == "" or pos_split[i] == "\n"): positions_data.append(pos_split[i]) #index for height: 2,6,10 etc in positions_data height = [] myList = range(2, len(positions_data)) for i in myList[::4]: height.append(positions_data[i]) p = [] for i in range(0, len(height)): p.append(height2pressure(float(height[i]))) #reading wind speed, wind direction, air temperature and dew point data to 'values' values = "" for elem in root.getiterator( tag='{http://www.opengis.net/gml/3.2}doubleOrNilReasonTupleList'): values = elem.text #split 'values' into a list by " " #then remove empty chars and "\n" val_split = values.split(' ') values_data = [] for i in range(0, len(val_split)): if not (val_split[i] == "" or val_split[i] == "\n"): values_data.append(val_split[i]) #data in values_data: w_speed, w_dir, t_air, t_dew wind_speed = [] wind_dir = [] T = [] Td = [] myList = range(0, len(values_data)) for i in myList[::4]: wind_speed.append(float(values_data[i])) wind_dir.append(float(values_data[i + 1])) T.append(float(values_data[i + 2])) Td.append(float(values_data[i + 3])) if stationid == "101104": loc_time = "Jokioinen Ilmala " + time1 elif stationid == "101932": loc_time = "Sodankyla Tahtela " + time1 else: return None #calculate wind components u,v: u = [] v = [] for i in range(0, len(wind_speed)): u1, v1 = getWindComponent(wind_speed[i], wind_dir[i]) u.append(u1) v.append(v1) #find index for pressure < 100hPa (for number of wind bars) if min(p) > 100: wthin = len(p) / 20 u_plot = u v_plot = v p_plot = p else: for i in range(0, len(p)): if p[i] - 100 <= 0: wthin = i / 20 u_plot = u[0:i] v_plot = v[0:i] p_plot = p[0:i] break #units wind_speed = wind_speed * units("m/s") wind_dir = wind_dir * units.deg T = T * units.degC Td = Td * units.degC p = p * units("hPa") #calculate pwat, lcl, cape, cin and plot cape pwat = mpcalc.precipitable_water(Td, p, bottom=None, top=None) lcl_pressure, lcl_temperature = mpcalc.lcl(p[0], T[0], Td[0]) prof = mpcalc.parcel_profile(p, T[0], Td[0]).to('degC') try: cape, cin = mpcalc.cape_cin(p, T, Td, prof) except IndexError: cape = 0 * units("J/kg") cin = 0 * units("J/kg") #__________________plotting__________________ fig = plt.figure(figsize=(9, 9)) skew = SkewT(fig, rotation=45) font_par = { 'family': 'monospace', 'color': 'darkred', 'weight': 'normal', 'size': 10, } font_title = { 'family': 'monospace', 'color': 'black', 'weight': 'normal', 'size': 20, } font_axis = { 'family': 'monospace', 'color': 'black', 'weight': 'normal', 'size': 10, } # Plot the data using normal plotting functions, in this case using # log scaling in Y, as dictated by the typical meteorological plot skew.plot(p, T, 'k') skew.plot(p, Td, 'b') skew.ax.set_ylim(1000, 100) skew.ax.set_xlim(-40, 60) skew.plot_barbs(p_plot[0::wthin], u_plot[0::wthin], v_plot[0::wthin]) skew.plot_dry_adiabats(alpha=0.4) skew.plot_moist_adiabats(alpha=0.4) skew.plot_mixing_lines(alpha=0.4) skew.shade_cape(p, T, prof, color="orangered") plt.title(loc_time, fontdict=font_title) plt.xlabel("T (C)", fontdict=font_axis) plt.ylabel("P (hPa)", fontdict=font_axis) #round and remove units from cape,cin,plcl,tlcl,pwat if cape.magnitude > 0: capestr = str(np.round(cape.magnitude)) else: capestr = "NaN" if cin.magnitude > 0: cinstr = str(np.round(cin.magnitude)) else: cinstr = "NaN" lclpstr = str(np.round(lcl_pressure.magnitude)) lclTstr = str(np.round(lcl_temperature.magnitude)) pwatstr = str(np.round(pwat.magnitude)) str_par = "CAPE[J/kg]=" + capestr + " CIN[J/kg]=" + cinstr + " Plcl[hPa]=" + lclpstr + " Tlcl[C]=" + lclTstr + " pwat[mm]=" + pwatstr font = { 'family': 'monospace', 'color': 'darkred', 'weight': 'normal', 'size': 10, } plt.text(-20, 1250, str_par, fontdict=font_par) save_file = "figures/" + img_name + ".png" plt.savefig(save_file)
def plot_soundings(fig, ax, temp, rh, sfc_pressure, centerlat, centerlon, domainsize, model, cape=False, wetbulb=False): """ This function will plot a bunch of little soundings onto a matplotlib fig,ax. temp is an xarray dataarray with temperature data on pressure levels at least between 1000 and 300mb (you can change the ylimits for other datasets) rh is an xarray dataarray with temperature data on pressure levels at least between 1000 and 300mb (you can change ) sfc_pressure is an xarray dataarray with surface pressure data (NOT MSLP!) centerlat and centerlon are the coordinates around which you want your map to be centered. both are floats or integers and are in degrees of latitude and degrees of longitude west (i.e. 70W would be input as positive 70 here) domainsize is a string either 'local' for ~WFO-size domains or 'regional' for NE/SE/Mid-Atlantic-size domains (12 deg lat by 15 deg lon). More will be added soon. model is a string that specifies which model is providing data for the plots. This determines a few things, most importantly longitude selections. Models currently supported are 'GFS','NAM',and 'RAP' cape is a boolean to indicate whether you want to overlay parcel paths and shade CAPE/CIN on soundings with >100 J/kg of CAPE (this value can be changed) wetbulb is a boolean to indicate whether you want to draw wet bulb profiles note that this function doesn't "return" anything but if you just call it and provide the right arguments, it works. for example: import soundingmaps as smap ... smap.plot_soundings(fig,ax1,data['temperature'],data['rh'],30.5,87.5,'local',cape=True) """ r = 5 if domainsize == "local": init_lat_delt = 1.625 init_lon_delt = 0.45 lat_delts = [0.2, 0.7, 1.2, 1.75, 2.25, 2.8] londelt = 0.76 startlon = centerlon - 2 + 0.45 elif domainsize == "regional": init_lat_delt = 6 init_lon_delt = 1.6 lat_delts = [0.6, 2.5, 4.5, 6.4, 8.4, 10.25] londelt = 2.9 startlon = centerlon - 7.5 + 1.6 # Lon adjustment for GFS because it's [0,360] not [-180,180] if model == 'GFS': startlon = 360 - startlon # set lat/lon grid from which to pull data to plot soundings startlat = centerlat - init_lat_delt sound_lats = [] sound_lons = [] for i in range(0, 6): lats = startlat + lat_delts[i] sound_lats.append(lats) for i in range(0, r): if model == 'GFS': lons = startlon - (londelt * i) else: lons = -startlon - (londelt * i) sound_lons.append(lons) # this sets how high each row of soundings is on the plot plot_elevs = [0.2, 0.3, 0.4, 0.5, 0.6, 0.7] # whole bunch of legend stuff dashed_red_line = lines.Line2D([], [], linestyle='solid', color='r', label='Temperature') dashed_purple_line = lines.Line2D([], [], linestyle='dashed', color='purple', label='0C Isotherm') dashed_green_line = lines.Line2D([], [], linestyle='solid', color='g', label='Dew Point') grey_line = lines.Line2D([], [], color='darkgray', label='MSLP (hPa)') blue_line = lines.Line2D([], [], color='b', label='Wet Bulb') pink_line = lines.Line2D([], [], color='fuchsia', label='Surface-Based Parcel Path') teal_line = lines.Line2D([], [], linestyle='dashed', color='teal', label='HGZ') green_dot = lines.Line2D([], [], marker='o', color='forestgreen', label='LCL') black_dot = lines.Line2D([], [], marker='o', color='k', label='Sounding Origin') red = mpatches.Patch(color='tab:red', label='CAPE') blue = mpatches.Patch(color='tab:blue', label='CIN') # do the plotting based on user inputs if cape and wetbulb is True: print('CAPE + Wetbulb') for i, plot_elev in enumerate(plot_elevs): soundlat = sound_lats[i] if k < 2: s = 1 else: s = 0 for i in range(s, r): levs_abv_ground = [] soundlon = sound_lons[i] sound_temps = temp.interp(lat=soundlat, lon=soundlon) - 273.15 sound_rh = rh.interp(lat=soundlat, lon=soundlon) sound_pres = temp.lev spres = sfc_pressure.interp(lat=soundlat, lon=soundlon) sound_dp = mpcalc.dewpoint_from_relative_humidity( sound_temps.data * units.degC, sound_rh.data * units.percent) sound_wb = mpcalc.wet_bulb_temperature( sound_pres, sound_temps.data * units.degC, sound_dp) #Only want data above the ground abv_sfc_temp = spt.mask_below_terrain(spres, sound_temps, sound_pres)[0] abv_sfc_dewp = spt.mask_below_terrain(spres, sound_dp, sound_pres)[0] abv_sfc_wetb = spt.mask_below_terrain(spres, sound_wb, sound_pres)[0] pres_abv_ground = spt.mask_below_terrain( spres, sound_temps, sound_pres)[1] #sound_wb = sound_wb*units.degC skew = SkewT(fig=fig, rect=(0.75 - (0.15 * i), plot_elev, .15, .1)) parcel_prof = mpcalc.parcel_profile( pres_abv_ground, abv_sfc_temp[0].data * units.degC, abv_sfc_dewp[0]) cape = mpcalc.cape_cin(pres_abv_ground, abv_sfc_temp.data * units.degC, abv_sfc_dewp, parcel_prof) capeout = int(cape[0].m) cinout = int(cape[1].m) #skew.ax.axvspan(-30, -10, color='cyan', alpha=0.4) skew.plot(pres_abv_ground, abv_sfc_wetb, 'b', linewidth=2) skew.plot(pres_abv_ground, abv_sfc_dewp, 'g', linewidth=3) skew.plot(pres_abv_ground, abv_sfc_temp, 'r', linewidth=3) if capeout > 100: # Shade areas of CAPE and CIN print(pres_abv_ground) print(abv_sfc_temp.data * units.degC) print(parcel_prof) skew.shade_cin(pres_abv_ground, abv_sfc_temp.data * units.degC, parcel_prof) skew.shade_cape(pres_abv_ground, abv_sfc_temp.data * units.degC, parcel_prof) skew.plot(pres_abv_ground, parcel_prof, color='fuchsia', linewidth=1) lcl_pressure, lcl_temperature = mpcalc.lcl( pres_abv_ground[0], abv_sfc_temp.data[0] * units.degC, abv_sfc_dewp[0]) skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='forestgreen') skew.ax.axvline(-30, color='teal', linestyle='--', linewidth=1) skew.ax.axvline(-10, color='teal', linestyle='--', linewidth=1) skew.plot(975, 0, 'ko', markerfacecolor='k') skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3) skew.ax.set_ylim((1000, 300)) skew.ax.axis('off') leg = ax.legend(handles=[ dashed_red_line, dashed_green_line, blue_line, dashed_purple_line, teal_line, green_dot, pink_line, red, blue, black_dot ], title='Sounding Legend', loc=4, framealpha=1) elif cape == True and wetbulb == False: print('CAPE no wetbulb') for k in range(len(plot_elevs)): soundlat = sound_lats[k] plot_elev = plot_elevs[k] if k == 0: s = 1 else: s = 0 for i in range(s, r): levs_abv_ground = [] soundlon = sound_lons[i] sound_temps = temp.interp(lat=soundlat, lon=soundlon) - 273.15 sound_rh = rh.interp(lat=soundlat, lon=soundlon) sound_pres = temp.lev spres = sfc_pressure.interp(lat=soundlat, lon=soundlon) sound_dp = mpcalc.dewpoint_from_relative_humidity( sound_temps.data * units.degC, sound_rh.data * units.percent) abv_sfc_temp = spt.mask_below_terrain(spres, sound_temps, sound_pres)[0] abv_sfc_dewp = spt.mask_below_terrain(spres, sound_dp, sound_pres)[0] pres_abv_ground = spt.mask_below_terrain( spres, sound_temps, sound_pres)[1] skew = SkewT(fig=fig, rect=(0.75 - (0.15 * i), plot_elev, .15, .1)) parcel_prof = mpcalc.parcel_profile( pres_abv_ground, abv_sfc_temp[0].data * units.degC, abv_sfc_dewp[0]) cape = mpcalc.cape_cin(pres_abv_ground, abv_sfc_temp.data * units.degC, abv_sfc_dewp, parcel_prof) capeout = int(cape[0].m) cinout = int(cape[1].m) skew.plot(pres_abv_ground, abv_sfc_dewp, 'g', linewidth=3) skew.plot(pres_abv_ground, abv_sfc_temp, 'r', linewidth=3) if capeout > 100: # Shade areas of CAPE and CIN skew.shade_cin(pres_abv_ground, abv_sfc_temp.data * units.degC, parcel_prof) skew.shade_cape(pres_abv_ground, abv_sfc_temp.data * units.degC, parcel_prof) skew.plot(pres_abv_ground, parcel_prof, color='fuchsia', linewidth=1) print(abv_sfc_temp) lcl_pressure, lcl_temperature = mpcalc.lcl( pres_abv_ground[0], abv_sfc_temp.data[0] * units.degC, abv_sfc_dewp[0]) skew.plot(lcl_pressure, lcl_temperature, 'ko', markerfacecolor='forestgreen') skew.ax.axvline(-30, color='teal', linestyle='--', linewidth=1) skew.ax.axvline(-10, color='teal', linestyle='--', linewidth=1) skew.plot(975, 0, 'ko', markerfacecolor='k') skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3) skew.ax.set_ylim((1000, 300)) skew.ax.axis('off') leg = ax.legend(handles=[ dashed_red_line, dashed_green_line, dashed_purple_line, teal_line, green_dot, pink_line, red, blue, black_dot ], title='Sounding Legend', loc=4, framealpha=1) elif wetbulb == True and cape == False: print('Wetbulb no CAPE') for k in range(len(plot_elevs)): soundlat = sound_lats[k] plot_elev = plot_elevs[k] if k == 0: s = 1 else: s = 0 for i in range(s, r): levs_abv_ground = [] soundlon = sound_lons[i] sound_temps = temp.interp(lat=soundlat, lon=soundlon) - 273.15 sound_rh = rh.interp(lat=soundlat, lon=soundlon) sound_pres = temp.lev spres = sfc_pressure.interp(lat=soundlat, lon=soundlon) sound_dp = mpcalc.dewpoint_from_relative_humidity( sound_temps.data * units.degC, sound_rh.data * units.percent) sound_wb = mpcalc.wet_bulb_temperature( sound_pres, sound_temps.data * units.degC, sound_dp) abv_sfc_temp = spt.mask_below_terrain(spres, sound_temps, sound_pres)[0] abv_sfc_dewp = spt.mask_below_terrain(spres, sound_dp, sound_pres)[0] abv_sfc_wetb = spt.mask_below_terrain(spres, sound_wb, sound_pres)[0] pres_abv_ground = spt.mask_below_terrain( spres, sound_temps, sound_pres)[1] #sound_wb = sound_wb*units.degC skew = SkewT(fig=fig, rect=(0.75 - (0.15 * i), plot_elev, .15, .1)) skew.plot(pres_abv_ground, abv_sfc_wetb, 'b', linewidth=2) skew.plot(pres_abv_ground, abv_sfc_dewp, 'g', linewidth=3) skew.plot(pres_abv_ground, abv_sfc_temp, 'r', linewidth=3) skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3) skew.ax.set_ylim((1000, 300)) skew.ax.axis('off') else: print('No Wetbulb or CAPE') for k in range(len(plot_elevs)): soundlat = sound_lats[k] plot_elev = plot_elevs[k] if k == 0: s = 1 else: s = 0 for i in range(s, r): sound_pres = temp.lev sound_temps = temp.interp(lat=soundlat, lon=soundlon) - 273.15 sound_rh = rh.interp(lat=soundlat, lon=soundlon) sound_dp = mpcalc.dewpoint_from_relative_humidity( sound_temps.data * units.degC, sound_rh.data * units.percent) skew = SkewT(fig=fig, rect=(0.75 - (0.15 * i), plot_elev, .15, .1)) skew.plot(sound_pres, sound_dp, 'g', linewidth=3) skew.plot(sound_pres, sound_temps, 'r', linewidth=3) skew.plot(1000, 0, 'ko', markerfacecolor='k') skew.ax.axvline(0, color='purple', linestyle='--', linewidth=3) skew.ax.set_ylim((1000, 300)) skew.ax.axis('off') leg = ax.legend(handles=[ dashed_red_line, dashed_green_line, blue_line, dashed_purple_line, black_dot ], title='Sounding Legend', loc=4, framealpha=1)