Creating .mbtiles DB for iOS MapBox from hi-res map image

Sometimes I'm surprised how great software/tools/apps you can get and use for free. One of them is MapBox iOS SDK made by MapBox. It's great for custom online maps with hosting on their own severs and they also have a very nice OS X app TileMill.

My use case was slightly different. I wanted to make an offline map from a single high resolution image. MapBox Example for iOS comes with exactly the same type of map (offline tiled map) but I spent around 10 hours trying to figure out how to generate my own .mbtiles map, dispute the fact that .mbites file format is well documented and actually very simple SQLite database. Also I haven't found any tutorial on generating .mbites. if you did, please leave me a commend below.

For all image processing I'm using ImageMagick library. You can install it via eg. Mac Ports.

Creating tiles from a large image

So, I have one big image of Chernarus peninsula and I want to convert it into tiles of 256x256 (you can download full size image and try it on your own).

Download full size image 2048x2048 (1.5 MB)
NOTE: You won't find this place on a real map. It's a fictional place from DayZ mod for Arma II.

The original image is 2048x2048. By the way, it doesn't have to be square, but its much easier because both dimensions should be divisible by 256 for all zoom levels and this is quiet problematic with non-square images.

To split this large image I'm going to use convert command which is part of ImageMagick library.

Also, I need several zoom levels to make smooth transition when user zooms the map so I'm going to make tiles for each of these sizes 2048, 1024, 512 and 256. For simplicity, I'm using just four scales but if you were going to use it in a real application with more zoom levels the technique is the same. In my experience processing images with dimensions around 10000x10000 takes quiet a while and consumes a lot of memory.

So, I made a simple bash script that takes all sizes you specify and first scales the original picture and then splits it into smaller pieces 256x256 each.

This script creates tiles and puts them into correct directories like at this image.

Please notice that tiles are named from tile000.jpg to tile015.jpg, that's 16 tiles in total which is correct for 1024x1024. tile000.jpg is top left tile and tile015.jpg is bottom right tile. This order is very important in the next step.

Just to make it absolutely clear, this is how tiles are scattered to form original 1024x1024 image:

Notice that the coordinate origin tile000.jpg is in top left corner.

Generating .mbtiles DB

.mbtiles is just an SQLite database with two tables as described in its specification. Although this looks very simple I spent several hours trying to get it running! The problem wasn't with generating SQLite database but with its coordinate system as I realized after desperately extracting example control-room-0.2.0.mbtiles from mapbox-ios-example and examining its content.

For this purpose I made this .mbtiles extractor in Python which creates a directory for each zoom level, fills it with images and dumps metadata info as JSON. If you were having some troubles with your .mbtiles DBs you can try to extract them and maybe try to check what's wrong.

Anyway, the problem turned out to be in my coordinate system. Our tiles start at top left corner with tile000.jpg (which is [0;0] position or column = 0, row = 0) and end in bottom right corner. In other words my origin [0;0] is in top left corner.
But .mbtiles and MapBox SDK expects origin in bottom left corner. This important fact isn't mentioned in the spec (maybe I should add an issue on their github page).

Notice that the coordinate origin [0;0] is in bottom left corner.

With that in mind I made another Python script that takes our tiles and puts them into one SQLite database.

Now run it with python create_mbtiles.py chernarus.mbtiles and it generates a single chernarus.mbtiles file which is the SQLite database. You can check it with file chernarus.mbtiles.

You can download my chernarus.mbtiles (2.3 MB) if you don't want to reproduce all the steps described here.

Using offline .mbtiles in MapBox iOS SDK

For the last part I strongly recommend to clone mapbox-ios-example and look at OfflineLayerViewController.m how to create an instance of our offline map. I just copy & pasted it into a new project with a single view controller and made some minor changes:

Also you need to include MabBox SDK in your project. Thats very simple, just drag & drop MapView.xcodeproj to your project (NOTE: you have to close all Xcode windows with MapView.xcodeproj otherwise it will throw some error). I'm not absolutely sure what dependencies you have to include in your project, I just took the same libraries that mapbox-ios-example has so if you're not sure, try look at it.

Now just build and run project in simulator and that's all.

What's next?

I think MapBox has great potential and it doesn't have to be used just to show maps. I believe if you had some high resolution pictures and wanted to browse them in an iOS app, MapBox is a great choice.

I should probably say that I'm not an expert on MapBox nor mapping technologies. There are some things that aren't very clear to me. For example, if you don't include in the .mbtiles database first zoom level with one 256x256 tile, the map somehow breaks that you can't see entire tiled region. I don't know why because iPhone 4 screen size is 960x640 and you never want to zoom out so you can see just one 256x256 tile.

Anyway, if you have never tried or never heard about MapBox you should give it a try because it's really great. If you've seen some nice and creative implementation of MapBox, leave me a comment. I would love to see it.

blog comments powered by Disqus