Skip to content
Open
Show file tree
Hide file tree
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
79 changes: 79 additions & 0 deletions doc/gallery/gridded/barbs.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "c73d803c-a005-496f-9812-7eb414ce6449",
"metadata": {},
"source": [
"# Wind Barbs\n",
"\n",
"A wind barbs plot showing wind speed and direction using meteorological wind barb symbols.\n",
"\n",
":::{note}\n",
"Wind barb plots require `geoviews` to be installed.\n",
":::"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "ac5ea091-6484-4fc8-9852-d48acd90cc74",
"metadata": {},
"outputs": [],
"source": [
"import hvplot.xarray # noqa\n",
"import numpy as np\n",
"import xarray as xr\n",
"\n",
"def sample_wind_data(shape=(20, 30)):\n",
" x = np.linspace(311.9, 391.1, shape[1])\n",
" y = np.linspace(-23.6, 24.8, shape[0])\n",
" x2d, y2d = np.meshgrid(x, y)\n",
" u = 10 * (2 * np.cos(2 * np.deg2rad(x2d) + 3 * np.deg2rad(y2d + 30)) ** 2)\n",
" v = 20 * np.cos(6 * np.deg2rad(x2d))\n",
" return x, y, u, v\n",
"\n",
"xs, ys, U, V = sample_wind_data()\n",
"# Calculate magnitude and angle for wind barbs\n",
"mag = np.sqrt(U**2 + V**2)\n",
"angle = (np.pi/2.) - np.arctan2(U/mag, V/mag)\n",
"\n",
"ds = xr.Dataset({\n",
" 'speed': xr.DataArray(mag, dims=('y', 'x'), coords={'y': ys, 'x': xs}),\n",
" 'angle': xr.DataArray(angle, dims=('y', 'x'), coords={'y': ys, 'x': xs})\n",
"})\n",
"\n",
"ds.hvplot.windbarbs(\n",
" x='x',\n",
" y='y',\n",
" angle='angle',\n",
" mag='speed',\n",
" color='speed',\n",
" cmap='viridis',\n",
" colorbar=True,\n",
" title='Wind Barbs Plot',\n",
" width=700,\n",
" height=400\n",
")"
]
},
{
"cell_type": "markdown",
"id": "4f0cd955-3e03-42a5-b581-f828036e1716",
"metadata": {},
"source": [
":::{seealso}\n",
"- [Wind Barbs reference documentation](../../ref/api/manual/hvplot.hvPlot.barbs.ipynb).\n",
":::"
]
}
],
"metadata": {
"language_info": {
"name": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 5
}
2 changes: 2 additions & 0 deletions doc/ref/api/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,7 @@ This section documents all the plotting methods of the `hvPlot` class, which as

.. autosummary::

hvPlot.windbarbs
hvPlot.contour
hvPlot.contourf
hvPlot.image
Expand Down Expand Up @@ -123,6 +124,7 @@ hvPlot's structure is based on Pandas' plotting API and as such provides special

hvplot.hvPlot.area <manual/hvplot.hvPlot.area>
hvplot.hvPlot.bar <manual/hvplot.hvPlot.bar>
hvplot.hvPlot.windbarbs <manual/hvplot.hvPlot.windbarbs>
hvplot.hvPlot.barh <manual/hvplot.hvPlot.barh>
hvplot.hvPlot.box <manual/hvplot.hvPlot.box>
hvplot.hvPlot.bivariate <manual/hvplot.hvPlot.bivariate>
Expand Down
157 changes: 157 additions & 0 deletions doc/ref/api/manual/hvplot.hvPlot.barbs.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,157 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# hvPlot.windbarbs\n",
"\n",
"```{eval-rst}\n",
".. currentmodule:: hvplot\n",
"\n",
".. automethod:: hvPlot.windbarbs\n",
"```\n",
"\n",
":::{note}\n",
"Wind barb plots require `geoviews` to be installed.\n",
":::\n",
"\n",
"## Backend-specific styling options\n",
"\n",
"```{eval-rst}\n",
".. backend-styling-options:: windbarbs\n",
"```\n",
"\n",
"## Examples\n",
"\n",
"### Basic wind barbs plot\n",
"\n",
"In this example we create a simple DataFrame with 4 columns `x`, `y`, `angle` and `speed`. `x` and `y` represent the location of the wind barbs. `angle` defines the wind direction expressed in radians (meteorological convention: direction FROM which the wind is blowing, with 0 being North). `speed` defines the wind speed magnitude."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import hvplot.pandas\n",
"import pandas as pd\n",
"import numpy as np\n",
"\n",
"df = pd.DataFrame(dict(\n",
" x=[0, -1, 0, 1, 0, 1, 1, -1, -1],\n",
" y=[0, 0, -1, 0, 1, 1, -1, -1, 1],\n",
" angle=[0, 0, np.pi/2, np.pi, 3*np.pi/2, np.pi/4, np.pi/3, np.pi/6, np.pi/8],\n",
" speed=[0, 5, 10, 15, 20, 50, 75, 100, 150],\n",
"))\n",
"\n",
"df.hvplot.windbarbs(\n",
" x=\"x\", y=\"y\", angle=\"angle\", mag=\"speed\",\n",
" data_aspect=1, padding=0.2, width=400, height=400,\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Xarray example\n",
"\n",
"In this example we show how to create a barb plot using Xarray data with hvPlot."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import hvplot.xarray # noqa\n",
"import numpy as np\n",
"import xarray as xr\n",
"\n",
"def sample_wind_data(shape=(20, 30)):\n",
" x = np.linspace(311.9, 391.1, shape[1])\n",
" y = np.linspace(-23.6, 24.8, shape[0])\n",
" x2d, y2d = np.meshgrid(x, y)\n",
" u = 10 * (2 * np.cos(2 * np.deg2rad(x2d) + 3 * np.deg2rad(y2d + 30)) ** 2)\n",
" v = 20 * np.cos(6 * np.deg2rad(x2d))\n",
" return x, y, u, v\n",
"\n",
"xs, ys, U, V = sample_wind_data()\n",
"mag = np.sqrt(U**2 + V**2)\n",
"angle = (np.pi/2.) - np.arctan2(U/mag, V/mag)\n",
"ds = xr.Dataset({\n",
" 'speed': xr.DataArray(mag, dims=('y', 'x'), coords={'y': ys, 'x': xs}),\n",
" 'angle': xr.DataArray(angle, dims=('y', 'x'), coords={'y': ys, 'x': xs})\n",
"})\n",
"\n",
"ds.hvplot.windbarbs(\n",
" x='x', y='y', angle='angle', mag='speed',\n",
" width=700, height=400, scale=0.3\n",
")"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"### Geographic example with Xarray\n",
"\n",
"The `xarray.Dataset` constructed in this example has a `'crs'` key in its `attrs` dictionary, which lets us simply set `geo=True` to turn this plot into a correctly projected geographic plot overlaid on web map tiles."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import cartopy.crs as ccrs\n",
"import hvplot.xarray # noqa\n",
"import numpy as np\n",
"import xarray as xr\n",
"\n",
"def sample_data(shape=(20, 30)):\n",
" \"\"\"\n",
" Return ``(x, y, u, v, crs)`` of some vector data computed mathematically.\n",
" The returned crs will be a rotated pole CRS, meaning that the vectors\n",
" will be unevenly spaced in regular PlateCarree space.\n",
" \"\"\"\n",
" crs = ccrs.RotatedPole(pole_longitude=177.5, pole_latitude=37.5)\n",
"\n",
" x = np.linspace(311.9, 391.1, shape[1])\n",
" y = np.linspace(-23.6, 24.8, shape[0])\n",
" x2d, y2d = np.meshgrid(x, y)\n",
" u = 10 * (2 * np.cos(2 * np.deg2rad(x2d) + 3 * np.deg2rad(y2d + 30)) ** 2)\n",
" v = 20 * np.cos(6 * np.deg2rad(x2d))\n",
" return x, y, u, v, crs\n",
"\n",
"xs, ys, U, V, crs = sample_data()\n",
"mag = np.sqrt(U**2 + V**2)\n",
"angle = (np.pi/2.) - np.arctan2(U/mag, V/mag)\n",
"ds = xr.Dataset(\n",
" {\n",
" 'speed': xr.DataArray(mag, dims=('y', 'x'), coords={'y': ys, 'x': xs}),\n",
" 'angle': xr.DataArray(angle, dims=('y', 'x'), coords={'y': ys, 'x': xs})\n",
" },\n",
" attrs={'crs': crs},\n",
")\n",
"\n",
"ds.hvplot.windbarbs(\n",
" x=\"x\", y=\"y\", angle=\"angle\", mag=\"speed\",\n",
" geo=True, tiles=\"CartoLight\", width=700, height=400\n",
").opts(\"WindBarbs\", scale=0.3)"
]
}
],
"metadata": {
"language_info": {
"name": "python",
"pygments_lexer": "ipython3"
}
},
"nbformat": 4,
"nbformat_minor": 4
}
48 changes: 46 additions & 2 deletions hvplot/converter.py
Original file line number Diff line number Diff line change
Expand Up @@ -574,7 +574,9 @@ class HoloViewsConverter:
_geom_types = ['paths', 'polygons']

