Polar Chart
A polar chart: bars set out round a middle, each as long from the middle out as its value: a rose. It suits what comes round again, the hours of a day, the months of a year, the points of a compass, and what is to be compared at a glance by how far each reaches.
import com.pantrixui.components.chart.polar.PantrixPolarChart
- Side by side and stacked. The bars of several series stand side by side in a sector; the series that name the same
stackshare one bar there, each further out than the one before it. - The hole in the middle. With
holethe bars start from a ring: the shortest shows too. No more than nine tenths of the radius. - Rings. The value rings are round: so are the bars' ends, which are read against them. With
grid = RadarGrid.Polygonthe rings are drawn straight from one side of a sector to the other, and the bars' ends are straight too. The marked values are written over the line at the start of the first sector, over the bars. - Fill.
fillAlphasays how much of its colour a series' bars are filled with: under one, the rings behind show. The legend and the tooltip show the whole colour. - Axes.
xAxisis the sectors: their names (labels,label) and the lines between them (line).yAxisis the axis of values: its ends, how many values it marks, how they are written, the values (labels) and the rings (grid). - The sector pointed at. The sector the pointer is over, or the one that is tapped, is shaded, and the tooltip writes the values the series have there.
- Direction. The chart turns the same way in a language read from right to left too.
- Export.
exportputs a button over the chart: the chart is made, as it is on the screen and without the part that is pointed at or its tooltip, into a PNG, an SVG or a PDF file and handed to the app. - Automation.
PolarChart_<tagKey>; the plot is.Plot, the sectors.Point_<number>, the legend's entries.Legend_<series>, the tooltip.Tooltip.
Examples
The hours of a day
A shop's visitors by the hour of the day, in parts of two hours. There is a hole in the middle (hole): the short bars of the night hours show too.
Move the pointer over a sector or tap it: the sector is shaded and the tooltip writes the visitors of those hours.
PantrixPolarChart(
series = listOf(PolarSeries("visits", "Visits", shopHours(), x = { it.hours }, y = { it.weekday })),
hole = 0.25f,
description = "Visits by the hour",
tagKey = "plain"
)
internal fun shopHours(): List<ShopHours> = listOf(
ShopHours("00", weekday = 4, weekend = 9, online = 12),
ShopHours("02", weekday = 2, weekend = 5, online = 7),
ShopHours("04", weekday = 2, weekend = 3, online = 4),
ShopHours("06", weekday = 8, weekend = 4, online = 6),
ShopHours("08", weekday = 26, weekend = 12, online = 14),
ShopHours("10", weekday = 41, weekend = 30, online = 22),
ShopHours("12", weekday = 58, weekend = 47, online = 27),
ShopHours("14", weekday = 46, weekend = 62, online = 25),
ShopHours("16", weekday = 52, weekend = 70, online = 29),
ShopHours("18", weekday = 63, weekend = 55, online = 38),
ShopHours("20", weekday = 35, weekend = 41, online = 44),
ShopHours("22", weekday = 14, weekend = 24, online = 30)
)
internal class ShopHours(
val hours: String,
val weekday: Int,
val weekend: Int,
val online: Int
)
Two series side by side
Weekdays and the weekend, side by side in each sector: the difference between the two at the same hours is read.
val hours = shopHours()
PantrixPolarChart(
series = listOf(
PolarSeries("weekday", "Weekdays", hours, x = { it.hours }, y = { it.weekday }),
PolarSeries("weekend", "Weekend", hours, x = { it.hours }, y = { it.weekend })
),
hole = 0.25f,
description = "Visits by the hour, on weekdays and at the weekend",
tagKey = "grouped"
)
internal fun shopHours(): List<ShopHours> = listOf(
ShopHours("00", weekday = 4, weekend = 9, online = 12),
ShopHours("02", weekday = 2, weekend = 5, online = 7),
ShopHours("04", weekday = 2, weekend = 3, online = 4),
ShopHours("06", weekday = 8, weekend = 4, online = 6),
ShopHours("08", weekday = 26, weekend = 12, online = 14),
ShopHours("10", weekday = 41, weekend = 30, online = 22),
ShopHours("12", weekday = 58, weekend = 47, online = 27),
ShopHours("14", weekday = 46, weekend = 62, online = 25),
ShopHours("16", weekday = 52, weekend = 70, online = 29),
ShopHours("18", weekday = 63, weekend = 55, online = 38),
ShopHours("20", weekday = 35, weekend = 41, online = 44),
ShopHours("22", weekday = 14, weekend = 24, online = 30)
)
internal class ShopHours(
val hours: String,
val weekday: Int,
val weekend: Int,
val online: Int
)
Stacked
Two series that name the same stack share one bar in each sector: the whole bar shows the total, and its parts who it came from.
val hours = shopHours()
PantrixPolarChart(
series = listOf(
PolarSeries("store", "Shop", hours, x = { it.hours }, y = { it.weekday }, stack = "all"),
PolarSeries("online", "Online", hours, x = { it.hours }, y = { it.online }, stack = "all")
),
description = "Visits by the hour, in the shop and online",
tagKey = "stacked"
)
internal fun shopHours(): List<ShopHours> = listOf(
ShopHours("00", weekday = 4, weekend = 9, online = 12),
ShopHours("02", weekday = 2, weekend = 5, online = 7),
ShopHours("04", weekday = 2, weekend = 3, online = 4),
ShopHours("06", weekday = 8, weekend = 4, online = 6),
ShopHours("08", weekday = 26, weekend = 12, online = 14),
ShopHours("10", weekday = 41, weekend = 30, online = 22),
ShopHours("12", weekday = 58, weekend = 47, online = 27),
ShopHours("14", weekday = 46, weekend = 62, online = 25),
ShopHours("16", weekday = 52, weekend = 70, online = 29),
ShopHours("18", weekday = 63, weekend = 55, online = 38),
ShopHours("20", weekday = 35, weekend = 41, online = 44),
ShopHours("22", weekday = 14, weekend = 24, online = 30)
)
internal class ShopHours(
val hours: String,
val weekday: Int,
val weekend: Int,
val online: Int
)
Straight rings
Straight rings (RadarGrid.Polygon): each ring is straight from one side of a sector to the other, and the bars' ends are straight too and sit on their ring's line. The bars are filled with sixty per cent of their colour (fillAlpha): the rings behind them show.
PantrixPolarChart(
series = listOf(
PolarSeries("visits", "Visits", shopHours(), x = { it.hours }, y = { it.weekday }, fillAlpha = 0.6f)
),
hole = 0.25f,
grid = RadarGrid.Polygon,
description = "Visits by the hour",
tagKey = "polygon"
)
internal fun shopHours(): List<ShopHours> = listOf(
ShopHours("00", weekday = 4, weekend = 9, online = 12),
ShopHours("02", weekday = 2, weekend = 5, online = 7),
ShopHours("04", weekday = 2, weekend = 3, online = 4),
ShopHours("06", weekday = 8, weekend = 4, online = 6),
ShopHours("08", weekday = 26, weekend = 12, online = 14),
ShopHours("10", weekday = 41, weekend = 30, online = 22),
ShopHours("12", weekday = 58, weekend = 47, online = 27),
ShopHours("14", weekday = 46, weekend = 62, online = 25),
ShopHours("16", weekday = 52, weekend = 70, online = 29),
ShopHours("18", weekday = 63, weekend = 55, online = 38),
ShopHours("20", weekday = 35, weekend = 41, online = 44),
ShopHours("22", weekday = 14, weekend = 24, online = 30)
)
internal class ShopHours(
val hours: String,
val weekday: Int,
val weekend: Int,
val online: Int
)
A look of its own
A chart's own look: more room between the bars (sliceGap), no lines between the sectors (the line of xAxis), and an axis that marks four values (the ticks of yAxis). A field that is not given stays the theme's.
PantrixPolarChart(
series = listOf(PolarSeries("visits", "Visits", shopHours(), x = { it.hours }, y = { it.weekend })),
xAxis = ChartAxis(line = false),
yAxis = ChartAxis(ticks = 4),
description = "Visits by the hour at the weekend",
style = ChartStyle(sliceGap = 6.dp),
tagKey = "own_look"
)
internal fun shopHours(): List<ShopHours> = listOf(
ShopHours("00", weekday = 4, weekend = 9, online = 12),
ShopHours("02", weekday = 2, weekend = 5, online = 7),
ShopHours("04", weekday = 2, weekend = 3, online = 4),
ShopHours("06", weekday = 8, weekend = 4, online = 6),
ShopHours("08", weekday = 26, weekend = 12, online = 14),
ShopHours("10", weekday = 41, weekend = 30, online = 22),
ShopHours("12", weekday = 58, weekend = 47, online = 27),
ShopHours("14", weekday = 46, weekend = 62, online = 25),
ShopHours("16", weekday = 52, weekend = 70, online = 29),
ShopHours("18", weekday = 63, weekend = 55, online = 38),
ShopHours("20", weekday = 35, weekend = 41, online = 44),
ShopHours("22", weekday = 14, weekend = 24, online = 30)
)
internal class ShopHours(
val hours: String,
val weekday: Int,
val weekend: Int,
val online: Int
)
Export
export = ChartExport(...) puts an export button over the chart: press it and choose the kind (PNG, SVG or PDF). The chart makes the file and hands it to onExport; below, its name and size show, and a PNG's picture, and "Save the file" hands it over in the platform's way.
The file has the rings, the hours and the slices.
var file by remember { mutableStateOf<ChartExportFile?>(null) }
PantrixPolarChart(
series = listOf(PolarSeries("visits", "Visits", shopHours(), x = { it.hours }, y = { it.weekday })),
hole = 0.25f,
description = "Visits by the hour",
tagKey = "exported",
export = ChartExport(fileName = "hours") { file = it }
)
ExportedFile(file, tagKey = "exported")
internal fun shopHours(): List<ShopHours> = listOf(
ShopHours("00", weekday = 4, weekend = 9, online = 12),
ShopHours("02", weekday = 2, weekend = 5, online = 7),
ShopHours("04", weekday = 2, weekend = 3, online = 4),
ShopHours("06", weekday = 8, weekend = 4, online = 6),
ShopHours("08", weekday = 26, weekend = 12, online = 14),
ShopHours("10", weekday = 41, weekend = 30, online = 22),
ShopHours("12", weekday = 58, weekend = 47, online = 27),
ShopHours("14", weekday = 46, weekend = 62, online = 25),
ShopHours("16", weekday = 52, weekend = 70, online = 29),
ShopHours("18", weekday = 63, weekend = 55, online = 38),
ShopHours("20", weekday = 35, weekend = 41, online = 44),
ShopHours("22", weekday = 14, weekend = 24, online = 30)
)
internal class ShopHours(
val hours: String,
val weekday: Int,
val weekend: Int,
val online: Int
)
API
series
A polar chart: bars set out round a middle, each as long from the middle out as its value: a rose. It suits what comes round again, the hours of a day, the months of a year, the points of a compass, and what is to be compared at a glance by how far each reaches.
| Parameter | Type | Default | What it is |
|---|---|---|---|
series | List<PolarSeries<*>> | the series, in the order they are drawn and listed. Their keys tell them apart, and two with one key are refused. | |
modifier | Modifier | Modifier | the chart's modifier. A height here is the chart's, the legend's with it. |
hole | Float | 0f | how much of the chart's radius is cut out of its middle: nought for bars that start at the middle. No more than nine tenths. |
xAxis | ChartAxis? | ChartAxis() | the sectors: whether their names are written about the chart (labels), how a name is written (label), and whether a line is drawn between one sector and the next (line). null draws nothing of them. |
yAxis | ChartAxis? | ChartAxis() | the axis of the values, out from the chart's middle: its ends, about how many values it marks, how a value is written, whether the marked values are written (labels) and whether a ring is drawn at each (grid). It starts at nought unless startFromZero or min says otherwise. null draws nothing of it. |
legend | ChartLegend? | ChartLegend() | where the names of the series stand. null shows none. |
tooltip | ChartTooltip? | ChartTooltip() | what is shown for the sector that is pointed at. null shows none. |
empty | (@Composable () -> Unit)? | null | what the chart shows while it has no value to draw, in the place of the theme's words for it ("No data"), which a screen reader is then not told either. null writes the words. |
state | ChartState | rememberChartState() | what the reader has hidden and what they point at: the chart's own where none is given. |
description | String? | null | what the chart shows, in a few words, for a screen reader: the first thing it says of the chart. Left out, the chart is announced by the names of its series. |
onSelect | ((ChartPoint) -> Unit)? | null | called with the value that is pressed: the one pointed at, or of the series whose bar is nearest the press where the sector's values are pointed at together. |
style | ChartStyle? | null | this chart's own geometry: the fields set here, with the theme's for the rest. |
colors | ChartColors? | null | this chart's own colours, where they are not the theme's. |
tagKey | String? | null | the chart's automation key: the chart is PolarChart_<tagKey>, its plot PolarChart_<tagKey>.Plot, a sector of it PolarChart_<tagKey>.Point_<number> counted from one, the legend's entry of a series PolarChart_<tagKey>.Legend_<series' key> and the tooltip PolarChart_<tagKey>.Tooltip. A press on a value is reported as a PolarChart select event with the series' key, and a press on a legend's entry as a toggle event with the key and whether the series is shown. Nothing of the values is reported. |
grid | RadarGrid | RadarGrid.Circle | how the rings are drawn: round, or straight from one side of a sector to the next, a web, whose bars end straight on its lines and are read against them. A chart of fewer than three sectors has round rings either way. |
export | ChartExport? | null | what lets the reader take the chart out of the app as a PNG, an SVG or a PDF: a button over the chart makes the file of it as it is and hands it over (see ChartExport). null, the chart has no such button. |
ChartStyle
A chart's geometry: how tall it is where nothing says, how thick its lines and how large its marks are, and the room between its parts; and whether its fills have textures besides their colours.
| Parameter | Type | Default | What it is |
|---|---|---|---|
height | Dp | Dp.Unspecified | How tall a chart's plot is, with its axes, where the chart's modifier gives it no height. A legend above or under the plot adds its own. |
lineWidth | Dp | Dp.Unspecified | The width of a series' line. |
markerSize | Dp | Dp.Unspecified | The width and height of the mark on a point. |
selectedMarkerSize | Dp | Dp.Unspecified | The same of the mark on the point that is pointed at. |
gridWidth | Dp | Dp.Unspecified | The width of the grid's lines, of the lines along the axes and of the line that marks the place pointed at. Zero draws none. |
labelGap | Dp | Dp.Unspecified | Between the plot and what is written beside it and under it, and between that and an axis' title. |
tickSpacing | Dp | Dp.Unspecified | The least room between two values marked along the axis of values: what decides how many are marked where the axis does not say. |
legendGap | Dp | Dp.Unspecified | Between the legend and the chart it stands beside. |
legendItemGap | Dp | Dp.Unspecified | Between one entry of the legend and the next. |
swatchSize | Dp | Dp.Unspecified | How wide the sample of a series is in the legend and in the tooltip: a short run of its line, or a patch of its fill. |
swatchGap | Dp | Dp.Unspecified | Between that sample and the name after it. |
tooltipShape | ShapeSpec | ShapeSpec.Unspecified | The tooltip's corners: a step of the brand's shape scale, or a fixed shape. |
tooltipHorizontalPadding | Dp | Dp.Unspecified | Between the tooltip's sides and what it holds. |
tooltipVerticalPadding | Dp | Dp.Unspecified | Between its top and bottom and what it holds, and between its heading and rows. |
tooltipGap | Dp | Dp.Unspecified | Between the place pointed at and the tooltip beside it. |
tooltipElevation | Dp | Dp.Unspecified | The tooltip's shadow. |
barGap | Dp | Dp.Unspecified | Between two bars that stand side by side at a place. Bars too thin for it stand closer. |
barMaxWidth | Dp | Dp.Unspecified | The widest a bar is, however much room its place has: a chart of three places is not three slabs. |
barRadius | Dp | Dp.Unspecified | How round the corners of a bar's end are, the end away from nought. |
sliceGap | Dp | Dp.Unspecified | Between two slices of a pie that lie side by side, the same from the pie's middle to its rim. |
selectedSliceOffset | Dp | Dp.Unspecified | How far the slice of a pie that is pointed at stands out past the pie's rim. The pie keeps this much room about itself, so that the slice has where to stand. |
gaugeWidth | Dp | Dp.Unspecified | How thick the arc of a gauge is. An arc is no thicker than half its radius. |
meterHeight | Dp | Dp.Unspecified | How tall the bar of a meter group is. |
trackerHeight | Dp | Dp.Unspecified | How tall the blocks of a status tracker are. |
bubbleMinSize | Dp | Dp.Unspecified | How far across the smallest bubble of a scatter chart is: a point whose size is nothing, or too little to be seen as an area, is still a point. |
bubbleMaxSize | Dp | Dp.Unspecified | How far across its largest bubble is: the one whose size is the most of the chart's. |
navigatorHeight | Dp | Dp.Unspecified | How tall the strip under a chart's plot is, on which the part of the chart that is in view is marked and moved, in a chart whose zoom has one: how wide, beside a plot whose places go down its side. |
barBandShare | Float | Float.NaN | How much of a place's band its bars take, more than nought and one at the most: the rest is the room between one place's bars and the next's. Bars are no wider than barMaxWidth all the same. |
markLineWidth | Dp | Dp.Unspecified | The width of a mark's line: a target, a limit, a day. Zero draws none, and the mark's words are written all the same. |
markBandAlpha | Float | Float.NaN | How much of its colour a mark's band is drawn in, from nought to one: a band lies behind the series, and is a tint. |
patterns | Boolean? | null | Whether the fills of a chart's series, slices and tiles are drawn with textures besides their colours, and its lines broken in turn (ChartPattern): what tells them apart for a reader who cannot tell the colours apart. Left out, as the system is set: on where it is set to tell things apart without colour, iOS's "Differentiate Without Color" and Android's colour correction, and off otherwise. |
nodeWidth | Dp | Dp.Unspecified | How wide a node of a sankey is. |
nodeGap | Dp | Dp.Unspecified | Between two nodes of a column of a sankey. |
ChartColors
What a chart is painted with, for a chart that does not take the theme's colours: a colour that is set is used, and one that is left out stays the theme's.
| Parameter | Type | Default | What it is |
|---|---|---|---|
series | List<Color> | emptyList() | The colours the series are drawn in, in the order they are handed out, for the series that name none of their own. Empty, they are the theme's chart colours. |
grid | Color | Color.Unspecified | The lines drawn across the chart at the marks of an axis. The theme's divider. |
axis | Color | Color.Unspecified | The line along an axis. The theme's border. |
label | Color | Color.Unspecified | What is written beside the axes: the values, the places and the axes' titles, and the names in the legend. The theme's textSecondary. |
crosshair | Color | Color.Unspecified | The line that marks the place pointed at. The theme's borderHover: stronger than the grid it crosses, and quieter than the series it stands behind. |
tooltipContainer | Color | Color.Unspecified | The tooltip's fill. The theme's surface. |
tooltipBorder | Color | Color.Unspecified | The tooltip's edge. The theme's border. |
tooltipText | Color | Color.Unspecified | The tooltip's heading and its values. The theme's textPrimary. |
tooltipLabel | Color | Color.Unspecified | The names of the series in the tooltip. The theme's textSecondary. |
rising | Color | Color.Unspecified | What a value that goes up is drawn in: a step of a waterfall that adds. The theme's success. |
falling | Color | Color.Unspecified | What a value that goes down is drawn in: a step of a waterfall that takes away. The theme's danger. |
Accessibility
- Keyboard. The chart is one stop for Tab: the arrows go from sector to sector round the chart, and after the last back to the first; Home and End go to the first and the last sector, Escape lets go, Enter and Space press the value that is pointed at.
- Screen reader. The chart says its
description, then each sector with the values the series have there.