If you are working on a web map it is difficult to see what changes a line of CartoCSS can cause. With visual regression testing you can create a diff between versions and ensure quality.

At Geometa Lab we are currently building Mapbox Streets compatible vector tiles of the entire planet for custom styling with Mapbox Studio Classic. We want to make sure that even though you are using our vector tiles, you get the same end results when you switch out the source in Mapbox Studio Classic styles such as OSM Bright 2.

Visual Regression Testing Example

Assume we apply a few changes to the OSM Bright 2 style. We change the labels to German, make the forests a bit darker and greener and change the width of main roads at lower zoom levels.

With subtle changes it is difficult to detect what actually happened and what impact the changes have. LeafletJS creator Vladimir Agafonkin recently open sourced the pixelmatch library for pixel-level image comparison for visual regression tests. Now we generate the diff between the two images.

pixelmatch image_v1.png image_v2.png image_diff.png 0.005 1

We can also generate an animated GIF visualizing the changes using the all mighty ImageMagick.

convert image_v1.png image_v2.png -gravity south -set delay 100 image_diff.gif

Diff Raster Map

With tilelive we are now able to generate raster maps so that one can interactively browse the map diffs as web maps.

GIF Animation

Browse animated GIFs where the different tiles are the GIF frames.

### Visual Diff

Browse the visual difference between the generated tiles.

Generate Raster Map

Install the necessary tilelive packages.

npm install -g tl tilelive-mapbox tilelive-file tilejson mbtiles

Export the Mapbox API access token.

epxort MAPBOX_ACCESS_TOKEN = "pk.eyJ1IjoibW9yZ..."

Now copy a section of your existing map v1 to disk, in my case it is Zurich.

tl copy -z 6 -Z 14 -b "8.4039 47.3137 8.6531 47.4578" \ mapbox:///morgenkaffee.fab6dc76 file://./tiles_v1

You can also copy from a tilejson source using node-tilejson.

After you made the changes to your map copy the changed tiles v2 .

tl copy -z 6 -Z 14 -b "8.4039 47.3137 8.6531 47.4578" \ mapbox:///morgenkaffee.9c069ced file://./tiles_v2

Create the Diff

Now we need to compare the changes between all tiles in the tiles_v1 folder with the tiles from tiles_v2 . We loop through the folder structures of the two folders and execute the pixelmatch and ImageMagick commands.

Create the bash file create_diffs.sh and make it executable.

#!/bin/bash set -o errexit set -o pipefail set -o nounset readonly PROGNAME = $( basename $0 ) readonly CWD = " $( cd " $( dirname " ${ BASH_SOURCE [0] } " ) " && pwd ) " readonly DIFF_DIR = " $CWD /diff" readonly GIF_DIR = " $CWD /gif" readonly THRESHOLD = ${ THRESHOLD :- 0 .005 } readonly ANTIALIASING = ${ ANTIALIASING :- 1 } if [ "$#" -ne 2 ] ; then echo "Usage: $PROGNAME <tile_folder_1> <tile_folder_2>" exit 1 fi readonly DIR_1 = $1 readonly DIR_2 = $2 function create_diffs () { mkdir -p $DIFF_DIR mkdir -p $GIF_DIR # Metadata is needed to recreate a map out of the diffed tiles cp " $DIR_1 /metadata.json" " $DIFF_DIR " cp " $DIR_1 /metadata.json" " $GIF_DIR " local z_folder for z_folder in $DIR_1 / * / ; do local x_folder for x_folder in $z_folder * / ; do local y_file for y_file in $x_folder * .png ; do echo $y_file local y_name = $( basename " $y_file " ) local y_basename = ${ y_name %.* } local z_name = $( basename " $z_folder " ) local x_name = $( basename " $x_folder " ) local src = " $y_file " local dst = " $DIR_2 / $z_name / $x_name / $y_name " local diff_output = " $DIFF_DIR / $z_name / $x_name / $y_name " local gif_output = " $GIF_DIR / $z_name / $x_name / $y_basename " .gif mkdir -p " $DIFF_DIR / $z_name / $x_name " mkdir -p " $GIF_DIR / $z_name / $x_name " pixelmatch " $src " " $dst " " $diff_output " " $THRESHOLD " " $ANTIALIASING " convert " $src " " $dst " -gravity south -set delay 100 " $gif_output " # Trick mbtiles into using GIFs as PNGs mv " $gif_output " " $GIF_DIR / $z_name / $x_name / $y_basename " .png done done done } create_diffs

You can now create diffs between two tile folders.

./create_diffs.sh tiles_v1 tiles_v2

You can fine tune the image comparison threshold and the antialising pixels with env vars.

THRESHOLD = 0.01 ANTIALIASING = 2 ./create_diffs.sh tiles_v1 tiles_v2

Copy Raster Tiles

Now we can create a new raster map out of the diff tiles. Copy the tiles into MBTiles.

tl copy file://./diff mbtiles://./diffs.mbtiles

And for the GIFs as well.

tl copy file://./gif mbtiles://./gifs.mbtiles

Now you can serve the MBTiles yourself with tileserver-php or upload it to Mapbox.

Upload to Mapbox

Upload your raster MBTiles to Mapbox. You can use the mapbox-upload script to upload the MBTiles programmatically. The raster map should now appear in the data section and you can browse the diffs.

You can preview the raster MBTiles in your browser and look through all the changes you made on all zoom levels.

Conclusion

Ensuring quality of your maps is not that hard and tools like tilelive make it really easy to extract and compare tiles. For greater benefit you should include visual regression into your CI workflow.