"""
The graphics module contains graphic functions.
.. rubric:: Contents
.. autosummary::
signal_convexhull
signal_xsection
animate_puffs
sensor_locations
"""
from __future__ import print_function, division
try:
import matplotlib.pyplot as plt
from matplotlib import ticker
from mpl_toolkits.mplot3d import Axes3D
from matplotlib.patches import Circle, Ellipse, Rectangle
from matplotlib.collections import PatchCollection
from matplotlib.animation import FuncAnimation
except:
pass
from scipy.spatial import ConvexHull
import numpy as np
from chama.sensors import Mobile
[docs]def signal_convexhull(signal, scenarios, threshold, timesteps=None,
colormap=plt.cm.viridis,
x_range=(None, None), y_range=(None, None),
z_range=(None, None)):
"""
Plots of the signal's 3D convex hull.
Parameters
--------------
signal: pandas DataFrame
Signal data from the simulation.
The DataFrame contains columns 'X', 'Y', 'Z', 'T', and one column
for each scenario.
scenarios: list
Column names for the scenarios to be plotted
threshold: float
The minimum value of the signal to be included
timesteps: list (optional)
List of the time steps to include in the plot
colormap: matplotlib.pyplot ColorMap (optional)
A ColorMap object sent to the contourf function
x_range: tuple (optional)
The x-axis limits for the plot
y_range: tuple (optional)
The y-axis limits for the plot
z_range: tuple (optional)
The z-axis limits for the plot
"""
t_col = 'T'
x_col = 'X'
y_col = 'Y'
z_col = 'Z'
if timesteps is None:
timesteps = sorted(set(signal.loc[:, t_col]))
fig = plt.figure()
plt.set_cmap(colormap)
ax = fig.add_subplot(111, projection='3d')
for scenario in scenarios:
i = 0
for timestep in timesteps:
try:
color = colormap(i)
i += 1 / float(len(timesteps))
signal_t = signal[signal[t_col] == timestep]
conc_filter = signal_t[scenario] > threshold
# plot points
# data = signal_t[[x_col,y_col,z_col,scenario]][conc_filter]
# data = data.as_matrix()
# ax.scatter(data[:,0], data[:,1], data[:,2], c=data[:,3],s=30)
data = signal_t[[x_col, y_col, z_col]][conc_filter]
data = data.as_matrix()
hull = ConvexHull(data)
ax.plot_trisurf(data[:, 0], data[:, 1], data[:, 2],
triangles=hull.simplices,
edgecolor='none',
shade=False,
color=color)
except:
pass
ax.set_xlabel(x_col)
ax.set_ylabel(y_col)
ax.set_zlabel(z_col)
ax.set_xlim3d(x_range[0], x_range[1])
ax.set_ylim3d(y_range[0], y_range[1])
ax.set_zlim3d(z_range[0], z_range[1])
fig.show()
[docs]def signal_xsection(signal, signal_name, threshold=None, timesteps=None,
x_value=None, y_value=None, z_value=None, log_flag=False,
colormap=plt.cm.viridis, alpha=0.7, N=5,
x_range=(None, None), y_range=(None, None),
z_range=(None, None)):
"""
Plots x-y, x-z, and y-z cross sections. The signal is
summed over all desired time steps and summed across the axis not
included in the plot unless a value for the third access is specified.
Parameters
--------------
signal: pandas DataFrame
Signal data from the simulation.
The DataFrame contains columns 'X', 'Y', 'Z', 'T', and one column
for each scenario.
signal_name: string
Column name for the signal to be plotted
threshold: float (optional)
The minimum value of the signal to be plotted
timesteps: list (optional)
List of the time steps to include in the plot
x_value: list (optional)
List of the x locations to include in the plot
y_value: list (optional)
List of the y locations to include in the plot
z_value: list (optional)
List of the z locations to include in the plot
log_flag: boolean (optional)
Flag specifying whether the signal should be plotted on a log scale
colormap: matplotlib.pyplot ColorMap (optional)
A ColorMap object sent to the contourf function
alpha: float (optional)
Value between 0 and 1 representing the alpha blending value
passed to the contourf function
N: int (optional)
The number of levels to include in the plot, passed to the
contourf function
x_range: tuple (optional)
The x-axis limits for the plot
y_range: tuple (optional)
The y-axis limits for the plot
z_range: tuple (optional)
The z-axis limits for the plot
"""
t_col = 'T'
x_col = 'X'
y_col = 'Y'
z_col = 'Z'
if timesteps is None:
timesteps = sorted(set(signal.loc[:, t_col]))
if log_flag:
log_flag = ticker.LogLocator()
else:
log_flag = ticker.MaxNLocator()
fig = plt.figure(figsize=(20, 5))
plt.set_cmap(colormap)
ax1 = fig.add_subplot(1, 3, 1)
ax2 = fig.add_subplot(1, 3, 2)
ax3 = fig.add_subplot(1, 3, 3)
signal_t = signal[signal[t_col].isin(timesteps)]
signal_t = signal_t.groupby([x_col, y_col, z_col]).sum()
data = signal_t[signal_name]
def contour_data(temp, threshold, log_flag):
temp = temp.unstack()
X = temp.index.values
Y = temp.columns.values
Z = temp.values
if threshold:
Z[Z <= threshold] = 0
Z = np.transpose(Z)
return X, Y, Z
temp = data.reset_index()
if z_value:
if type(z_value) is not list:
z_value = [z_value]
temp = temp[temp[z_col].isin(z_value)]
temp = temp.groupby([x_col, y_col])[signal_name].sum()
Xi, Yi, Z = contour_data(temp, threshold, log_flag)
cplot1 = ax1.contourf(Xi, Yi, Z, alpha=alpha, cmap=colormap,
locator=log_flag)
ax1.set_xlim(x_range[0], x_range[1])
ax1.set_ylim(y_range[0], y_range[1])
ax1.set_xlabel(x_col)
ax1.set_ylabel(y_col)
ax1.set_title(signal_name)
temp = data.reset_index()
if y_value:
if type(y_value) is not list:
y_value = [y_value]
temp = temp[temp[y_col].isin(y_value)]
temp = temp.groupby([x_col, z_col])[signal_name].sum()
Xi, Yi, Z = contour_data(temp, threshold, log_flag)
cplot2 = ax2.contourf(Xi, Yi, Z, N, alpha=alpha, cmap=colormap,
locator=log_flag)
ax2.set_xlim(x_range[0], x_range[1])
ax2.set_ylim(z_range[0], z_range[1])
ax2.set_xlabel(x_col)
ax2.set_ylabel(z_col)
ax2.set_title(signal_name)
temp = data.reset_index()
if x_value:
if type(x_value) is not list:
x_value = [x_value]
temp = temp[temp[x_col].isin(x_value)]
temp = temp.groupby([y_col, z_col])[signal_name].sum()
Xi, Yi, Z = contour_data(temp, threshold, log_flag)
cplot3 = ax3.contourf(Xi, Yi, Z, N, alpha=alpha, cmap=colormap,
locator=log_flag)
ax3.set_xlim(y_range[0], y_range[1])
ax3.set_ylim(z_range[0], z_range[1])
ax3.set_xlabel(y_col)
ax3.set_ylabel(z_col)
ax3.set_title(signal_name)
fig.colorbar(cplot1, ax=ax1)
fig.colorbar(cplot2, ax=ax2)
fig.colorbar(cplot3, ax=ax3)
fig.show()
[docs]def animate_puffs(puff, x_range=(None, None), y_range=(None, None)):
"""
Plots the horizontal movement of puffs from a GaussianPuff simulation
over time. Each puff is represented as a circle centered at the puff
center location with radius equal to the standard deviation in the
horizontal direction (sigmaY).
Parameters
------------------
puff: pandas DataFrame
The puff DataFrame created by a GaussianPuff object
x_range: tuple (xmin, xmax) (optional)
The x-axis limits for the plot
y_range: tuple (ymin, ymax) (optional)
The y-axis limits for the plot
"""
def circles(x, y, s, c='b', vmin=None, vmax=None, **kwargs):
"""
Make a scatter plot of circles.
Similar to plt.scatter, but the size of circles are in data scale.
Parameters
----------
x, y : scalar or array_like, shape (n, )
Input data
s : scalar or array_like, shape (n, )
Radius of circles.
c : color or sequence of color, optional, default : 'b'
`c` can be a single color format string, or a sequence of color
specifications of length `N`, or a sequence of `N` numbers to be
mapped to colors using the `cmap` and `norm` specified via kwargs.
Note that `c` should not be a single numeric RGB or RGBA sequence
because that is indistinguishable from an array of values
to be colormapped. (If you insist, use `color` instead.)
`c` can be a 2-D array in which the rows are RGB or RGBA, however.
vmin, vmax : scalar, optional, default: None
`vmin` and `vmax` are used in conjunction with `norm` to normalize
luminance data. If either are `None`, the min and max of the
color array is used.
kwargs : `~matplotlib.collections.Collection` properties
Eg. alpha, edgecolor(ec), facecolor(fc), linewidth(lw),
linestyle(ls), norm, cmap, transform, etc.
Returns
-------
paths : `~matplotlib.collections.PathCollection`
"""
if np.isscalar(c):
kwargs.setdefault('color', c)
c = None
if 'fc' in kwargs:
kwargs.setdefault('facecolor', kwargs.pop('fc'))
if 'ec' in kwargs:
kwargs.setdefault('edgecolor', kwargs.pop('ec'))
if 'ls' in kwargs:
kwargs.setdefault('linestyle', kwargs.pop('ls'))
if 'lw' in kwargs:
kwargs.setdefault('linewidth', kwargs.pop('lw'))
# You can set `facecolor` with an array for each patch,
# while you can only set `facecolors` with a value for all.
zipped = np.broadcast(x, y, s)
patches = [Circle((x_, y_), s_)
for x_, y_, s_ in zipped]
collection = PatchCollection(patches, **kwargs)
if c is not None:
c = np.broadcast_to(c, zipped.shape).ravel()
collection.set_array(c)
collection.set_clim(vmin, vmax)
ax = plt.gca()
ax.add_collection(collection)
ax.autoscale_view()
plt.draw_if_interactive()
if c is not None:
plt.sci(collection)
return collection
fig, ax = plt.subplots()
# ln, = plt.plot([],[],animated=True)
def update(time):
plt.cla()
ax.set_xlim(x_range[0], x_range[1])
ax.set_ylim(y_range[0], y_range[1])
ax.set_title('T = %6.2f' % time)
ax.set_xlabel('X')
ax.set_ylabel('Y')
temp = puff.loc[puff['T'] == time]
out = circles(temp['X'], temp['Y'], temp['sigmaY'], alpha=0.5,
edgecolor='none')
return out
ani = FuncAnimation(fig, update, frames=puff['T'].unique())
# Need a coder like ffmpeg installed in order to save
# ani.save('puff.mp4')
plt.show()
[docs]def sensor_locations(sensors, x_range=(None, None), y_range=(None, None),
z_range=(None, None), legend=False,
colors=None, markers=None):
"""
Plots sensor locations.
Parameters
-------------
sensors : dict
A dictionary of sensors with key:value pairs containing
{'sensor name': chama Sensor object}
x_range: tuple (optional)
The x-axis limits for the plot
y_range: tuple (optional)
The y-axis limits for the plot
z_range: tuple (optional)
The z-axis limits for the plot
legend : Boolean (optional)
Indicates if legend should be added to the plot
colors : dict (optional)
A dictionary containing the color string to be used when plotting each
sensor. The key:value pairs are {'sensor name' : String
representing the color to be passed to the plot function)
markers : dict (optional)
A dictionary containing the marker to be used when plotting each
sensor. The key:value pairs are {'sensor name' : String
representing the marker to be passed to the plot function)
"""
fig = plt.figure()
ax = fig.add_subplot(111, projection='3d')
for name, sensor in sensors.items():
plot_options = {}
if colors:
plot_options['color'] = colors[name]
if markers:
plot_options['marker'] = markers[name]
position = sensor.position
if isinstance(position, Mobile):
x = [val[0] for val in position.location]
y = [val[1] for val in position.location]
z = [val[2] for val in position.location]
ax.plot(x, y, z, label=name)
else:
x = position.location[0]
y = position.location[1]
z = position.location[2]
ax.scatter(x, y, z, label=name, **plot_options)
ax.set_xlim3d(x_range[0], x_range[1])
ax.set_ylim3d(y_range[0], y_range[1])
ax.set_zlim3d(z_range[0], z_range[1])
if legend:
ax.legend()
fig.show()