Français
Étape 9 : graphiques polaires
Un graphique polaire dessine les mêmes séries avec un angle et une distance au centre. Seuls les axes changent.
ts
import {
D3PAutoDomain, d3pAutoInteractionArea, D3PAxis, D3PAxisDomain, D3PAxisTitle, D3PBarRenderer, D3PChart,
D3PCursorIndicator, d3pDomTooltip, D3PLegendPanel, D3PLineRenderer, D3PPanTool, D3PResetTool, D3PSeries,
D3PTitle, D3PToolbox, D3PTooltip, D3PWheelZoomTool, getGlobalBus,
D3PAutoDomain, d3pAutoInteractionArea, D3PAxis, D3PAxisDomain, D3PAxisTitle, 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 } } })
const yRight = new D3PAxis({ bus, name: 'humidity', domain: percent, position: 'right', props: { tickFormat: (v) => `${Math.round(v)} %` } })
// the same domains on polar axes: the hour is an angle, the temperature a distance from the center
const x = new D3PAxis({ bus, name: 'hour', domain: hours, position: 'angle', props: { tickCount: 6, tickFormat: (v) => `${Math.round(v)} h`, grid: { enabled: true } } })
const y = new D3PAxis({ bus, name: 'temperature', domain: degrees, position: 'radius', props: { tickFormat: (v) => `${Math.round(v * 10) / 10} °C`, grid: { enabled: true } } })
// 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 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, humidityBars, line, feelsLine, cursor, tooltip, tools, title, xTitle, yTitle, yRightTitle, legendPanel]
const objects = [x, y, line, feelsLine, cursor, tooltip, tools, title, xTitle, yTitle, 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)Ce qui a changé :
- Les axes ont d'autres positions.
position: 'angle'fait de l'heure un angle,position: 'radius'fait de la température une distance au centre. Les domaines, les séries, les rendus, le curseur, l'infobulle, les outils et la légende sont ceux du graphique cartésien. - L'humidité a disparu, pour garder un graphique qui se lit bien : un système polaire a un angle et un rayon.
- Les titres sont contre le disque : celui de l'angle dessous, celui du rayon à sa gauche.
Les rendus ne savent pas dans quelle géométrie ils dessinent. Ils demandent à leur système de coordonnées de placer les points : le système cartésien leur donne un rectangle, le polaire un disque.
Le système polaire
Le système est créé avec le premier axe polaire, avec ses réglages par défaut. Pour les changer, créez-le vous-même et attachez-le avant les axes :
ts
const polar = new D3PPolarSystem({ bus, props: { startAngle: 90, clockwise: false, innerRadius: 0.4 } })| Prop | Est |
|---|---|
startAngle | où commence le domaine de l'angle, en degrés (0 est en haut) |
clockwise | le sens dans lequel l'angle croît |
sweep | la partie du cercle couverte, en degrés : 180 est un demi-cercle (une jauge) |
innerRadius | le trou, en fraction du rayon : un anneau, un donut |
shift | déplace le centre, en fractions du rayon |
padding | pixels gardés autour du disque pour les étiquettes de l'angle |
reach | jusqu'où au-delà du disque le pointeur compte encore comme sur le graphique (112 px) |
Les valeurs hors du domaine n'ont pas de place : une courbe polaire est coupée à la plage d'angle et écrêtée à l'anneau, jamais enroulée autour du cercle.
Mêmes données, deux géométries
Échangez les positions et le graphique change. L'exemple cartésien et polaire le fait avec un bouton. L'axe du rayon peut suivre le pointeur (D3PCursorRadiusAxisRenderer).
Camemberts et radars
Un camembert est un rendu dans un système polaire (D3PPieRenderer ; un D3PPolarSystem avec un innerRadius fait un anneau). Un radar utilise des axes polaires avec D3PRadarAngleAxisRenderer ; une rose ou des barres radiales utilisent D3PBarRenderer. Voir les types de graphiques.
Des graphiques qui se suivent
Deux graphiques dont les axes partagent un D3PAxisDomain restent synchrones, un cartésien et un polaire compris : cartésien et polaire, synchronisés.