Adding offline capabilities for MapView in Expo

In this example we will be implementing the capability for us to use OpenStreetMaps in offline mode in our MapView component.

To implement offline maps in Expo we need the following:

#1 From the map region the user is viewing, figure out all tiles that need to be downloaded for the current zoom level.

#2 Download all these tiles to the filesystem. We will be leveraging the Expo Filesystem api for this.

#3 Toggle to offline mode, loading tiles from the file system instead of using the OSM web server.

Finding the zoom

From my experience it looks like the zoom level of MapView and OpenStreetMap are not exactly equal. From my testing this function gave me the best fit:

Math.round(Math.log(360 / longitudeDelta) / Math.LN2) + 2

Finding all tiles for a region

To find all tiles for a given region we will use the function below. Just give it the mapRegion given by the MapView component and a max and min zoom that you can calculate with the formula above.

It will spit out a array with tiles with the format
{x: number, y: number, z: number} .

Downloading the tiles to the filesystem

To download the files we first need to create the folder structure, for some reason Expo’s filesystem won’t create the directory when using downloadAsync. After the directories have been set up we can download all the tiles to the created directories and we should be ready to go.

The tileGrid argument would be the result from tileGridForRegion in the previous step. In my example app I have set up these constants:
rootFolder = `${FileSystem.documentDirectory}tiles` 
tileServerUrl = http://c.tile.openstreetmap.org

Using the downloaded tiles in MapView

Now we can have a urlTemplate for online and for offline mode, and create a button to toggle between them. If everything worked fine the downloaded tiles should be available using the offlineUrlTemplate.

Gothas

Since the tiles will be stores in the folder structure /tiles/{z}/{x}/{y}.png we must first create the folder z and x before downloading anything there.

Use MapView.UrlTile instead of MapView.LocalTile as the filesystem in Expo is treated as a url.

Improvements

  • Allow user to have multiple packages of stored tiles for different areas.
  • Show progress bar while downloading bigger collections of tiles, can posibly be done with Expo.FileSystem.createDownloadResumable

Example app

Sourcecode on Github

Expo QR code to test the prototype