Tell a Story by Map

Yi Xu
4 min readJul 23, 2018

--

As mapping technologies become more and more powerful, maps are not only used for navigation or simply telling you how the world looks like. We can use maps to create cool data visualizations to power the business intelligence. Now, we can even use maps to tell stories.

During Russia World Cup 2018, Mapbox initiated a campaign called World Map Cup. In round 3, I learned how to create an interactive story map. I created a story map for Iceland because it was their first time to enter the World Cup Finals.

How I built it

Choose a style

First, we need an Iceland style basemap. I used Cartogram, a drag-and-drop tool to create a custom map in seconds.

Use an image of the Iceland team to create a basemap

Get the starter code

Rafa shared a tutorial about how to create an interactive story map. He shared a piece of starter code to help us quickly start to work on the project.

Add a 3D choropleth map

3D choropleth map

On Wikipedia, there is a list of countries by population. I manually created a geojson and added the population of all Russia World Cup qualifiers. To create a choropleth map, in the map layer’s paint property, we can set several stops for different colors. I recommend this tool to create the color ramp.

'fill-extrusion-color': {
property: 'population',
type: 'interval',
stops: [
[337780, '#ffffd9'],
[5754356, '#edf8b1'],
[11364372, '#c7e9b4'],
[32885991, '#7fcdbb'],
[46017766, '#41b6c4'],
[66573504, '#1d91c0'],
[127185332, '#225ea8'],
[143964709, '#253494'],
[143964709, '#081d58']
]
}

Then choose a property as the height of the map layer. I divided the population by 50 so that the 3D polygons will not be too high.

'fill-extrusion-height': ['/', ['get', 'population'], 50],
'fill-extrusion-base': 0

Add a chart

Created with Highcharts

I used a library called Hightcharts. It is well documented and has plenty of demos. Sometimes, when comparing data, charts are better than maps. But when you want to compare the data spatially, a map is definitely better.

Create hover effects

In the chapter 2 and 4, I added hover effects to the map layers. I implemented Mapbox’s new expressions and feature-state feature. You can learn how to use Mapbox GLJS expressions here. The feature-state has not been documented yet but you can find a pretty good example here.

When creating the map layer, you need to specify the style when the layer is hovered. Use Mapbox GL JS expressions to do the job.

map.addLayer({
id: 'players',
type: 'circle',
source: 'playerData',
paint: {
'circle-color': '#fff',
'circle-stroke-width': 1,
'circle-stroke-color': '#000',
'circle-radius': ['case',
['boolean', ['feature-state', 'hover'], false],
15,
12
]
}
});

Next, write the hovering function. The key of the hovering function is the hover id. When you create the geojson data source for the map layer, remember to specify a unique id (must be numeric) for every object. With the unique id, map.setFeatureState() can change the feature state of an individual map object instead of the whole layer.

var hoverId = null;// When the user moves their mouse over the players layer, we'll 
// update the feature state for the feature under the mouse.
map.on('mouseenter', 'players', function(e) {
if (e.features.length) {
map.getCanvas().style.cursor = 'pointer';
if (hoverId) {
map.setFeatureState({source: 'playerData', id: hoverId}, { hover: false});
}
hoverId = e.features[0].id;
map.setFeatureState({source: 'playerData', id: hoverId}, { hover: true});
}
});
// Reset layer's feature state when the mouse leaves the layer.
map.on('mouseleave', 'players', function() {
map.getCanvas().style.cursor = '';
if (hoverId) {
map.setFeatureState({source: 'playerData', id: hoverId}, { hover: false});
}
hoverId = null;
});

Animated the marker

marker animation

Learned from Rasagy Sharma’s blog, I was able to create my own animated markers. The key to creating the animation is window.setInterval() function. It will execute the function periodically. In the window.setInterval() function, we need to periodically change the marker’s paint properties (in my case, they are size and opacity).

window.setInterval(function(){
//If the circle is expanded, reduce the size and opacity
//If the circle is not expanded, increase the size and opacity
var size = (expanded)? 10 : 20;
var opacity = (expanded)? 0 : 0.25;
//Change the radius and opacity of the circles
if (map.getLayer('stadium-halo')) {
map.setPaintProperty('stadium-halo', 'circle-radius', size);
map.setPaintProperty('stadium-halo', 'circle-opacity', opacity);
}
//Toggle the value of expanded
expanded = !expanded;
},500);

Summary

These are pretty much the key features of my story map. Thank Mapbox for sharing these amazing mapping technologies. Basically all of the skills I used to create this map are learned from Mapbox’s blogs, documentation, and examples. Also, in this project, I feel like I was working as a filmmaker. It was really fun to prepare the story, look for data and manipulate the map camera.

You can play the interactive map here. Check out the full source code, and hit me up on Twitter if you have any questions!

Happy Mapping y’all!

--

--