English
Step 8: other kinds of chart
The kind of chart is the renderer. The axes, the series, the cursor, the tooltip stay as they are.
ts
import {
D3PAutoDomain, d3pAutoInteractionArea, D3PAxis, D3PAxisDomain, D3PAxisTitle, D3PChart, D3PCursorIndicator,
d3pDomTooltip, D3PLegendPanel, D3PLineRenderer, D3PPanTool, D3PResetTool, D3PSeries, D3PTitle, D3PToolbox,
D3PTooltip, D3PWheelZoomTool, getGlobalBus,
D3PAutoDomain, d3pAutoInteractionArea, D3PAxis, D3PAxisDomain, D3PAxisTitle, D3PBarRenderer, D3PChart,
D3PCursorIndicator, d3pDomTooltip, D3PLegendPanel, D3PLineRenderer, D3PPanTool, D3PResetTool, D3PSeries,
D3PTitle, D3PToolbox, D3PTooltip, D3PWheelZoomTool, getGlobalBus,
} from 'd3pict'
const bus = getGlobalBus()
const container = document.getElementById('app')! // any element with a size: the chart fills it
const temperature = new D3PSeries({ bus, name: 'temperature', data: [[0, 12], [4, 9], [8, 14], [12, 21], [16, 24], [20, 17], [24, 13]] })
const feelsLike = new D3PSeries({ bus, name: 'feels like', data: [[0, 10], [4, 6], [8, 12], [12, 22], [16, 25], [20, 15], [24, 11]] })
const humidity = new D3PSeries({ bus, name: 'humidity', data: [[0, 80], [4, 85], [8, 70], [12, 45], [16, 40], [20, 60], [24, 75]] })
const hours = new D3PAxisDomain({ bus, domain: [0, 24] })
const degrees = new D3PAxisDomain({ bus, domain: [0, 30] })
const percent = new D3PAxisDomain({ bus, domain: [0, 100] })
const auto = new D3PAutoDomain({ bus })
auto.linkSeries(temperature, [hours, degrees])
auto.linkSeries(feelsLike, [hours, degrees])
const x = new D3PAxis({ bus, name: 'hour', domain: hours, props: { tickCount: 6, tickFormat: (v) => `${Math.round(v)} h`, grid: { enabled: true } } })
const y = new D3PAxis({ bus, name: 'temperature', domain: degrees, position: 'left', props: { tickFormat: (v) => `${Math.round(v * 10) / 10} °C`, grid: { enabled: true } } })
// a second Y axis, on the right, for a quantity in other units
const yRight = new D3PAxis({ bus, name: 'humidity', domain: percent, position: 'right', props: { tickFormat: (v) => `${Math.round(v)} %` } })
// one renderer per series; its style is a prop
const line = new D3PLineRenderer({ bus, series: temperature, props: { style: { color: '#e15759', curve: 'monotone', strokeWidth: 2.5 } } })
const humidityLine = new D3PLineRenderer({ bus, series: humidity, axes: ['hour', 'humidity'], props: { style: { color: '#59a14f', curve: 'monotone' } } })
// the same series, drawn as bars; a renderer is the only thing that changes
const humidityBars = new D3PBarRenderer({ bus, series: humidity, axes: ['hour', 'humidity'], props: { style: { color: '#9ccc96', barWidth: 14 } } })
const feelsLine = new D3PLineRenderer({ bus, series: feelsLike, props: { style: { color: '#4e79a7', curve: 'monotone' } } })
// what the pointer shows: a crosshair and a tooltip
const cursor = new D3PCursorIndicator({ bus })
const tooltip = new D3PTooltip({ bus })
// tools act on the domains: drag to pan, wheel to zoom
const tools = new D3PToolbox({ bus })
tools.addTool(new D3PPanTool({ bus }))
tools.addTool(new D3PWheelZoomTool({ bus }))
tools.addTool(new D3PResetTool({ bus }))
for (const name of ['pan', 'wheel-zoom', 'reset']) tools.setToolActive(name)
// titles and a legend take their room from the plot
const title = new D3PTitle({ bus, props: { text: 'Temperature', subtitle: 'on 12 June' } })
const xTitle = new D3PAxisTitle({ bus, axis: x, props: { text: 'hour of the day' } })
const yTitle = new D3PAxisTitle({ bus, axis: y, props: { text: 'temperature' } })
const yRightTitle = new D3PAxisTitle({ bus, axis: yRight, props: { text: 'humidity' } })
const legendPanel = new D3PLegendPanel({ bus, placement: 'bottom' })
const objects = [x, y, yRight, line, feelsLine, humidityLine, cursor, tooltip, tools, title, xTitle, yTitle, yRightTitle, legendPanel]
const objects = [x, y, yRight, humidityBars, line, feelsLine, cursor, tooltip, tools, title, xTitle, yTitle, yRightTitle, legendPanel]
const chart = new D3PChart({ container, bus })
const stopArea = d3pAutoInteractionArea(chart) // before the objects: the cursor, the tooltip and the tools ask for an interaction area
objects.forEach((o) => o.addToChart(chart))
const stopTooltip = d3pDomTooltip(tooltip)What changed:
- Bars instead of a line. The humidity series is the same;
D3PBarRendererreplacesD3PLineRenderer.barWidthis in pixels on a continuous axis, and a fraction of the band on a band axis. - The bars are first in the list of objects, so they are drawn behind the lines: renderers draw in the order they are attached.
To put an area under a line, add a second renderer on the same series: new D3PAreaRenderer({ bus, series, marker: false }). Its marker: false leaves the dot of the tooltip to the line above it.
The renderers
| Renderer | Draws |
|---|---|
D3PLineRenderer, D3PAreaRenderer, D3PScatterRenderer | lines, areas (down to zero, to a base, or between two series), points |
D3PBarRenderer | columns, horizontal bars, grouped or stacked bars, lollipops, ranges; bars on polar axes |
D3PBubbleRenderer | points of a size given by a third column |
D3PCandlestickRenderer, D3PBoxPlotRenderer, D3PDumbbellRenderer | series with several values per point |
D3PHeatmapRenderer | a grid colored by a value (D3PColorScale, D3PColorLegend) |
D3PPieRenderer, D3PFunnelRenderer | parts of a whole: no axes |
Some chart types need a transform to prepare the series: D3PStack (stacked areas and bars, streamgraphs), D3PWaterfall, D3PHistogram. Annotations (D3PAnnotationLine, D3PAnnotationBand, D3PAnnotationPoint, D3PAnnotationText) mark values of the axes and follow zooms. They stop at the edge of the plot: a mark whose value is outside the axis is not drawn, and neither is its label.
Each chart type has a page with the chart and the code that makes it, and the chart types reference says how each one is built.