Workshop Mapping

Introduction

Data

One of the biggest pain points with D3.js is actually loading in your data! It can be extremely frustrating to fight with your computer to even start working. Luckily D3.js as several API calls for common data types including .csv, .txt, and .json. Mapping adds another level of complexity to the data, as we have to deal with projections to correctly display the underlying data. Usually this would entail a .shp (and associated) files to store geographic information. Instead we will use GeoJSON and it's svelte younger brother, TopoJSON.

GeoJSON

GeoJSON is a file format to represent simple geographic features via JavaScript Object Notation. The features are one of four types; points, lines, polygons, and non-spatial attributes. This makes GeoJSON extremely flexible! Unfortunately, the file format encodes the spatial attributes for each object in the file. If there's any lines that overlap, for instance countries sharing borders, the information is doubly encoded.

Here's how we read in a GeoJSON file:

d3.json('path/to/file.json', function(data) { // GeoJSON stores it's objects within a 'features' category var features = data.features; // ... mapping code below });

TopoJSON

TopoJSON is an extension built on top of GeoJSON. It solves the encoding problem by storing all of the geometry and pointing the file objects only to it's subset of features. This makes the files easier to move around the web, but adds one more step for us to be able to convert to a format that we can use.

Here's how we read in a TopoJSON file:

d3.json('path/to/file.json', function(data) { // Use the topojson.js library to extract the features // Note that gson value below is equivalent to the 'data' variable // we read in the prior code snippet. var gjson = topojson.feature(data, data.objects.countries); // Our extra step is done, we can proceed as normal var features = gjson.features; // ... mapping code below });

Projections

How to set projections.

// D3.js includes common geographic projections in their API // Usually you can speed up the process and use an existing function var projection_albers = d3.geoAlbersUsa(); var projection_mercator = d3.geoMercator(); // Custom projections // you can either specify your projection before reading it in var projection_specify = d3.geoTransverseMercator() .rotate([x, y]) .precision(x) .translate() .scale(x); // Or set the projection after reading in the data d3.json('data/' + states_f, function(err, data) { // Note we're fitting the projection to our specifiec width/height! var projection_fit = d3.geoAlbers().fitSize([width, height], data); // ... mapping code below });

Paths

Ultimately we will need to display the information! Most of the overhead of setting dimensions, adding svg's, and setting scales is done. D3 uses a function d3.geoPath() to draw geographic lines. This differs from a regular SVG line in that uses a geographic projection to scale latitude and longitude to X, Y coordinates.

var margin = {top: 10, right: 10, bottom: 10, left: 10}, width = 480 - margin.left - margin.right, height = 250 - margin.top - margin.bottom; var svg = d3.select('#pathExample').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.json('data/us-states.json', function(err, data) { if (err) throw err; // Set geographic variables var geo_projection = d3.geoAlbersUsa().fitSize([width, height], data); var geo_path = d3.geoPath().pointRadius(2).projection(geo_projection); // GeoJSON stores it's objects within a 'features' category var counties = data.features; /* D3-SELECTION svg_temp: Using our svg, create empty selections for each county .data(): Use the GeoJSON features list .enter().append('path'): // Add a 'path' element .attr('class'): Add the class, fufilling the selection .attr('d'): Generate a path using each feature's geometry */ svg_temp.selectAll('.county') .data(counties) .enter().append('path') .attr('class', 'county') .attr('d', geo_path); });