_geo_types = sorted(
_gridded_types + _geom_types + ['points', 'vectorfield', 'labels', 'hexbin', 'bivariate']
_gridded_types
+ _geom_types
+ ['points', 'windbarbs', 'vectorfield', 'labels', 'hexbin', 'bivariate']
)

_stats_types = ['hist', 'kde', 'violin', 'box', 'density']
Expand Down Expand Up @@ -745,6 +747,7 @@ class HoloViewsConverter:
'area': ['x', 'y', 'y2', 'stacked'],
'bar': ['x', 'y', 'stacked'],
'barh': ['x', 'y', 'stacked'],
'windbarbs': ['x', 'y', 'angle', 'mag', 'scale'],
'box': ['x', 'y'],
'errorbars': ['x', 'y', 'yerr1', 'yerr2'],
'bivariate': ['x', 'y', 'bandwidth', 'cut', 'filled', 'levels'],
Expand Down Expand Up @@ -3275,15 +3278,56 @@ def contourf(self, x=None, y=None, z=None, data=None):
else:
return contourf

def windbarbs(self, x=None, y=None, angle=None, mag=None, data=None):
self._error_if_unavailable('windbarbs')
data, x, y, _ = self._process_gridded_args(data, x, y, z=None)

if not (x and y):
if hasattr(data, 'coords'):
x, y = list(k for k, v in data.coords.items() if v.size > 1)
else:
x, y = data.columns[:2]

