Skip to content

Latest commit

 

History

History
336 lines (263 loc) · 8.15 KB

D3.md

File metadata and controls

336 lines (263 loc) · 8.15 KB

D3

Smartdown makes it easy to incorporate D3-based diagrams and experiences.

Streaming Financial Chart

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);

Smelly London Line Graph

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;
}

dc.js

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