This is a simple example of how to build a 'navigator' chart using the d3fc brush component. The example chart plots weather data collected by the University of Edinburgh.
The d3fc brush is built using the d3-brush component, but provides a simpler interface. This exampe highlights some of the d3fc-brush features:
xxxxxxxxxx
<!-- include polyfills for custom event and Symbol (for IE) -->
<script src="https://unpkg.com/babel-polyfill@6.26.0/dist/polyfill.js"></script>
<script src="https://unpkg.com/custom-event-polyfill@0.3.0/custom-event-polyfill.js"></script>
<!-- use babel so that we can use arrow functions and other goodness in this block! -->
<script src="https://unpkg.com/babel-standalone@6/babel.min.js"></script>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://unpkg.com/d3fc@13.1.1"></script>
<style>
.plot-area {
overflow: hidden;
}
/* for the navigator chart, hide the y axis, and 'collapse' the chart title
and x-axis label so that they do not occupy any space */
#navigator-chart .y-axis {
visibility: hidden;
}
#navigator-chart .chart-label, #navigator-chart .x-axis-label {
display: none;
}
</style>
<div id='main-chart' style='height: 300px'></div>
<div id='navigator-chart' style='height: 100px'></div>
<script type='text/babel'>
d3.csv('downsample.csv',
(row) => {
// parse the dates and simplify property access
return {
date: new Date(row['date-time']),
temperature: Number(row['surface temperature (C)'])
}
},
(error, data) => {
if (error) {
console.error(error);
}
const yExtent = fc.extentLinear()
.accessors([d => d.temperature])
.pad([0.1, 0.1]);
const xExtent = fc.extentDate()
.accessors([d => d.date]);
// create the scales
const x = d3.scaleTime()
.domain(xExtent(data));
const y = d3.scaleLinear()
.domain(yExtent(data));
// create the data that is bound to the charts. It is a combination of the
// chart data and the brushed / navigator range
var chartData = {
series: data,
// set an initial brushed range
brushedRange: [0.75, 1]
};
const area = fc.seriesSvgArea()
.crossValue(d => d.date)
.mainValue(d => d.temperature)
.baseValue(y.domain()[0])
const line = fc.seriesSvgLine()
.crossValue(d => d.date)
.mainValue(d => d.temperature);
const brush = fc.brushX()
.on('brush', (evt) => {
// if the brush has zero height there is no selection
if (evt.selection) {
// update the bound data based on the new selcetion
chartData.brushedRange = evt.selection;
// update the domain of the main chart to reflect the brush
mainChart.xDomain(evt.xDomain);
render();
}
});
// create a multi series, combining the brush and area
const multi = fc.seriesSvgMulti()
.series([area, brush])
.mapping((data, index, series) => {
switch (series[index]) {
case area:
return data.series;
case brush:
return data.brushedRange;
}
});
// create the two charts
const mainChart = fc.chartSvgCartesian(x, y)
.plotArea(line);
const navigatorChart = fc.chartSvgCartesian(x.copy(), y.copy())
.plotArea(multi);
// set the initial domain for the main chart based on the
// brushed range
var scale = d3.scaleLinear().domain(x.domain());
mainChart.xDomain(chartData.brushedRange.map(scale.invert));
// each time the bruch changes, re-render both charts
const render = () => {
d3.select('#main-chart')
.datum(chartData.series)
.call(mainChart);
d3.select('#navigator-chart')
.datum(chartData)
.call(navigatorChart);
}
render();
});
</script>
https://unpkg.com/babel-polyfill@6.26.0/dist/polyfill.js
https://unpkg.com/custom-event-polyfill@0.3.0/custom-event-polyfill.js
https://unpkg.com/babel-standalone@6/babel.min.js
https://d3js.org/d3.v4.min.js
https://unpkg.com/d3fc@13.1.1