angle = self.kwds.get('angle')
mag = self.kwds.get('mag')

if (angle is None) != (mag is None):
raise ValueError("windbarbs requires either both 'angle' and 'mag' or neither")
if angle is None and mag is None:
raise ValueError("windbarbs requires 'angle' and 'mag' parameters")

z = [angle, mag] + self.hover_cols
redim = self._merge_redim({z[1]: self._dim_ranges['c']})
params = dict(self._relabel)

element = self._get_element('windbarbs')
cur_opts, compat_opts = self._get_compat_opts('WindBarbs')
if self.geo:
params['crs'] = self.crs

return redim_(
element(data, [x, y], z, **params),
**redim,
).apply(self._set_backends_opts, cur_opts=cur_opts, compat_opts=compat_opts)

def vectorfield(self, x=None, y=None, angle=None, mag=None, data=None):
self._error_if_unavailable('vectorfield')
data, x, y, _ = self._process_gridded_args(data, x, y, z=None)

if not (x and y):
x, y = list(k for k, v in data.coords.items() if v.size > 1)
if hasattr(data, 'coords'):
x, y = list(k for k, v in data.coords.items() if v.size > 1)
else:
x, y = data.columns[:2]

angle = self.kwds.get('angle')
mag = self.kwds.get('mag')

if (angle is None) != (mag is None):
raise ValueError("vectorfield requires either both 'angle' and 'mag' or neither")
if angle is None and mag is None:
raise ValueError("vectorfield requires 'angle' and 'mag' parameters")

z = [angle, mag] + self.hover_cols
redim = self._merge_redim({z[1]: self._dim_ranges['c']})
params = dict(self._relabel)
Expand Down
Loading