Skip to content

Step 2: axes and domains ​

An axis turns the values of your data into positions. Two things take part: its domain, the range of values it shows, and its scale, the function that maps that range to pixels. You write the domain; the axis builds the scale from it and from the size of the chart.

ts
import { D3PAxis, D3PAxisDomain, D3PChart, D3PLineRenderer, D3PSeries, getGlobalBus } from 'd3pict'
import {
  D3PAutoDomain, D3PAxis, D3PAxisDomain, D3PChart, D3PLineRenderer, D3PSeries, 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 x = new D3PAxis({ bus, name: 'hour', domain: new D3PAxisDomain({ bus, domain: [0, 24] }) })
const y = new D3PAxis({ bus, name: 'temperature', domain: new D3PAxisDomain({ bus, domain: [0, 30] }), position: 'left' })
// the domain of an axis is the range of values it shows
const hours = new D3PAxisDomain({ bus, domain: [0, 24] })
const degrees = new D3PAxisDomain({ bus, domain: [0, 30] })
// an auto domain fits the domains to the data of the series it is linked to
const auto = new D3PAutoDomain({ bus })
auto.linkSeries(temperature, [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 line = new D3PLineRenderer({ bus, series: temperature })

const objects = [x, y, line]

const chart = new D3PChart({ container, bus })
objects.forEach((o) => o.addToChart(chart))

What changed:

  • The domains are variables. hours and degrees are D3PAxisDomain objects, given to the axes. A domain is a plain object that holds a range, nothing else: not a scale, not pixels.
  • An auto domain fits them to the data. D3PAutoDomain.linkSeries(series, [xDomain, yDomain]) sets the domains to the extent of the series, and keeps them up to date when the data changes. Leave it out to keep the ranges you wrote.
  • The axes are set with props. tickCount is the number of ticks wanted, tickFormat writes a value, grid draws the grid lines, displayName is the name shown in the cursor labels and the titles.

Domains are shared and writable ​

The domain of an axis can be read and written: hours.domain = [6, 18] shows only the day, and the axis follows. This is all the zoom and the pan do (they write domains), and it is how charts stay in step: two axes built on the same D3PAxisDomain always show the same range, even on two different charts.

Kinds of scale ​

scaleType on the axis chooses the scale.

scaleTypeForDomain
linear (default)numbers[min, max]
time, utcdates[start, end] as Dates
lognumbers over several orders of magnitude[min, max], both above 0
symlogthe same, with negative values and zero[min, max]
pow, sqrtareas and other power laws[min, max]
bandcategories drawn as bands (bars)the list of categories
pointcategories drawn as points (a line over categories)the list of categories

scaleOptions passes options to the scale: the base of a log scale, the exponent of a pow one.