Creating Map-based visualization using vector tiles

Zhenan Hong
5 min readOct 14, 2016

--

Why vector tiles?

Traditionally, maps are created from raster tiles(image tiles). Vector tiles are a way to deliver geographic data in small chunks to a browser or other client app. Vector tiles are similar to raster tiles but instead of raster images the data returned is a vector representation of the features in the tile. They contain geometries and metadata — like road names, place names, house numbers — in a compact, structured format.

Vector tiles have two important advantages over fully rendered image tiles:

  • Styling — as vectors, tiles can be styled when requested, allowing for on demand changing styles and programmatically updating
  • Size — vector tiles are really small, enabling global high resolution maps, fast map loads, and efficient caching

A typical vector tile API will look like this

http://{API-host-name}.com/version/{z}/{x}/{y}.mvt

Displaying vector tiles on a map

There are a couple of mapping libraries that support vector tiles, such as MapboxGL, Mapzen and Leaflet.js. Different libraries provide different sets of APIs but the ideas behind the scenes are some what similar. I will use MapboxGL as example in this article to take you through how to create visualization using vector tiles.

  1. Include MapboxGL library in your HTML file and call new mapboxgl.Map() to initiate a map instance. This will get you a basic map showing in the browser.
<!DOCTYPE html>
<html>
<head>
<meta charset='utf-8' />
<title></title>
<meta name='viewport' content='initial-scale=1,maximum-scale=1,user-scalable=no' />
<script src='https://api.tiles.mapbox.com/mapbox-gl-js/v0.25.1/mapbox-gl.js'></script>
<link href='https://api.tiles.mapbox.com/mapbox-gl-js/v0.25.1/mapbox-gl.css' rel='stylesheet' />
<style>
body { margin:0; padding:0; }
#map { position:absolute; top:0; bottom:0; width:100%; }
</style>
</head>
<body>

<div id='map'></div>
<script>
mapboxgl.accessToken = 'pk.eyJ1IjoiZ3VvZXIyMyIsImEiOiJpYmU5MFp3In0.lm0nc3EaP-b1S6bWFFtjmg';
var map = new mapboxgl.Map({
container: 'map', // container id
style: 'mapbox://styles/mapbox/streets-v9', //stylesheet location
center: [-74.50, 40], // starting position
zoom: 9 // starting zoom
});
</script>

</body>
</html>
A basic map

2. Add a layer using vector tile API

map.on('load', function () {
map.addSource('terrain-data', {
type: 'vector',
tiles: ['http://{API-host}.com/version/{z}/{x}/{y}.mvt']
});
map.addLayer({
"id": "visualization-layer",
"type": "fill",
"source": "hex",
"source-layer": "hex",
"paint": {
"fill-color": "yellow",
"fill-outline-color": "rgba(37,37,37,0.5)"
}
});
});

Let’s break down the code above a little bit. map.addSource() is used to specify the type of tiles and the data source. It uses tiles the tell where the maps should load vector tiles from, which is the vector tile API endpoint in this case. map.addLayer() is used to put a layer on top of the base map. Layer is an abstraction to describe how the vector data is going to be rendered on the map. Put it simple, it controls how the data looks like. So all the styling control will happen in the layer object. I will explain more about it later. Now the code you just saw basically render the data as yellow color filled hexagons on the map.

vector tiles rendered in yellow color

Update styles on the fly

Now it comes to the interesting part. Let’s change style of the vector tiles without any request to the backend, which means the update will happen on the front-end and its supper fast and smooth.

map.setPaintProperty(‘hex’, ‘fill-color’, 'green');

With this single line, the hexagons on the map will change to green. Here hex attribute is the name of the layer and other two attributes are self-explanatory.

Data-driven Visualization

One of the benefits of vector tile is that it carries the data that we need to visualize with on itself and we can access these data quickly through APIs. Data are usually stored in vector tile’s properties attribute. By accessing properties on tiles, we can create more versatile data-driven visualizations. And it can be done by modifying just a few lines:

map.addLayer({
"id": "visualization-layer",
"type": "fill",
"source": "hex",
"source-layer": "hex",
"paint": {
"fill-color": {
property: “value”,
stops: [[-140, “#d7191c”], [-120, “#fdae61”], [-115, “#ffffbf”], [-110, “#a6d96a”], [-100, ‘#1a9641’], [0, ‘#1a9641’]],
type: “interval”
},
"fill-outline-color": "rgba(37,37,37,0.5)"
}
});

The above code changes fill-color from a single string representation of color to an object, which provides more detail description on how to display tiles. Stops is the function to describe how to render the tiles according to the property “value”.

data-driven visualization

Querying data from vector tiles

Since the data is already with the tiles on browser, it makes sense to query them directly on front-end instead of a back-end service call.

  1. Query tiles by a point
map.on('click', function(e: any) {
var features = map.queryRenderedFeatures([e.point.x, e.point.y], { layers: [“hex”] });
});

For example, this will return the tiles on the location, where user clicks on the map. To retrieve more information in that tile, simply something like feature.properties.value to access the data in it.

2. Query tiles by an area

var features = map.queryRenderedFeatures({ layers: [“hex”] });

Using the same API, if you don’t specify a point, you can specify a bounding box. If you leave it blank, it will return all the tiles visible on the screen. This is useful when you need to do some statical calculation but don’t want to initiate a backend API call. To the a simple count, you can do

var count = features.length;

and that’s it. whenever the viewpoint changes, for example user drag the map to another location, the features will be re-queried and the count will be re-calculated. Then you can do all sorts of real time data dashboard using this technique.

Conclusion

There are a lot of powerful features provided by vector tiles and how to use it depends on different use cases. Let me know your ideas on how to create the next generation spatial data visualization.

--

--