Building tilesets with tilemill and OSM data

The problem of creating tiles seems to be really simple: You have the OSM data in one end, which can be downloaded from here, and the .png tiles in the other. In the middle there should be something that reads that data, applies some templates or description, and generates the tiles. But life is never so simple.

mapnik cannot read any of OSM's exported files (XML or .pbf), but only from a SQLite or PostgreSQL/GIS database; and we can only conver to the latter either with imposm or osm2pgsql, so that road we go. It's mostly a matter of following TileMill's page about using OSM's data (and some template called osm-bright). For Debian sid you follow its instructions for Ubuntu Oneiric Ocelot.

Importing the data should be as simple as:

mdione@mustang:~/src/projects/osm$ sudo su -c "osm2pgsql --database osm --input-reader pbf --verbose --create $(pwd)/france.osm.pbf" postgres
[...]
Unable to open /home/mdione/src/projects/osm/data/france.osm.pbf

No error message. strace gives us a clue:

open("/home/mdione/src/projects/osm/data/france.osm.pbf", O_RDONLY) = -1 EOVERFLOW (Value too large for defined data type)

A little bit cryptic, but basically it says the file is too large. So I downloaded only the region where I live (for some countries there are individual files) and after some cache tweaking:

mdione@mustang:~/src/projects/osm/data$ sudo su -c "osm2pgsql --database osm --input-reader pbf --verbose --cache $((1024+512)) --create $(pwd)/provence-alpes-cote-d-azur.osm.pbf" postgres

This time I got it right. Some numbers so you have an idea how much time and space this takes:

Input file size: 184054kB
Processing: Node(19153k 832.8k/s) Way(3019k 23.59k/s) Relation(9110 112.47/s)  parse time: 233s
node cache: stored: 19153339(100.00%), storage efficiency: 16.41% (dense blocks: 113981, sparse nodes: 0), hit rate: 0.00%
Osm2pgsql took 2125s overall
Final DB size: ~1GiB

That's some 35 minutes.

Once finished you fire TileMill, create a new project and add a PostGIS layer. It took me some time to figure out what to put in the different fields, even when there is a tutorial for that, but I used these, mostly taken from inspecting the database's schemas:

  • ID and Class: osm-roads
  • Connection: dbname=osm host=localhost user=postgres
  • Table or subquery: planet_osm_roads
  • Unique key field: osm_id
  • Geometry field: way
  • The rest: default

And then add the following to the style.mss which you can edit in the right box of TileMill:

#osm-roads {
  ::outline {
    line-color: #7f0000;
    line-width: 2;
    line-join: round;
  }
}

The result is kind of dissapointing: first, you have to specify more in the "Table or subquery" field, because the data actually is not only some kind of roads but also borders. I used (select * from planet_osm_roads where highway!='') as foo for all roads and (select * from planet_osm_roads where boundary='administrative') as foo for borders as a first attempt to be more selective. Second, as I said, it's only the main roads (down to secondary in OSM's terms, list which by the way seems to grow everytime I see it), but nothing smaller. But as a start I think is enough.