Building an indoor map with Mapbox


One of the first things I was tasked to build at the Met was a map for the Met App. Unfortunately, the Met App was first launched without an interactive map. Given that the Met is an incredibly large and complicated building, it comes as no surprise that users were practically screaming at us to include a map of the museum within the app. In order to quickly add this feature to the app, and to take advantage of an existing pixel coordinate system that we were using for the museum website that mapped gallery locations to our print map, we decided that the best way to move forward and produce an MVP map was to build the map using Mapbox which is built on top of the fantastic Leaflet open source library.

In this blog post, I will walk through some of the steps involved in building an interactive map using a raster image and Mapbox.js.

Generating map tiles

The first thing we have to do is generate map tiles that we can display using Mapbox.js. Mapbox provides us with a number of handy tools to style and customize maps. I used TileMill to generate and upload map tiles to be hosted on Mapbox.

In order to create a map layer with the image file of your map floor plan, the image has to first be converted to a GeoTIFF. This process essentially projects your floor plan image on to the world map to add spatial reference. To get started, we will need to download this:

Once you have installed the GDAL package, you can then add it to your PATH like so:

echo 'export PATH=/Library/Frameworks/GDAL.framework/Programs:$PATH' >> ~/.bash_profile
source ~/.bash_profile

We can now go ahead and use GDAL to translate our image into a GeoTIFF. The projection we will need to use is Google Web Mercator, which can be referenced by the code ‘EPSG:3857’. We will also be specifying the bounds of our output file which represent the western, southern, eastern and northern limits of a web mercator map.

gdal_translate -a_ullr -20037508.34 -20037508.34 20037508.34 20037508.34 -a_srs EPSG:3857 first-floor.png first-floor.tif

The resulting GeoTIFF can then be added as a layer in TileMill. In your TileMill project, click on Add Layer and browse to the generated .tif file. Select 900913 as the SRS projection, then click Save and Style. Once you are happy with your new layer, you can click on Export and then Upload in order to upload this as a layer hosted on Mapbox.

Repeat this process for each floor of your building.

Adding floors to your map

To simulate toggling between different floors of the building, I took advantage of Leaflet’s layer control and added each floor as a base layer. Note that the floor you want to see on first load has to be added to the map along with the layer control.

	'G': L.mapbox.tileLayer('ground-floor-layer-id'),
    '1/M': L.mapbox.tileLayer('first-floor-layer-id').addTo(map),
    '2/3': L.mapbox.tileLayer('second-floor-layer-id')
null, // no overlay layers to add
	position: 'topright',
	collapsed: false

Setting bounds to your map

You will notice that by default, the floor map will keep repeating itself as you pan around similar to the expected functionality of the world map. In order to disable this, we will have to set bounds to the map object.

var southWest = L.latLng(-180,-180),
	northEast = L.latLng(180,180),
	bounds = L.latLngBounds(southWest, northEast);


One additional thing we will need to do is when we create the tile layer like we did in the previous section, we can specify in the options to not load tiles outside the world width.

L.mapbox.tileLayer('ground-floor-layer-id', { noWrap: true });

Projecting pixel coordinates

The final piece is being able to project the pixel coordinate of a location on your floor plan image to a lat/long on the map. Thankfully, Leaflet provides us with a helper method that can do just that!

var latLng = map.unproject(L.point(x,y), zoomLevel);

Once we have the lat/long values, we can proceed with creating and adding markers to the map as usual.


Here are links to articles that helped me get started on this:

Share this on → Twitter Facebook Google+