Custom offline maps in Android using QGIS

One of my clients produces heritage and tourism apps and I’ve been lucky enough to work on a number of these for him.

Recently he came to me needing some help adding custom/offline mapping support to one of his Android apps. He had a map image he’d licensed and needed it to work largely offline in addition to already implemented mapping using the Google Maps SDK.

This is quite a text heavy article and could use some images. I hope in the next few days to get some time to add them in and refine the text

Over the years I’ve worked with a number of solutions for creating offline maps. A while back I used the Android library osmdroid to display an offline map created in TileMill. TileMill used a OpenStreetMap I had downloaded and installed into a fairly large local PostgreSQL database. It read the data and constructed tiles at various zoom levels and saved them into a MBTiles SQLite database.

As there was already code written to display a map using the Google Maps SDK together with map pins, walking routes, etc I decided to leverage all of that rather than add in a 3rd party mapping library.

Google Maps can display your own map tiles and some code from a GitHub library, with a few tweaks so it worked with the latest Google Maps, got a sample MBTiles database quickly showing.

It took a while but I eventually came up with a solution for creating an MBTiles database from the map my client supplied. TileMill itself is defunct, a fork is actively developed by the wonderful people at Mapbox, however it no longer provides the ability to create MBTiles databases.

The solution I settled on uses the excellent QGIS application. It was slightly easier to get this setup on Windows so I carried out the following in a Windows 10 VM - however it also supports Mac and Linux.

In order to create a georeferenced image (that is an image which is carefully placed on a map so that everything is correctly located) and to generate the MBTiles requires a few plugins:

  • Georeferencer GDAL - this allows you to easily ‘map’ features on the image to an online map to accurately place it within the world.
  • Lat Lon Tools - so that you can enter a latitude/longitude, such as that you may get from the Google Maps website. Without this you either have to locate the right part of world manually or use a coordinate system I’m not familiar with as I don’t often use GIS tools.
  • QMetaTiles - this generates the MBTiles database.
  • TileLayer - for showing map tiles from OpenStreetMap

All of the plugins can be installed from within QGIS (select the Plugins menu, then choose Manage and Install Plugins…) and simply type the plugin name in to find.

Once the plugins are up I did the following:

  1. Create a new project
  2. Use the TileLayer plugin to add the OSM map (Web > TileLayer Plugin > Add Tile Layer…). I created an external layer definition. To do this place a text file in a directory and choose Settings and select the directory. See below for the external layer definition I used.
  3. Use the Lat Lon Tools plugin to navigate the displayed map to the correct location. If this plugin is enabled you’ll find it bottom left.
  4. Bring up the Georeferencer plugin (Raster > Georeferencer > Georeferencer…)
  5. In Georeferencer bring up a new Raster and select your map image.
  6. Use the Add Point tool to tap a feature on your image (such as a road intersection), choose From map canvas and then click the appropriate point on the online map you setup using TileLayer.
  7. Once you’ve add at least 3 reference points on your image choose File > Start Georeferencing. The first time you do this you’ll be asked to define some settings. Select Transformation type Linear, Resampling method Nearest neighbour and Target SRS EPSG: 3857. Select somewhere to save the georeferenced image in the Output raster settings (although we won’t be needing this).
  8. You should now see your image appear on top of the online map. If its not in the correct place go back into the Georeferencer and add some more points or adjust ones you’ve already added.
  9. You will now have two layers showing the Layers Panel, probably called map1 and Mapbox. Turn off the Mapbox layer so that its no longer visible. There should now just be your image surrounded by white space.
  10. Its time to create the MBTiles file! Select Plugins > QMetaTiles > QMetaTiles.
  11. Make sure File is selected and click Browse. Set the Save as type to be MBTiles databases and enter a filename (e.g. map.mbtiles)
  12. Select the extent Layer extent and then the layer name of your image (e.g. map1)
  13. Set your zoom levels
  14. For tiles which look good on high end Android devices set the Tile width to be 512
  15. You can experiment with changing the format between PNG and JPG to see how it affects the quality of the map and size of the database.
  16. Finally click the OK button and after a few minutes you should have your MBTiles database file.

You can now use the database with the android-gmaps-addons code to get the Google Maps SDK to display your custom map.

The external layer definition for the TileLayer Plugin (saved as tilelayer_settings.tsv in its own directory). Make sure tabs are used to separate the fields:

Mapbox Mapbox{z}/{x}/{y}.png?access_token=pk.eyJ1IjoiYXBwc29udGhlbW92ZSIsImEiOiJjaXpzY29pbzcwMDM4MndueTVjNjBobGFqIn0.P_BMNILDJK45j3jUVjKzrw