def curvelinear_test1(fig): """ grid for custom transform. """ def tr(x, y): x, y = np.asarray(x), np.asarray(y) return x, y - x def inv_tr(x, y): x, y = np.asarray(x), np.asarray(y) return x, y + x grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax1 = Subplot(fig, 1, 2, 1, grid_helper=grid_helper) # ax1 will have a ticks and gridlines defined by the given # transform (+ transData of the Axes). Note that the transform of # the Axes itself (i.e., transData) is not affected by the given # transform. fig.add_subplot(ax1) xx, yy = tr([3, 6], [5.0, 10.]) ax1.plot(xx, yy, linewidth=2.0) ax1.set_aspect(1.) ax1.set_xlim(0, 10.) ax1.set_ylim(0, 10.) ax1.axis["t"] = ax1.new_floating_axis(0, 3.) ax1.axis["t2"] = ax1.new_floating_axis(1, 7.) ax1.grid(True, zorder=0)
def __spacetime_diagram_o_frame(self): # from (x',t') to (x,t) def tr(x_prime, t_prime): x_prime, t_prime = np.asarray(x_prime), np.asarray(t_prime) return self.lorentz_transformations.transform( x_prime, t_prime, -self.velocity) # form (x,t) to (x',t') def inv_tr(x, t): x, t = np.asarray(x), np.asarray(t) return self.lorentz_transformations.transform(x, t, self.velocity) grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax = Subplot(self.fig, 1, 2, 1, grid_helper=grid_helper) if self.showOPrime \ else Subplot(self.fig, 1, 1, 1, grid_helper=grid_helper) self.fig.add_subplot(ax) ax.set_xlabel("x", loc="center") ax.set_ylabel("t", loc="center") # O' x axis ax.axis["x1"] = x1 = ax.new_floating_axis(1, 0) x1.label.set_text("x'") # O' t axis ax.axis["t1"] = t1 = ax.new_floating_axis(0, 0) t1.label.set_text("t'") self.__add_x_and_y_axis(ax) ax.format_coord = self.__format_coord_o_frame self.__remove_ticks(ax, x1, t1) self.world_lines_plotter.plot(plt, ax, self.velocity)
def plotCorrelation(tauArray,kappaMatrix,kappaLower=None,kappaUpper=None,CI=None,amplify=1): """Plots Pearson Correlation Coefficient K(t,tau) with rotated axis to indicate absolute t, and relative time shift tau, between two signals x(t),y(t). Specified matrix has to be square with values -1 < p < +1 with corresponding time array giving the absolute time, t of the centers of each correlated window.""" # defining tranformation for relative time shifts def R(x, y): x, y = asarray(x), asarray(y) #return x,y return (2*x - y)/2, (y + 2*x)/2 def Rt(x, y): x, y = asarray(x), asarray(y) #return x,y return x + y, x - y # create figure with rotated axes fig = figure(figsize=(10, 10),frameon=False) grid_locator = angle_helper.LocatorDMS(20) grid_helper = GridHelperCurveLinear((R, Rt), grid_locator1=grid_locator, grid_locator2=grid_locator) ax = Subplot(fig, 1, 1, 1, grid_helper=grid_helper) fig.add_subplot(ax);ax.axis('off'); # copying over matrix K = array(kappaMatrix) # zero out correlations if confidence intervals overlap zero if all(kappaLower != None) and all(kappaUpper != None) : K[ (kappaLower<0) * (0<kappaUpper) ] = 0 # zero out statistically insignificant correlations if all(CI != None) : K[ abs(kappaMatrix) < CI ] = 0 # display pearson correlation matrix with +ive in red and -ive in blue ax.imshow(K,cmap="RdBu_r",interpolation="none",origin="bottom", extent = (tauArray[0],tauArray[-1],tauArray[0],tauArray[-1]),vmin=-1.0/amplify,vmax=1.0/amplify) # display rotated axes time,t and time delay,tau ax.axis["tau"] = tau = ax.new_floating_axis(0,0) ax.axis["t"] = t = ax.new_floating_axis(1,0) # setting axes options ax.set_xlim(tauArray[0],tauArray[-1]) ax.set_ylim(tauArray[0],tauArray[-1]) ax.grid(which="both") ax.set_aspect(1) return fig
def curvelinear_test1(fig): """ grid for custom transform. """ def tr(x, y): x, y = np.asarray(x), np.asarray(y) return x, y-x def inv_tr(x,y): x, y = np.asarray(x), np.asarray(y) return x, y+x grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax1 = Subplot(fig, 1, 2, 1, grid_helper=grid_helper) fig.add_subplot(ax1) xx, yy = tr([3, 6], [5.0, 10.]) ax1.plot(xx, yy) ax1.set_aspect(1.) ax1.set_xlim(0, 10.) ax1.set_ylim(0, 10.) ax1.axis["t"]=ax1.new_floating_axis(0, 3.) ax1.axis["t2"]=ax1.new_floating_axis(1, 7.) ax1.grid(True)
def curvelinear_test1(fig): """ Grid for custom transform. """ def tr(x, y): x, y = numpy.asarray(x), numpy.asarray(y) return x, y - (2 * x) # return x + (5 * y), (7 * y) + (3 * x) def inv_tr(x, y): x, y = numpy.asarray(x), numpy.asarray(y) return x, y + (2 * x) grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax1 = Subplot(fig, 1, 1, 1, grid_helper=grid_helper) # ax1 will have a ticks and gridlines defined by the given # transform (+ transData of the Axes). Note that the transform of # the Axes itself (i.e., transData) is not affected by the given # transform. fig.add_subplot(ax1) xx, yy = tr([0, 1], [0, 2]) ax1.plot(xx, yy, linewidth=2.0) ax1.set_aspect(1) ax1.set_xlim(-3, 3) ax1.set_ylim(-3, 3) ax1.axis["t"] = ax1.new_floating_axis( 0, 0 ) # first argument appears to be slope, second argument appears to be starting point on vertical ax1.axis["t2"] = ax1.new_floating_axis(1, 0) ax1.axhline(y=0, color='r') ax1.axvline(x=0, color='r') ax1.grid(True, zorder=0)
from mpl_toolkits.axisartist import Subplot from mpl_toolkits.axisartist.grid_helper_curvelinear import \ GridHelperCurveLinear def tr(x, y): # source (data) to target (rectilinear plot) coordinates x, y = numpy.asarray(x), numpy.asarray(y) return x + 0.2 * y, y - x def inv_tr(x, y): x, y = numpy.asarray(x), numpy.asarray(y) return x - 0.2 * y, y + x grid_helper = GridHelperCurveLinear((tr, inv_tr)) ax6 = Subplot(fig, nrow, ncol, 6, grid_helper=grid_helper) fig.add_subplot(ax6) ax6.set_title('non-ortho axes') xx, yy = tr([3, 6], [5.0, 10.]) ax6.plot(xx, yy) ax6.set_aspect(1.) ax6.set_xlim(0, 10.) ax6.set_ylim(0, 10.) ax6.axis["t"] = ax6.new_floating_axis(0, 3.) ax6.axis["t2"] = ax6.new_floating_axis(1, 7.) ax6.grid(True) plt.show()
class Tephigram: """ Generate a tephigram of one or more pressure and temperature data sets. """ def __init__( self, figure=None, isotherm_locator=None, dry_adiabat_locator=None, anchor=None, ): """ Initialise the tephigram transformation and plot axes. Kwargs: * figure: An existing :class:`matplotlib.figure.Figure` instance for the tephigram plot. If a figure is not provided, a new figure will be created by default. * isotherm_locator: A :class:`tephi.Locator` instance or a numeric step size for the isotherm lines. * dry_adiabat_locator: A :class:`tephi.Locator` instance or a numeric step size for the dry adiabat lines. * anchor: A sequence of two pressure, temperature pairs specifying the extent of the tephigram plot in terms of the bottom left hand corner and the top right hand corner. Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. For example: .. plot:: :include-source: import matplotlib.pyplot as plt from numpy import column_stack import os.path import tephi from tephi import Tephigram dew_point = os.path.join(tephi.DATA_DIR, 'dews.txt') dry_bulb = os.path.join(tephi.DATA_DIR, 'temps.txt') dew_data, temp_data = tephi.loadtxt(dew_point, dry_bulb) dews = column_stack((dew_data.pressure, dew_data.temperature)) temps = column_stack((temp_data.pressure, temp_data.temperature)) tpg = Tephigram() tpg.plot(dews, label='Dew-point', color='blue', linewidth=2) tpg.plot(temps, label='Dry-bulb', color='red', linewidth=2) plt.show() """ if not figure: # Create a default figure. self.figure = plt.figure(0, figsize=(9, 9)) else: self.figure = figure # Configure the locators. if isotherm_locator and not isinstance(isotherm_locator, Locator): if not isinstance(isotherm_locator, numbers.Number): raise ValueError("Invalid isotherm locator") locator_isotherm = Locator(isotherm_locator) else: locator_isotherm = isotherm_locator if dry_adiabat_locator and not isinstance(dry_adiabat_locator, Locator): if not isinstance(dry_adiabat_locator, numbers.Number): raise ValueError("Invalid dry adiabat locator") locator_theta = Locator(dry_adiabat_locator) else: locator_theta = dry_adiabat_locator # Define the tephigram coordinate-system transformation. self.tephi_transform = transforms.TephiTransform() ghelper = GridHelperCurveLinear( self.tephi_transform, tick_formatter1=_FormatterIsotherm(), grid_locator1=locator_isotherm, tick_formatter2=_FormatterTheta(), grid_locator2=locator_theta, ) self.axes = Subplot(self.figure, 1, 1, 1, grid_helper=ghelper) self.transform = self.tephi_transform + self.axes.transData self.axes.axis["isotherm"] = self.axes.new_floating_axis(1, 0) self.axes.axis["theta"] = self.axes.new_floating_axis(0, 0) self.axes.axis["left"].get_helper().nth_coord_ticks = 0 self.axes.axis["left"].toggle(all=True) self.axes.axis["bottom"].get_helper().nth_coord_ticks = 1 self.axes.axis["bottom"].toggle(all=True) self.axes.axis["top"].get_helper().nth_coord_ticks = 0 self.axes.axis["top"].toggle(all=False) self.axes.axis["right"].get_helper().nth_coord_ticks = 1 self.axes.axis["right"].toggle(all=True) self.axes.gridlines.set_linestyle("solid") self.figure.add_subplot(self.axes) # Configure default axes. axis = self.axes.axis["left"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("baseline") axis.major_ticklabels.set_rotation(135) axis = self.axes.axis["right"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("baseline") axis.major_ticklabels.set_rotation(-135) self.axes.axis["top"].major_ticklabels.set_fontsize(10) axis = self.axes.axis["bottom"] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_ha("left") axis.major_ticklabels.set_va("top") axis.major_ticklabels.set_rotation(-45) # Isotherms: lines of constant temperature (degC). axis = self.axes.axis["isotherm"] axis.set_axis_direction("right") axis.set_axislabel_direction("-") axis.major_ticklabels.set_rotation(90) axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("bottom") axis.major_ticklabels.set_color("grey") axis.major_ticklabels.set_visible(False) # turned-off # Dry adiabats: lines of constant potential temperature (degC). axis = self.axes.axis["theta"] axis.set_axis_direction("right") axis.set_axislabel_direction("+") axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va("bottom") axis.major_ticklabels.set_color("grey") axis.major_ticklabels.set_visible(False) # turned-off axis.line.set_linewidth(3) axis.line.set_linestyle("--") # Lock down the aspect ratio. self.axes.set_aspect(1.0) self.axes.grid(True) # Initialise the text formatter for the navigation status bar. self.axes.format_coord = self._status_bar # Factor in the tephigram transform. ISOBAR_TEXT["transform"] = self.transform WET_ADIABAT_TEXT["transform"] = self.transform MIXING_RATIO_TEXT["transform"] = self.transform # Create plot collections for the tephigram isopleths. func = partial( isopleths.isobar, MIN_THETA, MAX_THETA, self.axes, self.transform, ISOBAR_LINE, ) self._isobars = _PlotCollection( self.axes, ISOBAR_SPEC, MAX_PRESSURE, func, ISOBAR_TEXT, fixed=ISOBAR_FIXED, minimum=MIN_PRESSURE, ) func = partial( isopleths.wet_adiabat, MAX_PRESSURE, MIN_TEMPERATURE, self.axes, self.transform, WET_ADIABAT_LINE, ) self._wet_adiabats = _PlotCollection( self.axes, WET_ADIABAT_SPEC, MAX_WET_ADIABAT, func, WET_ADIABAT_TEXT, fixed=WET_ADIABAT_FIXED, minimum=MIN_WET_ADIABAT, xfocus=True, ) func = partial( isopleths.mixing_ratio, MIN_PRESSURE, MAX_PRESSURE, self.axes, self.transform, MIXING_RATIO_LINE, ) self._mixing_ratios = _PlotCollection( self.axes, MIXING_RATIO_SPEC, MIXING_RATIOS, func, MIXING_RATIO_TEXT, fixed=MIXING_RATIO_FIXED, ) # Initialise for the tephigram plot event handler. plt.connect("motion_notify_event", _handler) self.axes.tephigram = True self.axes.tephigram_original_delta_xlim = DEFAULT_WIDTH self.original_delta_xlim = DEFAULT_WIDTH self.axes.tephigram_transform = self.tephi_transform self.axes.tephigram_inverse = self.tephi_transform.inverted() self.axes.tephigram_isopleths = [ self._isobars, self._wet_adiabats, self._mixing_ratios, ] # The tephigram profiles. self._profiles = [] self.axes.tephigram_profiles = self._profiles # Center the plot around the anchor extent. self._anchor = anchor if self._anchor is not None: self._anchor = np.asarray(anchor) if (self._anchor.ndim != 2 or self._anchor.shape[-1] != 2 or len(self._anchor) != 2): msg = ("Invalid anchor, expecting [(bottom-left-pressure, " "bottom-left-temperature), (top-right-pressure, " "top-right-temperature)]") raise ValueError(msg) ( (bottom_pressure, bottom_temp), (top_pressure, top_temp), ) = self._anchor if (bottom_pressure - top_pressure) < 0: raise ValueError("Invalid anchor pressure range") if (bottom_temp - top_temp) < 0: raise ValueError("Invalid anchor temperature range") self._anchor = isopleths.Profile(anchor, self.axes) self._anchor.plot(visible=False) xlim, ylim = self._calculate_extents() self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) def plot(self, data, **kwargs): """ Plot the environmental lapse rate profile of the pressure and temperature data points. The pressure and temperature data points are transformed into potential temperature and temperature data points before plotting. By default, the tephigram will automatically center the plot around all profiles. .. warning:: Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. Args: * data: pressure and temperature pair data points. .. note:: All keyword arguments are passed through to :func:`matplotlib.pyplot.plot`. For example: .. plot:: :include-source: import matplotlib.pyplot as plt from tephi import Tephigram tpg = Tephigram() data = [[1006, 26.4], [924, 20.3], [900, 19.8], [850, 14.5], [800, 12.9], [755, 8.3]] profile = tpg.plot(data, color='red', linestyle='--', linewidth=2, marker='o') barbs = [(10, 45, 900), (20, 60, 850), (25, 90, 800)] profile.barbs(barbs) plt.show() For associating wind barbs with an environmental lapse rate profile, see :meth:`~tephi.isopleths.Profile.barbs`. """ profile = isopleths.Profile(data, self.axes) profile.plot(**kwargs) self._profiles.append(profile) # Center the tephigram plot around all the profiles. if self._anchor is None: xlim, ylim = self._calculate_extents(xfactor=0.25, yfactor=0.05) self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) # Refresh the tephigram plot isopleths. _refresh_isopleths(self.axes) # Show the plot legend. if "label" in kwargs: font_properties = FontProperties(size="x-small") plt.legend( loc="upper left", fancybox=True, shadow=True, prop=font_properties, ) return profile def _status_bar(self, x_point, y_point): """Generate text for the interactive backend navigation status bar.""" temperature, theta = transforms.convert_xy2Tt(x_point, y_point) pressure, _ = transforms.convert_Tt2pT(temperature, theta) xlim = self.axes.get_xlim() zoom = (xlim[1] - xlim[0]) / self.original_delta_xlim msg = "T:{:.2f}, theta:{:.2f}, phi:{:.2f} (zoom:{:.3f})" text = msg.format(float(temperature), float(theta), float(pressure), zoom) return text def _calculate_extents(self, xfactor=None, yfactor=None): min_x = min_y = 1e10 max_x = max_y = -1e-10 profiles = self._profiles transform = self.tephi_transform.transform if self._anchor is not None: profiles = [self._anchor] for profile in profiles: temperature = profile.temperature.reshape(-1, 1) theta = profile.theta.reshape(-1, 1) xy_points = transform(np.concatenate((temperature, theta), axis=1)) x_points = xy_points[:, 0] y_points = xy_points[:, 1] min_x = np.min([min_x, np.min(x_points)]) min_y = np.min([min_y, np.min(y_points)]) max_x = np.max([max_x, np.max(x_points)]) max_y = np.max([max_y, np.max(y_points)]) if xfactor is not None: delta_x = max_x - min_x min_x, max_x = min_x - xfactor * delta_x, max_x + xfactor * delta_x if yfactor is not None: delta_y = max_y - min_y min_y, max_y = min_y - yfactor * delta_y, max_y + yfactor * delta_y return ([min_x, max_x], [min_y, max_y])
class Tephigram(object): """ Generate a tephigram of one or more pressure and temperature data sets. """ def __init__(self, figure=None, isotherm_locator=None, dry_adiabat_locator=None, anchor=None): """ Initialise the tephigram transformation and plot axes. Kwargs: * figure: An existing :class:`matplotlib.figure.Figure` instance for the tephigram plot. If a figure is not provided, a new figure will be created by default. * isotherm_locator: A :class:`edson.Locator` instance or a numeric step size for the isotherm lines. * dry_adiabat_locator: A :class:`edson.Locator` instance or a numeric step size for the dry adiabat lines. * anchor: A sequence of two pressure, temperature pairs specifying the extent of the tephigram plot in terms of the bottom left hand corner and the top right hand corner. Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. For example: .. plot:: :include-source: import matplotlib.pyplot as plt import os.path import edson from edson import Tephigram dew_point = os.path.join(edson.RESOURCES_DIR, 'tephigram', 'dews.txt') dry_bulb = os.path.join(edson.RESOURCES_DIR, 'tephigram', 'temps.txt') dew_data, temp_data = edson.loadtxt(dew_point, dry_bulb) dews = zip(dew_data.pressure, dew_data.temperature) temps = zip(temp_data.pressure, temp_data.temperature) tephi = Tephigram() tephi.plot(dews, label='Dew-point', color='blue', linewidth=2, marker='s') tephi.plot(temps, label='Dry-bulb', color='red', linewidth=2, marker='o') plt.show() """ if not figure: # Create a default figure. self.figure = plt.figure(0, figsize=(9, 9)) else: self.figure = figure # Configure the locators. if isotherm_locator and not isinstance(isotherm_locator, Locator): if not isinstance(isotherm_locator, numbers.Number): raise ValueError('Invalid isotherm locator') locator_isotherm = Locator(isotherm_locator) else: locator_isotherm = isotherm_locator if dry_adiabat_locator and not isinstance(dry_adiabat_locator, Locator): if not isinstance(dry_adiabat_locator, numbers.Number): raise ValueError('Invalid dry adiabat locator') locator_theta = Locator(dry_adiabat_locator) else: locator_theta = dry_adiabat_locator # Define the tephigram coordinate-system transformation. self.tephi_transform = transforms.TephiTransform() grid_helper1 = GridHelperCurveLinear(self.tephi_transform, tick_formatter1=_FormatterIsotherm(), grid_locator1=locator_isotherm, tick_formatter2=_FormatterTheta(), grid_locator2=locator_theta) self.axes = Subplot(self.figure, 1, 1, 1, grid_helper=grid_helper1) self.transform = self.tephi_transform + self.axes.transData self.axes.axis['isotherm'] = self.axes.new_floating_axis(1, 0) self.axes.axis['theta'] = self.axes.new_floating_axis(0, 0) self.axes.axis['left'].get_helper().nth_coord_ticks = 0 self.axes.axis['left'].toggle(all=True) self.axes.axis['bottom'].get_helper().nth_coord_ticks = 1 self.axes.axis['bottom'].toggle(all=True) self.axes.axis['top'].get_helper().nth_coord_ticks = 0 self.axes.axis['top'].toggle(all=False) self.axes.axis['right'].get_helper().nth_coord_ticks = 1 self.axes.axis['right'].toggle(all=True) self.axes.gridlines.set_linestyle('solid') self.figure.add_subplot(self.axes) # Configure default axes. axis = self.axes.axis['left'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('baseline') axis.major_ticklabels.set_rotation(135) axis = self.axes.axis['right'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('baseline') axis.major_ticklabels.set_rotation(-135) self.axes.axis['top'].major_ticklabels.set_fontsize(10) axis = self.axes.axis['bottom'] axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_ha('left') axis.major_ticklabels.set_va('top') axis.major_ticklabels.set_rotation(-45) # Isotherms: lines of constant temperature (degC). axis = self.axes.axis['isotherm'] axis.set_axis_direction('right') axis.set_axislabel_direction('-') axis.major_ticklabels.set_rotation(90) axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('bottom') axis.major_ticklabels.set_color('grey') axis.major_ticklabels.set_visible(False) # turned-off # Dry adiabats: lines of constant potential temperature (degC). axis = self.axes.axis['theta'] axis.set_axis_direction('right') axis.set_axislabel_direction('+') axis.major_ticklabels.set_fontsize(10) axis.major_ticklabels.set_va('bottom') axis.major_ticklabels.set_color('grey') axis.major_ticklabels.set_visible(False) # turned-off axis.line.set_linewidth(3) axis.line.set_linestyle('--') # Lock down the aspect ratio. self.axes.set_aspect(1.) self.axes.grid(True) # Initialise the text formatter for the navigation status bar. self.axes.format_coord = self._status_bar # Factor in the tephigram transform. ISOBAR_TEXT['transform'] = self.transform WET_ADIABAT_TEXT['transform'] = self.transform MIXING_RATIO_TEXT['transform'] = self.transform # Create plot collections for the tephigram isopleths. func = partial(isopleths.isobar, MIN_THETA, MAX_THETA, self.axes, self.transform, ISOBAR_LINE) self._isobars = _PlotCollection(self.axes, ISOBAR_SPEC, MAX_PRESSURE, func, ISOBAR_TEXT, fixed=ISOBAR_FIXED, minimum=MIN_PRESSURE) func = partial(isopleths.wet_adiabat, MAX_PRESSURE, MIN_TEMPERATURE, self.axes, self.transform, WET_ADIABAT_LINE) self._wet_adiabats = _PlotCollection(self.axes, WET_ADIABAT_SPEC, MAX_WET_ADIABAT, func, WET_ADIABAT_TEXT, fixed=WET_ADIABAT_FIXED, minimum=MIN_WET_ADIABAT, xfocus=True) func = partial(isopleths.mixing_ratio, MIN_PRESSURE, MAX_PRESSURE, self.axes, self.transform, MIXING_RATIO_LINE) self._mixing_ratios = _PlotCollection(self.axes, MIXING_RATIO_SPEC, MIXING_RATIOS, func, MIXING_RATIO_TEXT, fixed=MIXING_RATIO_FIXED) # Initialise for the tephigram plot event handler. plt.connect('motion_notify_event', _handler) self.axes.tephigram = True self.axes.tephigram_original_delta_xlim = self.original_delta_xlim = DEFAULT_WIDTH self.axes.tephigram_transform = self.tephi_transform self.axes.tephigram_inverse = self.tephi_transform.inverted() self.axes.tephigram_isopleths = [self._isobars, self._wet_adiabats, self._mixing_ratios] # The tephigram profiles. self._profiles = [] self.axes.tephigram_profiles = self._profiles # Center the plot around the anchor extent. self._anchor = anchor if self._anchor is not None: self._anchor = np.asarray(anchor) if self._anchor.ndim != 2 or self._anchor.shape[-1] != 2 or \ len(self._anchor) != 2: msg = 'Invalid anchor, expecting [(bottom-left-pressure, ' \ 'bottom-left-temperature), (top-right-pressure, ' \ 'top-right-temperature)]' raise ValueError(msg) (bottom_pressure, bottom_temp), \ (top_pressure, top_temp) = self._anchor if (bottom_pressure - top_pressure) < 0: raise ValueError('Invalid anchor pressure range') if (bottom_temp - top_temp) < 0: raise ValueError('Invalid anchor temperature range') self._anchor = isopleths.Profile(anchor, self.axes) self._anchor.plot(visible=False) xlim, ylim = self._calculate_extents() self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) def plot(self, data, **kwargs): """ Plot the environmental lapse rate profile of the pressure and temperature data points. The pressure and temperature data points are transformed into potential temperature and temperature data points before plotting. By default, the tephigram will automatically center the plot around all profiles. .. warning:: Pressure data points must be in units of mb or hPa, and temperature data points must be in units of degC. Args: * data: pressure and temperature pair data points. .. note:: All keyword arguments are passed through to :func:`matplotlib.pyplot.plot`. For example: .. plot:: :include-source: import matplotlib.pyplot as plt from edson import Tephigram tephi = Tephigram() data = [[1006, 26.4], [924, 20.3], [900, 19.8], [850, 14.5], [800, 12.9], [755, 8.3]] profile = tephi.plot(data, color='red', linestyle='--', linewidth=2, marker='o') barbs = [(10, 45, 900), (20, 60, 850), (25, 90, 800)] profile.barbs(barbs) plt.show() For associating wind barbs with an environmental lapse rate profile, see :meth:`~edson.isopleths.Profile.barbs`. """ profile = isopleths.Profile(data, self.axes) profile.plot(**kwargs) self._profiles.append(profile) # Center the tephigram plot around all the profiles. if self._anchor is None: xlim, ylim = self._calculate_extents(xfactor=.25, yfactor=.05) self.axes.set_xlim(xlim) self.axes.set_ylim(ylim) # Refresh the tephigram plot isopleths. _refresh_isopleths(self.axes) # Show the plot legend. if 'label' in kwargs: font_properties = FontProperties(size='x-small') plt.legend(loc='upper left', fancybox=True, shadow=True, prop=font_properties) return profile def _status_bar(self, x_point, y_point): """Generate text for the interactive backend navigation status bar.""" temperature, theta = transforms.xy_to_temperature_theta(x_point, y_point) pressure, _ = transforms.temperature_theta_to_pressure_temperature(temperature, theta) xlim = self.axes.get_xlim() zoom = (xlim[1] - xlim[0]) / self.original_delta_xlim text = "T:%.2f, theta:%.2f, phi:%.2f (zoom:%.3f)" % (float(temperature), float(theta), float(pressure), zoom) return text def _calculate_extents(self, xfactor=None, yfactor=None): min_x = min_y = 1e10 max_x = max_y = -1e-10 profiles = self._profiles if self._anchor is not None: profiles = [self._anchor] for profile in profiles: xy_points = self.tephi_transform.transform(np.concatenate((profile.temperature.reshape(-1, 1), profile.theta.reshape(-1, 1)), axis=1)) x_points = xy_points[:, 0] y_points = xy_points[:, 1] min_x, min_y = np.min([min_x, np.min(x_points)]), np.min([min_y, np.min(y_points)]) max_x, max_y = np.max([max_x, np.max(x_points)]), np.max([max_y, np.max(y_points)]) if xfactor is not None: delta_x = max_x - min_x min_x, max_x = min_x - xfactor * delta_x, max_x + xfactor * delta_x if yfactor is not None: delta_y = max_y - min_y min_y, max_y = min_y - yfactor * delta_y, max_y + yfactor * delta_y return ([min_x, max_x], [min_y, max_y])