#%%imports
import logging
import matplotlib.pyplot as plt
import numpy as np
from ..utils import polar2cart
from ..base.LSteinCanvas import LSteinCanvas
from ..base.LSteinPanel import LSteinPanel
logger = logging.getLogger(__name__)
#%%definitions
[docs]
class LSteinMPL:
"""represents matplotlib backend for plotting LStein
- matplotlib backend to show an `LSteinCanvas` with all its `LSteinPanel` elements
- `ax` is a method argument to ensure signature equivalence of different backends
Attributes
- `LSC` -- see `__init__()`
Inferred Attributes
Methods
- `add_xaxis()`
- `add_thetaaxis()`
- `add_yaxis()`
- `add_ylabel()`
- `add_yaxis()`
- `scatter_()`
- `plot_()`
- `show()`
Dependencies
- `logging`
- `matplotlib`
- `numpy`
Comments
"""
[docs]
def __init__(self,
LSC:LSteinCanvas,
):
"""constructor
- initializes class
Parameters
- `LSC`
- `LSteinCanvas`
- canvas to display
Raises
Returns
"""
self.LSC = LSC
return
#canvas
[docs]
def add_xaxis(self,
ax:plt.Axes
):
"""adds x-axis to `ax`
- method to add the x-axis to `ax`
Parameters
- `ax`
- `plt.Axes`
- axis to draw into
Raises
Returns
"""
#get quantities
circles_x, circles_y, \
xtickpos_x, xtickpos_y, xticklabs, \
xlabpos_x, xlabpos_y, = self.LSC.compute_xaxis()
#plotting
ax.plot(circles_x.T, circles_y.T, **self.LSC.xtickkwargs)
for i in range(len(xticklabs)):
ax.annotate(xticklabs[i], xy=(xtickpos_x[i], xtickpos_y[i]), annotation_clip=False, **self.LSC.xticklabelkwargs)
ax.annotate(self.LSC.xlabel, xy=(xlabpos_x, xlabpos_y), annotation_clip=False, **self.LSC.xlabelkwargs)
return
[docs]
def add_thetaaxis(self,
ax:plt.Axes
):
"""adds theta-axis to `ax`
- method to add the theta-axis to `ax`
Parameters
- `ax`
- `plt.Axes`
- axis to draw into
Raises
Returns
"""
#get quantities
thetatickpos_xi, thetatickpos_yi, thetatickpos_xo, thetatickpos_yo, \
thetaticklabelpos_x, thetaticklabelpos_y, thetaticklabs, \
th_label_x, th_label_y, \
x_arrow, y_arrow, = self.LSC.compute_thetaaxis()
#plotting
##remove irrelevant kwargs
thetaticklabelkwargs = {k:v for (k,v) in self.LSC.thetaticklabelkwargs.items() if k not in ["pad"]}
ax.plot(np.array([thetatickpos_xi, thetatickpos_xo]), np.array([thetatickpos_yi, thetatickpos_yo]), **self.LSC.thetatickkwargs)
for i in range(len(self.LSC.thetaticks[0])): #ticklabels
ax.annotate(f"{thetaticklabs[i]}", xy=(thetaticklabelpos_x[i], thetaticklabelpos_y[i]), annotation_clip=False, **thetaticklabelkwargs)
line, = ax.plot(x_arrow[:-1], y_arrow[:-1], **self.LSC.thetatickkwargs)
ax.annotate("",
xy=(x_arrow[-1],y_arrow[-1]),
xytext=(x_arrow[-2],y_arrow[-2]),
arrowprops=dict(
arrowstyle="-|>",
lw=line.get_linewidth(),
color=line.get_color(),
fill=True,
),
annotation_clip=False,
)
ax.annotate(self.LSC.thetalabel, xy=(th_label_x,th_label_y), annotation_clip=False, **self.LSC.thetalabelkwargs)
return
[docs]
def add_ylabel(self,
ax:plt.Axes
):
"""adds y-label to `ax`
- method to add the y-label to `ax`
Parameters
- `ax`
- `plt.Axes`
- axis to draw into
Raises
Returns
"""
#get quantities
ylabpos_x, ylabpos_y = self.LSC.compute_ylabel()
#plotting
##remove irrelevant kwargs
ylabelkwargs = {k:v for (k,v) in self.LSC.ylabelkwargs.items() if k not in ["pad"]}
ax.annotate(self.LSC.ylabel, xy=(ylabpos_x, ylabpos_y), annotation_clip=False, **ylabelkwargs)
return
#panels
[docs]
def add_yaxis(self,
LSP:LSteinPanel, ax:plt.Axes
):
"""adds y-axis of `LSP` to `ax`
- method to add the y-axis of `LSP` to `ax`
Parameters
- `LSP`
- `LSteinPanel`
- y-axis of this canvas will be drawn
- `ax`
- `plt.Axes`
- axis to draw into
Raises
Returns
"""
#get panel boundaries
theta_offset, theta_lb, theta_ub = LSP.get_thetabounds()
r_lb, r_ub = LSP.get_rbounds()
r_bounds = np.array([r_lb, r_ub])
#get yticks
ytickpos_th, yticklabs = LSP.get_yticks(theta_lb, theta_ub)
#convert to cartesian for plotting
x_lb, y_lb = polar2cart(r_bounds, theta_lb)
x_ub, y_ub = polar2cart(r_bounds, theta_ub)
x_bounds = np.array([x_lb,x_ub])
y_bounds = np.array([y_lb,y_ub])
pad = LSP.yticklabelkwargs["pad"] #padding for yticklabels
r_, th_ = np.meshgrid(r_bounds, ytickpos_th)
ytickpos_x, ytickpos_y = polar2cart(r_, th_)
yticklabelpos_x, yticklabelpos_y = polar2cart((1+pad)*r_ub, ytickpos_th)
# ytickpos_x, ytickpos_y = ytickpos_x[::-1], ytickpos_y[::-1]
# yticklabelpos_x, yticklabelpos_y = yticklabelpos_x[::-1], yticklabelpos_y[::-1]
#plotting
##remove irrelevant kwargs
yticklabelkwargs = {k:v for (k,v) in LSP.yticklabelkwargs.items() if k not in ["pad"]}
if LSP.show_yticks:
ax.plot(ytickpos_x.T, ytickpos_y.T, **LSP.ytickkwargs)
for i in range(len(ytickpos_th)):
ax.annotate(yticklabs[i], xy=(yticklabelpos_x[i],yticklabelpos_y[i]), annotation_clip=False, **yticklabelkwargs)
if LSP.show_panelbounds: ax.plot(x_bounds.T, y_bounds.T, **LSP.panelboundskwargs)
return
#plotting methods
[docs]
def scatter_(self,
ax:plt.Axes, x:np.ndarray, y:np.ndarray,
*args, **kwargs
):
"""adds a scatterplot of `x` and `y` to `ax`
- method to add a scatterplot
- only to be called from within `LSteinMPL.show()`
Parameters
- `ax`
- `plt.Axes`
- axis to draw into
- `x`
- `np.ndarray`
- x-values of the series
- has to have same length as `y`
- `y`
- `np.ndarray`
- y-values of the series
- has to have same length as `x`
-`kwargs`
- kwargs to pass to `ax.scatter()`
Raises
Returns
"""
ax.scatter(x, y, **kwargs)
return
[docs]
def plot_(self,
ax:plt.Axes, x:np.ndarray, y:np.ndarray,
*args, **kwargs
):
"""adds a lineplot of `x` and `y` to `ax`
- method to add a lineplot
- only to be called from within `LSteinMPL.show()`
Parameters
- `ax`
- `plt.Axes`
- axis to draw into
- `x`
- `np.ndarray`
- x-values of the series
- has to have same length as `y`
- `y`
- `np.ndarray`
- y-values of the series
- has to have same length as `x`
- `kwargs`
- kwargs to pass to `ax.plot()`
Raises
Returns
"""
ax.plot(x, y, **kwargs)
return
#combined
[docs]
def show(self, ax:plt.Axes):
"""display LStein plot in `ax`
- method to display `self.LSC` within a matplotlib figure
- similar to `plt.show()`
- will
- draw the canvas
- add each panel
- plot series for each panel
Parameters
- `ax`
- `plt.Axes`
- axis to draw into
Raises
Returns
- `ax`
- `plt.Axes`
- `ax` with the respective elements added
"""
#disable some default settings
ax.set_aspect("equal")
ax.set_axis_off()
# ax.set_xmargin(.1)
# ax.set_ymargin(.1)
#add canvas elements
self.add_xaxis(ax)
self.add_thetaaxis(ax)
self.add_ylabel(ax)
#update switch denoting that canvas has been drawn
self.LSC.canvas_drawn = True
#draw panels
for LSP in self.LSC.Panels:
#draw panel if not drawn already
if not LSP.panel_drawn:
self.add_yaxis(LSP, ax)
LSP.panel_drawn = True
#plot all dataseries
for ds in LSP.dataseries:
if ds["seriestype"] == "scatter": func = self.scatter_
elif ds["seriestype"] == "line": func = self.plot_
else:
logger.warning(f"seriestype fof {ds['seriestype']} is not supported. try one of `['scatter','plot']`")
continue
func(ax, ds["x"], ds["y"], **ds["kwargs"])
return ax