I had no idea how to make custom maps, so I learnt by doing. You should too.

Lately, I’ve been making an app called Taxonomy. It’s designed to make your taxi experience better- a traveling companion, if you will. Going by the logic that a lot of people (in New York, at least) only really travel in taxis when out at night, I decided that it ought to have a dark colour scheme. Not morbid dark, but, y’know, “mischievous things happen at night” dark. I mostly succeeded- styling my logo, headers and buttons in a dark monochrome with some bright yellow highlights (it is a taxi app, after all), but every time I wanted to show the user’s current location this ridiculously bright Google Map showed up and ruined my carefully cultivated style. Something had to be done.

I played around with the styling options Google Maps now provides. It’s powerful, but it still wasn’t enough. The fonts were wrong, the options were difficult to tweak, and- as I was quickly discovering- the Google Maps API doesn’t even perform that well on mobile devices. I remembered back to a Hacker News post I’d seen some weeks earlier about TileMill, and the beautiful maps I’d seen created with it. Now, I’m just a web developer- I’ve done enough projects to know what latitude and longitude are, but that’s about the extent of my knowledge. Despite that, I decided to dive in and give it a try. How hard could it be?

Very hard. Also, very easy.

TileMill really is a joy to use. The installation (on my Ubuntu VM) was a simple sudo apt-get install, and the front-end is entirely web-based. Within five minutes I had it running, and was panning and zooming my way around a map of the world. But before I could get started on my Taxonomy maps, I needed some data. And that’s where it started to get a little complex.

OSMs and SHPs and DBFs, Oh my

First off: data sources. If you’re doing what I’m doing and making a roadmap, OpenStreetMap is your best bet (I actually used NYC government data for parts of mine, but it would take about five blog posts to cover that). I didn’t want to download the entire planet from OSM (all uncompressed 250GB of it), but luckily there are numerous sites out that that serve up pre-chopped, pre-compressed datasets for you to download. For me, Teczno served very well- it had both a SHP file of the New York coastline, and an OSM file of all the roads contained within.

The SHP is imported very easily, but the OSM has to be converted using a tool named Quantum GIS, which has an OSM plugin allowing to import OSM data, then export a shapefile.

Back to the mill

So far, so complex. But now we’re back in the cozy arms of TileMill, and everything feels a little easier again. Once you’ve imported all your layers, you just need to write some CSS. Well, not exactly- TileMill uses a language called Carto, which is syntactically identical to CSS (or, more like LESS/SCSS as it allows nesting), but uses different properties. You refer to layers by id or class, both specified on import.

OSM labels roads by category, allowing you to use different widths for different road types. For example:

#osmroads[highway='residential'] {line-width:5;}
#osmroads[highway='primary'] {line-width:10;}

Should you want to look through the road types, you can click the magnifying glass icon on the layer selector to open up a data view, like so:

Sadly, there’s no way to click on a road on the map and see the data on it- exasperating, as there are more road categories than you think, and it’s difficult to track them down. One fun solution I did find was uploading the dataset to the always amazing CartoDB (their developer Javier Torre is always on hand to give tips and advice) which lets you click on individual roads and see the associated data.

Say goodbye to your life

This is an awkward part of the blog, because it’s the part where I tell you that I’ve endangered your life. Put simply, tweaking these maps is addictive. You’re literally never done. Some of it is because the language is still a little fiddly- for instance, there’s no variable for the current zoom level, so if you want to vary widths you have to make multiple rules like:

#osmroads[zoom=10] {line-width:2;}
#osmroads[zoom=12] {line-width:4;}

and so on, and so forth. But it’s mostly because making maps is an art, and if you’re anything like me, you’re nowhere near the master of it. So every little tweak you make is an exploration in map design- what works, what doesn’t, when it’s too busy, when it’s too empty… TileMill is an enabler- it makes the tweaks so easy (and reflects them instantly in the preview) that you’ll probably never stop. Don’t say I didn’t warn you. Luckily for me, I was working to a deadline, so I had to stop. I’m not perfectly happy with the tiles I’ve got, but they’re good enough for the v1.0 of Taxonomy.

Putting it all together

So, once you’re happy with your maps, you need to export them. Get ready for a long wait- tile crunching takes time. To produce my set of NYC tiles took around 11 hours- multiply that by two because I had to make a second set for iPhone retina displays. It’s not a killer, but it does mean that you can’t get instant results- prepare accordingly.

Once all my tiles were exported into the TileMill .mbtiles format, I installed its stripped-down server counterpart, TileStream, on my Amazon EC2 server. With a CloudFront CDN instance in front of the server, it makes for a great distribution method- much easier than putting and deleting hundreds and thousands of images on S3. It’s worth pointing out that the makers of both these products, MapBox, also provide some very hassle-free hosting, should you be interested.

For the final integration into my app, I used leaflet.js in a web view. I was astonished at just how good Leaflet is- the Google Maps API had led me to believe that HTML5 maps would never match their native counterparts, but that’s not true. It’s allowed me to make Taxonomy 95% HTML5-based (just the nav bar components are native), and provides me with an extremely simple path to an Android port in the near future.

All done

If there is a conclusion to this post it’s that if you’ve ever considered making your own custom maps and are unsure- you need to try it out. I think they’ve been a major value-add for Taxonomy, and I’ve enjoyed every minute of working on them. If your site/app operates worldwide the time and disk space involved in crunching the tiles (my retina version of just NYC is 1.7GB) might prove prohibitive, but otherwise you should give it a shot.

If you want to take a virtual tour around New York using my maps, take a browse in the embedded map below. Discussion is welcome on the Hacker News post and Reddit post.

  • Peter

    Alastair, thx for the great post.

    What framework did you use for routing?