Smartdown makes it easy to incorporate D3-based diagrams and experiences.
Meanwhile, a cool graphic where the stream is an infinitely generated series of random numbers...
Adapted from Streaming Financial Chart.
smartdown.importCssCode(
`
.bollinger .area {
fill: #9cf;
fill-opacity: 0.5;
}
.bollinger .line {
stroke: #06c;
}
.chart {
height: 200px !important;
}
`);
var target = this.div;
target.classList.add('chart');
// create some test data
const stream = fc.randomFinancial().stream();
const data = stream.take(110);
function renderChart() {
if (target.style.display === 'none' && window.intervalId) {
window.clearInterval(window.intervalId);
return;
}
// add a new datapoint and remove an old one
data.push(stream.next());
data.shift();
const container = d3.select(target);
// Create and apply the bollinger algorithm
const bollingerAlgorithm = fc.indicatorBollingerBands()
.value(function(d) {
return d.close;
});
const bollingerData = bollingerAlgorithm(data);
const mergedData = data.map(function(d, i) {
return Object.assign({}, d, {
bollinger: bollingerData[i]
});
});
// Offset the range to include the full bar for the latest value
const DAY_MS = 1000 * 60 * 60 * 24;
const xTicks = 10;// $('#streaming-chart').width() >= 700 ? 10 : 5;
const xExtent = fc.extentDate()
.accessors([function(d) {
return d.date;
}])
.padUnit('domain')
.pad([DAY_MS * -bollingerAlgorithm.period()(mergedData), DAY_MS]);
// ensure y extent includes the bollinger bands
const yExtent = fc.extentLinear()
.accessors([
function(d) {
return Math.max(d.bollinger.upper, d.high);
},
function(d) {
return Math.min(d.bollinger.lower, d.low);
}
]);
// create a chart
const chart = fc.chartSvgCartesian(
d3.scaleTime(),
d3.scaleLinear()
)
.xDomain(xExtent(mergedData))
.xTicks(xTicks)
.yDomain(yExtent(mergedData))
.chartLabel('Streaming Candlestick');
// Create the gridlines and series
const gridlines = fc.annotationSvgGridline().xTicks(xTicks);
const candlestick = fc.seriesSvgCandlestick();
const bollingerBands = function() {
const area = fc.seriesSvgArea()
.mainValue(function(d) {
return d.bollinger.upper;
})
.baseValue(function(d) {
return d.bollinger.lower;
});
const upperLine = fc.seriesSvgLine()
.mainValue(function(d) {
return d.bollinger.upper;
});
const averageLine = fc.seriesSvgLine()
.mainValue(function(d) {
return d.bollinger.average;
});
const lowerLine = fc.seriesSvgLine()
.mainValue(function(d) {
return d.bollinger.lower;
});
const crossValue = function(d) {
return d.date;
};
area.crossValue(crossValue);
upperLine.crossValue(crossValue);
averageLine.crossValue(crossValue);
lowerLine.crossValue(crossValue);
const bollingerMulti = fc.seriesSvgMulti()
.series([area, upperLine, lowerLine, averageLine])
.decorate(function(g, datum, index) {
g.enter()
.attr('class', function(_, i) {
return 'multi bollinger ' + ['area', 'upper', 'lower', 'average'][i];
});
});
return bollingerMulti;
};
// add them to the chart via a multi-series
const multi = fc.seriesSvgMulti()
.series([gridlines, bollingerBands(), candlestick]);
chart.plotArea(multi);
container
.datum(mergedData)
.call(chart);
}
// re-render the chart every 200ms
renderChart();
if (window.intervalId) {
window.clearInterval(window.intervalId);
}
window.intervalId = setInterval(renderChart, 200);
One of the visualizations available via Smelly London is a Line Chart. I made some slight changes so that it can be rendered in Smartdown:
var renderDiv = this.div;
renderDiv.classList.add('foo');
smartdown.importCssCode(
`
body .foo svg {
font: 10px sans-serif;
background: lightyellow;
}
.foo .axis path,
.foo .axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.foo .x.axis path {
display: none;
}
.foo .line {
fill: none;
stroke: #2091c1;
stroke-width: 1.5px;
}
`);
var margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
// var formatDate = d3.time.format("%d-%b-%y");
var formatDateCarto = d3.timeParse("%Y/%m/%d");
var x = d3.scaleTime()
.range([0, width]);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x);
var yAxis = d3.axisLeft()
.scale(y);
var line = d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.number_of_smells); });
var svg = d3.select(renderDiv).append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// d3.tsv("data/all_records.tsv", type, function(error, data) {
// d3.tsv("data/output/Barnet.tsv", type, function(error, data) {
// d3.tsv("data/output/Barnet.tsv", type, function(error, data) {
// d3.tsv("data/output/Croydon.tsv", type, function(error, data) {
const tsvFile = 'https://rawcdn.githack.com/Smelly-London/Smelly-London/master/charts/data/output/Tower Hamlets.tsv';
d3.tsv(tsvFile, type).then(
function(data) {
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain(d3.extent(data, function(d) { return d.number_of_smells; }));
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Number of smells");
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
})
.catch(function(error) {
throw error;
});
function type(d) {
d.date = formatDateCarto(d.date);
d.number_of_smells = +d.number_of_smells;
return d;
}
Smartdown now incorporates the dc.js Dimensional Charting Libary because, why not?
The following example adapted from Sunburst Cat Example.
this.div.innerHTML =
`
<div
style="overflow-x:auto;height:650px;padding:10px;">
<div id="file_chart"></div>
<div id="type_chart"></div>
</div>
`;
var fileChart = dc.sunburstChart("#file_chart");
var typeChart = dc.pieChart("#type_chart");
d3.tsv('https://raw.githubusercontent.com/dc-js/dc.js/develop/web-src/examples/cat.tsv').then(function (cats) {
var ndx = crossfilter(cats);
var picturesDimension = ndx.dimension(function (d) {
return d.file.split('/');
});
var picturesGroup = picturesDimension.group().reduceSum(function (d) {
return d.size;
});
var typeDimension = ndx.dimension(function (d) {
return d.type;
});
var typeGroup = typeDimension.group().reduceSum(function (d) {
return d.size;
});
// d3.schemeCategory20b has been removed from D3v5
var d3SchemeCategory20b = [
'#393b79','#5254a3','#6b6ecf','#9c9ede','#637939',
'#8ca252','#b5cf6b','#cedb9c','#8c6d31','#bd9e39',
'#e7ba52','#e7cb94','#843c39','#ad494a','#d6616b',
'#e7969c','#7b4173','#a55194','#ce6dbd','#de9ed6'
];
fileChart
.width(700)
.height(640)
.dimension(picturesDimension)
.group(picturesGroup)
.colors(d3.scaleOrdinal(d3SchemeCategory20b))
.legend(dc.legend());
typeChart
.width(200)
.height(200)
.innerRadius(50)
.dimension(typeDimension)
.group(typeGroup);
dc.renderAll();
});
Back to Home