Polar bars & wind rose
Overview
A polar bar chart draws one bar per angle: an annular sector that starts at the center (or at a base) and reaches out to r. Use it for amounts per direction or per point of a cycle: wind frequency by compass direction, traffic by hour of the day, sales by month. Stacking several traces outwards gives a wind rose, which splits each direction's total by a second variable such as wind speed.
The barpolar trace is the polar twin of bar: the same marker styling, drawn on the polar subplots (layout.polar, polar2, …) that scatterpolar traces use. Both trace types can share a subplot.
Pick a different chart when:
- the angle is not a direction or a cycle: an ordinary bar chart compares lengths more accurately, because a polar bar's area grows with the square of its radius;
- you have points or a curve rather than amounts: use scatterpolar;
- you want the share of each part of a whole: use a pie.
Minimal example
import { createChart } from '@mk7s/holochart';
createChart(document.getElementById('chart')!, {
data: [{ type: 'barpolar', r: [3, 2, 4, 5], theta: ['N', 'E', 'S', 'W'] }],
layout: { polar: { angularaxis: { direction: 'clockwise' } } },
});Each theta gets a bar of length r. With category angles, the bars are spread evenly around the circle, and direction: 'clockwise' starts them at 12 o'clock, like a compass. The live example stacks two traces over eight directions:
Loading source…
polar/barpolarData format
randtheta, as for scatterpolar: arrays of the same length (the shorter one wins), orr0/drandtheta0/dthetafor evenly stepped values.thetais in the trace'sthetaunit('degrees'by default,'radians'or'gradians') on a numeric angular axis, or category names on a category axis.- Bar extent. Each bar spans from
base(default 0, in radial axis units) tobase + r.widthsets its angular width andoffsetshifts its leading edge fromtheta; both are inthetauniton numeric angular axes and in category slots on category axes, and take one value or one per bar. By default a bar is centered on its angle and as wide as the smallest angle between bars, lesspolar.bargap. - Stacking. With
polar.barmode: 'stack'(the default), bars of all traces at the same angle stack outwards in trace order. Traces that setbaseare not stacked: they are drawn from their base.barmode: 'overlay'draws every trace from zero, over one another. - Missing values. A bar whose
rorthetais not a number is not drawn. - Radial range. The radial axis autoranges over both ends of every bar, from zero (
rangemode: 'tozero'). Bars are clipped to the radial range and to the sector.
import { createChart } from '@mk7s/holochart';
// Hours of the day on a numeric angular axis (15° per hour), with bars 12° wide.
createChart(document.getElementById('chart')!, {
data: [
{
type: 'barpolar',
r: [2, 1, 1, 3, 8, 12, 9, 7, 6, 8, 10, 11, 9, 7, 5, 4, 6, 9, 12, 10, 7, 5, 3, 2],
theta0: 0,
dtheta: 15,
width: 12,
},
],
layout: { polar: { angularaxis: { direction: 'clockwise' } } },
});Variations
Wind rose
One trace per wind speed bin, stacked outwards over 16 compass directions, with one color per bin (marker.color), a percent radial axis (ticksuffix) and a slightly narrower bargap. The hovertemplate reads %{theta} and %{r}:
Loading source…
polar/wind-roseOverlaid bars
polar.barmode: 'overlay' draws each trace from the center, so bars at the same angle overlap; translucent markers (marker.opacity) keep both visible. The angular axis here is numeric (degrees, 30° apart), so bars are as wide as the smallest angle between them, less polar.bargap (0.25):
Loading source…
polar/barpolar-overlayFloating bars: base, width and offset
base starts each bar away from the center, for ranges per angle: here the daily temperature range per month, from the low to the high. width sets the angular width (in category slots on this category axis) and offset shifts each bar's leading edge off its angle, so two series sit side by side. Traces with a base are not stacked, even in stack mode:
Loading source…
polar/barpolar-baseBars on a polygon grid
With polar.gridshape: 'linear' (category angular axes), the grid is a polygon through the category angles, and stacked bars follow the polygon's straight edges instead of arcs. hole leaves the middle free:
Loading source…
polar/barpolar-polygonStyling
- Bars.
marker.color(one color, one per bar, or numbers mapped throughcolorscalewithcmin/cmax/cmid, a colorbar or a sharedcoloraxis),marker.line(color,width) for the outline, andmarker.opacity.selectedandunselectedstyle selected bars. See the bar page. - Spacing.
polar.bargap(default 0.1) is the gap between bars at adjacent angles, as a fraction of the smallest angle between them;widthandoffsetoverride it per trace or per bar. - Subplot and axes.
polar.bgcolor,sector,hole,gridshape, and the radial and angular axes, as for scatterpolar.angularaxis.direction: 'clockwise'(which starts at north) makes compass roses. - Default look. The default
holocharttemplate gives bars a thin outline in the background color, which separates stacked bars;layout.template: 'plotly-classic'renders Plotly's defaults. See themes and templates.
Interactivity
Hover. The bar under the pointer shows
r: …(the bar's own length, not the stacked total) andθ: …, then itstextorhovertext; the label sits on the middle of the bar's outer edge. A marker of a scatterpolar trace within reach wins over a bar, and among overlapping bars the narrower one wins.hovertemplatetakes%{r},%{theta},%{text}and%{customdata}.Zoom and rotation. The polar subplot's drags work as with scatterpolar: the radial axis handle changes the radial range or the axis angle, the band just outside the circle rotates the angular axis, a zoom box sets the radial range, and a double-click resets. They emit
relayoutingandrelayoutwith Plotly's keys. See scatterpolar interactivity.Events.
hoverandclickpoints carryr,thetaandpointNumber:tschart.on('click', (e) => console.log(e.points[0]?.theta, e.points[0]?.r));Selection. With
dragmode: 'select'or'lasso', a bar is selected when the middle of its outer edge is inside the box or lasso (Plotly's rule); Shift adds, a double-click clears.selected/unselectedstyle the bars, andselectedpoints carryrandtheta. See scatterpolar selection.Transitions. Transitions and
animatedon't interpolate polar bars, as in Plotly.
Performance notes
- On circular grids, a trace's bars are one instanced GPU draw call (annular sectors drawn by the arc primitive, outlines included), however many bars it has.
- On polygon grids (
gridshape: 'linear'), bars are batched polygon fills plus an outline. - Zoom and rotation redraw the bars from the subplot without re-running the figure pipeline. Stacking runs once per layout pass over every bar trace of the subplot.
- With
@mk7s/holochart-runtimeand your own module list, registerbarpolarwithpolarComponent(the axes and drags);sciTracesincludes both. See the performance guide.
Accessibility notes
- Screen readers: the chart is a
<canvas>; the hidden description (see the accessibility guide) reads each trace as a "polar bar" with its bar count and the lowest and highest bar (rwith its angle, formatted like the hover labels), and its data table lists the bars'randθ(andtextwhen given per bar). - Keyboard: Tab moves into the plot area; the arrow keys then step through the bars, each showing its hover label. See the keys by chart family. The script-tag build leaves these stops out for now.
- Reading the chart: a bar's area grows with the square of its length, so outer segments of a wind rose look bigger than they are. Say what the radial axis measures (a
ticksuffixor axis title), and keep the number of stacked bins small. - Color: use an ordered sequence of colors for ordered bins (wind speed), light to dark or along a sequential scale, so the order reads without the legend.
Attribute reference
See the barpolar attribute reference for every trace attribute, its type and its default. The subplot, barmode and bargap are under polar in the layout reference.
Related charts
- Polar & radar: markers, lines and filled shapes on the same subplots
- Bar: the cartesian twin, better for comparing lengths
- Pie: shares of a whole
Plotly migration notes
- Attribute names and defaults match Plotly's
barpolarandlayout.polar.barmode/bargap:r,theta,base,offset,width,thetaunit,subplot,marker,hovertemplate. Stacking and default widths follow Plotly's rules. - On polygon grids (
gridshape: 'linear'), bars are drawn along the polygon's straight edges, which can differ slightly from Plotly's drawing. thetaunit: 'gradians'converts gradians (400 per turn); plotly.js reads them as radians.- Transitions don't interpolate polar bars; Plotly doesn't either.
marker.patternhatches the bars as on bar charts, with tiles anchored at the pole on circular grids (see Patterns & textures).- Polar selections aren't stored in
layout.selections, as in Plotly, and their events have norange/lassoPoints.