D3.js

Updated: 2020-09-16

SVG vs Canvas

Common

const width = 960,
  height = 600;

const scale = height / 4;

const projection = d3.geoMercator()
  .translate([width / 2, height / 2])
  .scale(scale);

const data = ...; // geojson or topojson

SVG

const path = d3.geoPath().projection(projection);

const map = d3
  .select("#map")
  .append("svg")
  .attr("width", width)
  .attr("height", height);

map.append("path").datum(data).attr("class", "land").attr("d", path);

Canvas

const path = d3.geoPath().projection(projection).context(context);

const canvas = d3
  .select("#map")
  .append("canvas")
  .attr("width", width)
  .attr("height", height);

const context = canvas.node().getContext("2d");

context.beginPath();
path(data);
context.fillStyle = "#DDD";
context.fill();
context.closePath();

D3 Scales

D3 scales can be use not only for data viz

  • Domain: input
  • Range: output

Scale map from input(domain) to output(range)

  • Time: d3.time.scale()
  • Linear: d3.scale.linear()
  • Log: d3.scale.log()

E.g.

var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);

x.domain(
  d3.extent(data, function (d) {
    return d.date;
  })
);
y.domain([
  0,
  d3.max(data, function (d) {
    return d.close;
  }),
]);

Update Axis

svg.selectAll("g .y.axis").call(yAxis);

Remove

d3.select("svg").remove();

Date Format

To Create formats:

var rawFormat = d3.time.format("%Y%m%d%H%M");
var newFormat = d3.time.format("%Y-%m-%d  %H:%M");

To parse the data:

// timestamp is a string like "201809010805", this returns a Date object
var date = rawFormat.parse(timestamp);

To convert a Date object to string

// return a string like "2018-09-01  08:05"
var str = newFormat(date);

https://github.com/d3/d3/blob/master/API.md#time-formats-d3-time-format

Add Grid Lines

// function to generate axis(the axis is essentially a function)
function getYAxis() {
  return d3.svg.axis().scale(y).orient("left");
}

// Add initial Y Axis with ticks
svg.append("g").attr("class", "y axis").call(getYAxis());

// Add initial grid line(dummy Y Axis)
svg.append("g").attr("class", "grid-line").call(
  getYAxis()
    .tickFormat("") // Hide tick text
    .tickSize(-width, 0, 0) // Set tick width so it expands all the way to the right
);

function updateChart(data) {
  svg.selectAll("g .y.axis").call(getYAxis());

  svg
    .selectAll("g .grid-line")
    .call(getYAxis().tickFormat("").tickSize(-width, 0, 0));
}

Geo

const line = {
  type: "Feature",
  geometry: {
    type: "LineString",
    coordinates: [
      [-122.375, 37.618889],
      [-74.168611, 40.6925],
    ],
  },
  properties: {
    airports: ["SFO", "EWR"],
  },
};

To draw the cirle:

const circle = d3.geoCircle().center(line.geometry.coordinates[0]).radius(1.5);
context.beginPath();
geoGenerator(circle());
context.fillStyle = "red";
context.fill();

To add labels, use the following to style the text:

  • context.font: for font styles
  • context.strokeStyle and context.strokeText: for the stroke of the text
  • context.fillStyle and context.fillText: for the fill of the text
context.font = "bolder 12px monospace";
context.strokeStyle = "white";
context.strokeText("SFO", ...projection(line.geometry.coordinates[0]));
context.fillStyle = "black";
context.fillText("SFO", ...projection(line.geometry.coordinates[0]));

Note: use projection to calculate the right position from coordinates