Comparison of Mapbox GL JS vs D3.js

Mapbox map
D3 map

When you want to create an online-map from your data you have a ton of options available.
When I teach d3 or talk to people what tools they’re using the questions that comes up a lot is: “How do d3 and mapboxGL compare?”.
That is the question that I want to answer on this site.
I’m not only comparing the two libraries on a conceptiual basis but on a code basis as well.
Let’s start with one of the more confusing aspects:

The name

The definition of both libraries are as follow:

Mapbox GL JS is a JavaScript library for interactive, customizable vector maps on the web. It takes map styles that conform to the Mapbox Style Specification, applies them to vector tiles that conform to the Mapbox Vector Tile Specification, and renders them using WebGL.

Mapbox GL JS is part of Mapbox GL which is a cross-platform ecosystem and Mapbox is a company specializing in location data.

D3 (or D3.js) is a JavaScript library for visualizing data using web standards. D3 helps you bring data to life using SVG, Canvas and HTML.

Conceptional

D3.js and Mapbox GL JS (or Mapbox for short for the sake of writing less ;) ) are conceptionally very different. Mapbox is a library to create so-called “Slippy-Maps” and is in good company with Leaflet, OpenLayers and GoogleMaps.

Slippy Map is, in general, a term referring to modern web maps which let you zoom and pan around (the map slips around when you drag the mouse).

This quote from OpenStreetMap explains it very well :)
Although using this definition d3 maps can be slippy maps as well. You can add zoom and pan to them.

A core component of Slippy Maps is that the images should be served as tiles on a grid. Tiling images is an efficient way to browse large amounts of raster and vector map data that would be much too large to render as a single map image.

This explanation from the Planet Developer Resource “Slippy Maps 101” is more technical but brings it more to the point. These maps (used to) load images from a server and tile them to make them faster to load. Nowadays vector tiles have replaced the images, but they still get served as tiles to load and render faster.

Code

Let’s look at some code. Or to be exact the complete code to render above maps. Mapbox map is on the left. The d3 map on the right.

Mapbox

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
var url = "https://gist.githubusercontent.com/milafrerichs/78ef5702db2dc514fc2bed465d58406b/raw/f1366ee2a83a9afb1dd2427e9cbd4cd3db8d87ca/bundeslaender_simplify200.geojson";
mapboxgl.accessToken = 'YOUR_MAPBOX_TOKEN';
var map = new mapboxgl.Map({
  container: 'map',
    style: 'mapbox://styles/mapbox/streets-v11',
    center: [13.79,53.545], 
    zoom: 5
  });
  map.on('load', function () {
    map.addSource('bb', { type: 'geojson', data: url });
    map.addLayer({
      'id': 'berlin',
      'type': 'fill',
      'source': 'bb',
      'paint': {
        'fill-color': '#088',
        'fill-opacity': 0.8
      }
    });
    map.addLayer({
      'id': 'berlin-stroke',
      'type': 'line',
      'source': 'bb',
      'paint': {
        'line-width': 1,
        'line-color': '#000',
      }
    });
});
Open in CodeSandbox

D3

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
var url = "https://gist.githubusercontent.com/milafrerichs/78ef5702db2dc514fc2bed465d58406b/raw/f1366ee2a83a9afb1dd2427e9cbd4cd3db8d87ca/bundeslaender_simplify200.geojson";
d3.json(url).then(function(bb) {
  var bbox = d3.select('#d3-map').node().getBoundingClientRect()
  var width = bbox.width;
  var height = bbox.height;
  var projection = d3.geoEqualEarth();
  projection.fitExtent([[20, 20], [width, height]], bb);
  var geoGenerator = d3.geoPath().projection(projection);
  var svg = d3.select("#d3-map").append('svg')
      .style("width", "100%")
      .style("height", "100%");
  svg.append('g').selectAll('path')
  .data(bb.features)
  .enter()
    .append('path')
    .attr('d', geoGenerator)
    .attr('fill', '#088')
    .attr('stroke', '#000');
});
Open in CodeSandbox

