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
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
This script creates tiles and puts them into correct directories like at this image.
Please notice that tiles are named from
tile015.jpg, that's 16 tiles in total which is correct for
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
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.
.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
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.
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
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.