This article explains how to render a tiled map with TileMill from OpenStreetMap data and make it available on iOS (Android also should be possible) for offline usage. This is achieved by using a combination of PhoneGap with several plugins and Leaflet for showing the map.

iOS running the result

Obtaining OpenStreetMap data

There are several ways to obtain the data from the OpenStreetMap (OSM) project. I recommend the OpenStreetMap data extracts from Geofabrik. They are updated every 24 hours and provide a couple of different formats.

For this purpose I suggest the bziped XML dump (.osm.bz2). The binary Protocol Buffer format (.osm.pbf) is smaller and should also work, but it didn't for me.

Storing the OpenStreetMap dump in a SQLite database

For further processing we'll convert the data to a special SQLite database (with spatial extension), so we can use SQL queries to retrieve the data.

For this step, spatialite-tools are required. On Mac OS X they can be installed running brew install spatialite-tools (Homebrew must be installed).

Now the data can be converted by running this command after extracting the data with e.g. bunzip2:

spatialite_osm_map --osm-path mittelfranken-latest.osm --db-path SpatialiteTest.sqlite

Depending on the size of the dump, this can take a very long time (at least 5 minutes)!

The final SQLite database can be analized with e.g. sqlite3 or better graphical tools like phpLiteAdmin.

phpLiteAdmin showing the SQLite database

Note: The column "Geometry" of some tables may be shown as empty, although there is data (maybe this doesn't work because most tools don't provide the spatial extension).

Rendering the tiles with TileMill

TileMill is an open utility to style and render spatial data. It is damn slow, because Node.js is running under the hood, but the software works and exports the needed tiles in the MBTiles format, which simply is a SQLite database, too.

Importing the SQLite data

The relevant tables containing the lines, polygons and points start with "ln_", "pg_" and "pt_". They contain the shapes and associated data (street names etc.). The simplest way is to import each table as a layer in TileMill by specifying the CartoCSS selector, the SQLite database file and the SRS format. The latter information is stored in the column "ref_sys_name" of the table called "geom_cols_ref_sys". For spatialite databeses this always should be "WGS84".

TileMill Layer

Sadly I didn't find a way to automate this process, so this has to be repeated for each layer manually.

Now the map can be styled using CartoCSS and the selectors for the layers. There is a nice sample theme called OSM Bright which might help.

Exporting

After importing and styling is finished, it is time to export the map as tiles. In order to do so, select "Export" > "MBTiles" in the upper left corner. Make sure you write down the center point, because this will be likely the position shown when your app starts. The most important settings are the region/boundaries and the zoom levels, as they drastically influence the size of the exported MBTiles file. For mobile devices you want to keep it as small as possible to save bandwidth and storage space.

TileMillRendering

Showing the map with PhoneGap and Leaflet

The app simply downloads the MBTiles file to the local storage if it doesn't exist, yet. Then the file is read as a SQLite database and the tiles can be retrieved by using SQL queries.

Installing PhoneGap

To install PhoneGap, follow the official installation instructions.

Required PhoneGap plugins

At least two plugins (File and SQLitePlugin) are required in order to show MBTiles offline with PhoneGap, but then the file already has to be available on the local storage. If you want to download the MBTiles file you need the File Transfer plugin. I also strongly recommend installing the Console plugin which enables PhoneGap to show console.log() messages in the Xcode console.

# Shows debug messages in Xcode
phonegap local plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-console.git
# Enables access to local storage
phonegap local plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-file.git
# Enables file transfers (requires cordova-plugin-file)
phonegap local plugin add https://git-wip-us.apache.org/repos/asf/cordova-plugin-file-transfer.git
# Open SQLite bzw. MBTiles files
phonegap local plugin add https://github.com/lite4cordova/Cordova-SQLitePlugin.git

Please also take a look at the plugin installation guide, because you may need to modify the platform specific settings to enable the plugins (e.g. editing the "www/config.xml" for iOS).

Note: I had problems with missing dependencies when compiling the app after I was reinstalling some plugins. I could solve this issue by manually fixing the "plugins/ios.json" file, because there were plugins listed which were no more installed.

Showing the tiles with Leaflet

The "hardest" task was to load the tiles from the SQLite/MBTiles database instead of a URL. Unfortunately both SQLitePlugin and Leaflet are not documented very well, so first it was not very clear to me how to achive this. But luckily there is this blogpost solving exactly this problem including the source code hosted at GitHub, which was a very good starting point, although it was slightly outdated.

Note: In order to load the MBtiles file from a webserver, you need to add the host to "www/config.xml" otherwise it won't work! See the Domain Whitelist Guide in the official documentation.

Source Code

The source code is available at GitHub. You are free to use it for any project. In return I'd like to hear what you are using it for and maybe you could contribute back to it.