Using the D3 JavaScript Library
for Interactive Graphs and Maps

D3 for Maps

In many ways, a map is just another data-driven graphic. So, of course D3 can also be used to create maps. We will use a geosjon file to load a map of the same Overijssel municipalities we have used above. The data was created using the export to geojson option in QGIS, such possibilities are available in most GIS software, or from web services such as the one at www.mapshaper.org

Visualising Geographic Data

The map is made using the code in listing 13 below. If you study the code to see how the map was made, you can see we are using techniques very similar to the BarChart examples. You will see only a few D3 functions and methods that we did not use before:
First of all a projection object, in lines 27-31. D3 has a lot of functionality to use geographic data, i.e. data that is expressed as coordinate pairs in longitude and latitude. Here we use the d3.geoMercator() function that is a so-called factory to turn lon-lat coordinate pairs into cartesian screen coordinates, using the Mercator projection mathematics. To specify the parameters, here we use the .center, .scale and .translate methods of this projection object to zoom into the Overijssel area. There are a multitude of available projections, you can look for examples and explanation at the D3 website.

The projection factory is used in turn in another very useful factory method called d3.geoPath(). This code, seen in line 33, takes a collection coordinates and transforms them into an SVG drawing path that creates an SVG object in the HTML DOM (which then is drawn on the webpage). The geoPath() factory is finally employed in line 41, in the statement .attr("d", svgpath) to load, project and draw the coordinates of each municipality from the dataset. Note that the second argument of the .attr() is the svgpath object, which in fact is a function (remember: in javascript everything is an object)...!

The dataset is loaded in line 35, using the function d3.json(). This works exactly as the d3.csv() we used earlier, but now for JSON or geoJSON data. The latter is a special version of the general JavaScript Object Notation (JSON) format. The format is standardised (see geojson.org), but the standard allows for several ways to structure the data, therefore it is sometimes tricky to find the proper objects to address. In the case of our Overijssel data this looks as follows:

As in most GeoJSON files, there is a top-level GeoJSON object with the type FeatureCollection, that has a sub-object with the name features. The value of features is a JSON array, and each element of the array is a Feature object which in turn will have properties, which store the data attributes of the features. In GeoJSON, each feature also has a geometry object, which can be of type Polygon, Line, or Point.

The array of features is similar to the array of data objects we used in the CSV solution, therefore to bind to it we use the line 39: .data(geojson.features).enter().append("path"). The d3.geoPath() function is smart enough to figure out how to extract the needed geometry objects from this array and apply them to create an SVG path...!
To get to the other elements of the data, you can address sub-objects of the features elements, for example you cans use d.properties.population to retrieve the number of inhabitants.

If you use the code above and test it in your browser, you will notice that the map does not really fit nicely in the SVG element (the grey rectangle). Experiment with the projection settings to make the map fit better (as in the figure above).

Create a Thematic Map

Just like in the bar chart example, you can of course use the attribute properties in the data files to create data-driven visualisations using the SVG drawing possibilities. For example, you can create an extra layer on top of the municipalities to draw circles of different sizes, depending on a data property. Thus, you would create a so-called proportional point symbol map, as shown in the Figure below.

You have already used almost all the elements and code parts necessary for you to create such a proportional point map yourself. There is one exception: To place the points, you need an appropriate point geometry, preferably automatically placed inside the municipality polygons. You can, of course, use a GIS software to create such a geometry and save that as GeoJSON data. But this is not necessary, because the D3 library has several useful functions that operate on geometry paths (see d3-geo documentation). The one to use here is path.centroid(), which "returns the projected planar centroid (typically in pixels) for the specified GeoJSON object." This is returned in an array of two coordinates [x,y]. Thus, to retrieve the X-coordinate of the centroid of a municipality, you would use svgpath.centroid(d)[0]...

Create a proportional point symbol map of the number of inhabitants per munipality of Overijssel. Think carefully about the several code parts needed to achieve that, and look at earlier pages to check how you achieved that. To refine your visualisation, experiment with the CSS settings and with the way you calculate the circle radii from the data values. If you are not succesful in creating a fully working map, the solution will be shown below in due time...