Amount of Code

The first difference you see is that the code for mapbox is almost twice as long as the d3 code. Which is a bit suprising. But the reason is that the styling takes a lot of effort and is formatted in a readable way.

Winner: D3

Ease of Setup

Both are relatively easy to setup. You either download their code and integrate it. Or use a CDN like I did on this page.

Mapbox setup:

You need to include the javascript code and the CSS for mapbox. You need to include the stylesheet to make sure the navigation elements are visible. Furthermore, you need to signup at mapbox.com to get an API token to use it. But that is free, just your email as payment.

1
2
<script src="https://unpkg.com/[email protected]" type="text/javascript"></script>
<link href="https://unpkg.com/[email protected]/dist/mapbox-gl.css" rel="stylesheet" type="text/css">

D3 setup:

1
<script src="https://unpkg.com/[email protected]/dist/d3.min.js" type="text/javascript"></script>

Winner: D3

Basemap

The biggest visible difference is the lack of a basemap for d3. But I made this on purpose. The power of d3 is that it does not include a basemap by default. You want the data to be the center of your map, not the basemap.

This is one of the more important pieces, you choose the libraries based on your needs and d3 and mapbox serve different needs. Mapbox and other basemap-based libraries shine when you need more information than just the data. For example street locations, more labels, etc.

Winner: Mapbox

Projections

The second big difference is the projection of these maps. Mapbox uses Mercator as their default and you cannot change the projection.

In D3 you choose the projection yourself. You can pick from a list of 14 different projections or can create your own. D3 projection Resource

6
var projection = d3.geoEqualEarth();

Winner: D3

Styling

Mapbox styling is done via Mapbox Style Spec while D3 styling is done via normal SVG attributes and CSS.

As you might have noticed we needed to create two layers in mapbox to style the fill as well as the stroke/outline of the feature.
That is one of the many limitations of the style specification. But you can do a lot with the style spec and do not need to think to hard about the implementation.

In d3 you have the power of SVG and CSS to style almost anything. And it is more convenient. In both cases you need to learn how you style elements.

Mapbox:

11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
    map.addLayer({
      'id': 'berlin',
      'type': 'fill',
      'source': 'bb',
      'paint': {
        'fill-color': '#088',
        'fill-opacity': 0.8
      }
    });
    map.addLayer({
      'id': 'berlin-stroke',
      'type': 'line',
      'source': 'bb',
      'paint': {
        'line-width': 1,
        'line-color': '#000',
      }
    });

D3:

In d3 we only need two lines of code.

17
18
  .attr('fill', '#088')
  .attr('stroke', '#000');

Winner: D3

Centering the map

While Mapbox relies on you, the creator, to set the zoom, center and so forth in advance, d3.js allows you to use your data to set the extent.

You can let mapbox figure out the best zoom and center as well, but it is more difficult and I did not include this currently. I might extent this post in the future.

Mapbox

6
7
  center: [13.79,53.545], 
  zoom: 5

D3

7
projection.fitExtent([[20, 20], [width, height]], bb);

Winner: D3

Concepts you need to learn

There are definetely fewer concepts you need to learn or understand to use mapbox.js. You just need to know that there are layers and sources, call the appropriate functions (look at examples & the docs :) ) and read the mapbox style spec and you’re good to go.

With d3, you should know the basics of HTML, learn a little bit of SVG and then know which d3 functions to call (look at examples, the docs).

Winner: mapbox

Conclusion

As I mentioned in the beginngin of this post, both d3 and mapbox.js serve somewhat different purposes. But they do have similar features.

So how do they stack up against each other. Let’s find out.

Mapbox: 2
D3: 5

I declare D3 as the winner of this comparison. But keep in mind, that if you need a basemap, mapbox is probably easier to setup.