Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
121 changes: 23 additions & 98 deletions LadybugTools_Engine/Python/src/ladybugtools_toolkit/wind.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,10 @@
analysis_period_to_datetimes,
describe_analysis_period,
)
from .bhom.logging import CONSOLE_LOGGER
from python_toolkit.plot.timeseries import timeseries
from .plot.utilities import contrasting_color, format_polar_plot
from python_toolkit.plot.polar import polar
from .plot.utilities import contrasting_color

# pylint: enable=E0401

Expand Down Expand Up @@ -1837,13 +1839,13 @@ def plot_windrose(
self,
ax: plt.Axes = None,
directions: int = 36,
other_data: list[float] = None,
other_bins: list[float] = None,
colors: list[str | tuple[float] | Colormap] = None,
value_bins: list[float] = BEAUFORT_CATEGORIES.bins,
colors: list[str | tuple[float] | Colormap] = BEAUFORT_CATEGORIES.cmap,
title: str = None,
legend: bool = True,
ylim: tuple[float] = None,
label: bool = False,
**kwargs,
) -> plt.Axes:
"""Create a wind rose showing wind speed and direction frequency.

Expand All @@ -1852,12 +1854,9 @@ def plot_windrose(
The axes to plot this chart on. Defaults to None.
directions (int, optional):
The number of directions to use. Defaults to 36.
other_data (list[float], optional):
A list of other data to bin by direction.
If None, then wind speed will be used.
other_bins (list[float]):
The other data bins to use for the histogram. These bins are right inclusive.
If other data is None, then the default Beaufort bins will be used,
value_bins (list[float], optional):
The bins to use for the wind speed values. These bins are right inclusive.
If unset, then the default BEAUFORT_CATEGORIES bins will be used.
otherwise 11 evenly spaced bins will be used.
colors: (str | tuple[float] | Colormap, optional):
A list of colors to use for the other bins. May also be a colormap.
Expand All @@ -1875,103 +1874,29 @@ def plot_windrose(
plt.Axes: The axes object.
"""

#deprecation warnings
if kwargs.pop("other_data", None) is not None:
CONSOLE_LOGGER.warning("'other_data' can no longer be used as an argument to plot custom polar plots with the Wind class. Please use the python_toolkit.plot.polar polar() method instead for this functionality.")

if kwargs.pop("other_bins", None) is not None:
CONSOLE_LOGGER.warning("'other_bins' has been renamed. Please use 'value_bins' in future instead.")
value_bins = kwargs["other_bins"]

if ax is None:
_, ax = plt.subplots(subplot_kw={"projection": "polar"})

# create grouped data for plotting
binned = self.histogram(
directions=directions,
other_data=other_data,
other_bins=other_bins,
density=True,
remove_calm=True,
)
df = pd.concat([self.ws, self.wd], axis=1)

df = df[self.ws >= 1e-10] #remove times where the wind speed is calm, as direction doesn't make sense in this context

# set colors
if colors is None:
if other_data is None:
colors = [
to_hex(BEAUFORT_CATEGORIES.cmap(i))
for i in np.linspace(0, 1, len(binned.columns))
]
else:
colors = [
to_hex(plt.get_cmap("viridis")(i))
for i in np.linspace(0, 1, len(binned.columns))
]
if isinstance(colors, str):
colors = plt.get_cmap(colors)
if isinstance(colors, Colormap):
colors = [to_hex(colors(i)) for i in np.linspace(0, 1, len(binned.columns))]
if isinstance(colors, list | tuple):
if len(colors) != len(binned.columns):
raise ValueError(
f"colors must be a list of length {len(binned.columns)}, or a colormap."
)

# HACK to ensure that bar ends are curved when using a polar plot.
fig = plt.figure()
rect = [0.1, 0.1, 0.8, 0.8]
hist_ax = plt.Axes(fig, rect)
hist_ax.bar(np.array([1]), np.array([1]))
colors = BEAUFORT_CATEGORIES.cmap

if title is None or title == "":
ax.set_title(textwrap.fill(f"{self.source}", 75))
else:
ax.set_title(title)

theta_width = np.deg2rad(360 / directions)
patches = []
color_list = []
x = theta_width / 2
for _, data_values in binned.iterrows():
y = 0
for n, val in enumerate(data_values.values):
patches.append(
Rectangle(
xy=(x, y),
width=theta_width,
height=val,
alpha=1,
)
)
color_list.append(colors[n])
y += val
if label:
ax.text(x, y, f"{y:0.1%}", ha="center", va="center", fontsize="x-small")
x += theta_width
local_cmap = ListedColormap(np.array(color_list).flatten())
pc = PatchCollection(patches, cmap=local_cmap)
pc.set_array(np.arange(len(color_list)))
ax.add_collection(pc)

# construct legend
if legend:
handles = [
mpatches.Patch(color=colors[n], label=f"{i} to {j}")
for n, (i, j) in enumerate(binned.columns.values)
]
_ = ax.legend(
handles=handles,
bbox_to_anchor=(1.1, 0.5),
loc="center left",
ncol=1,
borderaxespad=0,
frameon=False,
fontsize="small",
title=binned.columns.name,
title_fontsize="small",
)

# set y-axis limits
if ylim is None:
ylim = (0, max(binned.sum(axis=1)))
if len(ylim) != 2:
raise ValueError("ylim must be a tuple of length 2.")
ax.set_ylim(ylim)
ax.yaxis.set_major_formatter(mticker.PercentFormatter(xmax=1))
title = textwrap.fill(f"{self.source}", 75)

format_polar_plot(ax, yticklabels=True)
ax = polar(data=df, value_column=df.columns[0], direction_column=df.columns[1], ax=ax, directions=directions, value_bins=value_bins, colours=colors, title=title, legend=legend, ylim=ylim, label=label)

return ax

Expand Down