{"id":2400,"date":"2012-11-17T13:08:48","date_gmt":"2012-11-17T18:08:48","guid":{"rendered":"http:\/\/blogs.terrorware.com\/geoff\/?p=2400"},"modified":"2013-09-23T07:48:12","modified_gmt":"2013-09-23T12:48:12","slug":"offline-map-tiles-in-qgis","status":"publish","type":"post","link":"http:\/\/blogs.terrorware.com\/geoff\/2012\/11\/17\/offline-map-tiles-in-qgis\/","title":{"rendered":"Offline map tiles in QGIS"},"content":{"rendered":"<p><a href=\"http:\/\/www.flickr.com\/photos\/prestonrhea\/8036013621\/\"><img loading=\"lazy\" decoding=\"async\" src=\"https:\/\/live.staticflickr.com\/8309\/8036013621_ec4e1d7384_z.jpg\" alt=\"Participatory Mapping Station\" width=\"640\" height=\"480\" \/><\/a><\/p>\n<p>I was invited to run a station at a DiscoTech event sponsored by the <a title=\"Detroit Digital Justice Coalition\" href=\"http:\/\/detroitdjc.org\/\">Detroit Digital Justice Coalition<\/a> (DDJC). DiscoTechs are community technology fairs, and are a really great model for sharing tech knowledge within and across communities. The DDJC has <a title=\"DiscoTech Zine\" href=\"http:\/\/detroitdjc.org\/2012\/07\/01\/discotech-zine-out-now\/\">released a zine<\/a> with stories and bootstrap practices so other groups can run their own DiscoTech events. While previous events had been held in somewhat central locations, this DiscoTech was held at a community center in the <a title=\"48217 Map\" href=\"https:\/\/maps.google.com\/maps?oe=utf-8&amp;client=firefox-a&amp;q=48217+detroit&amp;ie=UTF-8&amp;hq=&amp;hnear=0x883b33e51c2c8907:0x8390a4c1206bc8c8,Detroit,+MI+48217&amp;gl=us&amp;ei=t8inUPSHAqiU2wWI04HwDg&amp;ved=0CIYBELYD\">48217 neighborhood<\/a> at the edge of Detroit and home to a strong community of environmental justice activists who are part of the DDJC.<\/p>\n<p>However, the recreation center where the event was located didn&#8217;t have Internet access. This posed a bit of a problem for our station, as we wanted to have people both create analog maps of their community using paper and transparency sheets with dry-erase markers, and digital ones. I chose Quantum GIS (QGIS) as the tool to make digital maps, because it could work on my notebook without any Internet connection. I&#8217;ve used the excellent <a title=\"OpenLayers QGIS Plugin\" href=\"https:\/\/github.com\/sourcepole\/qgis-openlayers-plugin\">OpenLayers QGIS Plugin<\/a> to provide slippy map tiles from Open Street Map as a layer in QGIS, but this wouldn&#8217;t work without an Internet connection. My solution was to essentially run a full mapping stack on my notebook computer. This was surprisingly straightforward, and helped me better understand how the pieces of a modern <a title=\"Take Control of Your Maps\" href=\"http:\/\/www.alistapart.com\/articles\/takecontrolofyourmaps\">custom mapping stack<\/a> work together.<\/p>\n<h3>Make your tiles<\/h3>\n<p>First, I had to get the raw data from which to generate my tiles. I grabbed an extract of the Detroit Metro Area from the <a title=\"Metro Extracts\" href=\"http:\/\/metro.teczno.com\/\">OpenStreetMap Metro Extracts page<\/a> since we were just making maps of Detroit and selecting a subset of the map data would save a lot of time when rendering the tiles.<\/p>\n<p>There are lots of ways to make map tiles, but <a title=\"TileMill\" href=\"http:\/\/mapbox.com\/tilemill\/\">TileMill<\/a> is pretty easy to use, and very powerful. MapBox, the makers of TileMill also provide a template project called <a title=\"OSM Bright\" href=\"https:\/\/github.com\/mapbox\/osm-bright\">OSM Bright<\/a>, which they describe as &#8220;a sensible starting point for quickly making beautiful maps in TileMill based on an OpenStreetMap database.&#8221; To get a basic set of tiles for Detroit, I just followed the <a title=\"OSM Bright quickstart\" href=\"http:\/\/mapbox.com\/tilemill\/docs\/guides\/osm-bright-ubuntu-quickstart\/\">OSM Bright quickstart tutorial<\/a> to generate the tiles I needed.<\/p>\n<p>TileMill lets you export your tiles in a number of different formats, but I chose the <a title=\"MBTiles spec\" href=\"http:\/\/mapbox.com\/developers\/mbtiles\/\">MBTiles<\/a> format because I liked the idea of all of my tiles being packaged in a single file. It just seemed more portable in case I wanted to move my project to a separate machine.<\/p>\n<h3>Serve your tiles<\/h3>\n<p>There are also many different ways to serve your custom tiles. <a title=\"Manually building a tile server\" href=\"http:\/\/switch2osm.org\/serving-tiles\/manually-building-a-tile-server\/\">This tutorial<\/a> provides a good rundown of one method of building a server, using Mapnik to generate the tiles. However, I wanted something really lightweight, and something that would use tiles in the MBTiles format. I found <a title=\"TileStache\" href=\"http:\/\/tilestache.org\/\">TileStache<\/a>, which I liked because it was written in Python, had its own built-in webserver, and supported MBTiles. Because it supports a wide variety of datasources, the configuration seemed a bit daunting at first, but was ultimately pretty simple, at least for using an MBTiles tileset.<\/p>\n<p>.<\/p>\n<p>This is what my tilestache.xml looked like:<\/p>\n<pre>&lt;GDAL_WMS&gt;\r\n    &lt;Service name=\"TMS\"&gt;\r\n        &lt;ServerUrl&gt;http:\/\/127.0.0.1:8000\/${z}\/${x}\/${y}.png&lt;\/ServerUrl&gt;\r\n    &lt;\/Service&gt;\r\n    &lt;DataWindow&gt;\r\n        &lt;UpperLeftX&gt;-20037508.34&lt;\/UpperLeftX&gt;\r\n        &lt;UpperLeftY&gt;20037508.34&lt;\/UpperLeftY&gt;\r\n        &lt;LowerRightX&gt;20037508.34&lt;\/LowerRightX&gt;\r\n        &lt;LowerRightY&gt;-20037508.34&lt;\/LowerRightY&gt;\r\n        &lt;TileLevel&gt;18&lt;\/TileLevel&gt;\r\n        &lt;TileCountX&gt;1&lt;\/TileCountX&gt;\r\n        &lt;TileCountY&gt;1&lt;\/TileCountY&gt;\r\n        &lt;YOrigin&gt;top&lt;\/YOrigin&gt;\r\n    &lt;\/DataWindow&gt;\r\n    &lt;Projection&gt;EPSG:900913&lt;\/Projection&gt;\r\n    &lt;BlockSizeX&gt;256&lt;\/BlockSizeX&gt;\r\n    &lt;BlockSizeY&gt;256&lt;\/BlockSizeY&gt;\r\n    &lt;BandsCount&gt;3&lt;\/BandsCount&gt;\r\n    &lt;Cache \/&gt;\r\n&lt;\/GDAL_WMS&gt;<\/pre>\n<p>And this is what my tilestache.cfg (the file that configures the datasource) looked like:<\/p>\n<pre>{\r\n  \"cache\": {\"name\": \"Test\"},\r\n  \"layers\": {\r\n    \"detroit\": {\r\n      \"provider\": {\r\n        \"name\": \"mbtiles\",\r\n        \"tileset\": \"detroit2.mbtiles\"\r\n      }\r\n    }      \r\n  }\r\n}<\/pre>\n<h3>Add your tiles as a layer in QGIS<\/h3>\n<p><strong>Update: <\/strong>I originally hacked the OpenLayers plugin as described below, but Nathan and David Forest pointed out in the comments that there&#8217;s a less hacky way.<\/p>\n<p>GDAL &gt; 1.7.0 supports the Tiled Map Service (TMS) format that we&#8217;re serving using to serve our tiles in Tilestache.\u00a0 You can create a VRT file as described in this blog post, <a title=\"http:\/\/www.3liz.com\/blog\/rldhont\/index.php?post\/2012\/07\/17\/OpenStreetMap-Tiles-in-QGIS\" href=\"http:\/\/www.3liz.com\/blog\/rldhont\/index.php?post\/2012\/07\/17\/OpenStreetMap-Tiles-in-QGIS\">How to display OpenStreetMap data tiles with no plugin inside Qgis<\/a> and add your tiles as a raster layer.<\/p>\n<h3><del>Hack the OpenLayers QGIS Plugin<\/del><\/h3>\n<p><del>The last step is to get the QGIS OpenLayers Plugin to use my local tileserver instead of grabbing them from the OSM tile server over the web. There&#8217;s definitely more elegant ways of doing this, but I needed to get this to work fast, so I simply edited the osm.html file that is part of the plugin. On my system, this file was located at ~\/.qgis\/python\/plugins\/openlayers\/html\/osm.html. I edited the file so that around <a title=\"osm.html\" href=\"https:\/\/github.com\/sourcepole\/qgis-openlayers-plugin\/blob\/master\/openlayers\/html\/osm.html#L33\">line 33 of the file<\/a><\/del><\/p>\n<p><del>, I replaced the tile URL string &#8220;http:\/\/tile.openstreetmap.org\/${z}\/${x}\/${y}.png&#8221; with &#8220;http:\/\/127.0.0.1:8000\/${z}\/${x}\/${y}.png&#8221;, the ServerUrl directive from my tilestache.xml.<\/del><\/p>\n<p><del>After editing this file and starting QGIS, I was able to choose &#8220;Add OpenStreetMap layer&#8221; from the &#8220;Plugins &gt; OpenLayers plugin&#8221; menu in QGIS to add a base layer of my custom tileset.<\/del><\/p>\n","protected":false},"excerpt":{"rendered":"<p>I was invited to run a station at a DiscoTech event sponsored by the Detroit Digital Justice Coalition (DDJC). DiscoTechs are community technology fairs, and are a really great model for sharing tech knowledge within and across communities. The DDJC has released a zine with stories and bootstrap practices so other groups can run their&hellip; <a class=\"more-link\" href=\"http:\/\/blogs.terrorware.com\/geoff\/2012\/11\/17\/offline-map-tiles-in-qgis\/\">Continue reading <span class=\"screen-reader-text\">Offline map tiles in QGIS<\/span><\/a><\/p>\n","protected":false},"author":3,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":false,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[27,5],"tags":[],"class_list":["post-2400","post","type-post","status-publish","format-standard","hentry","category-hacks","category-howto","entry"],"jetpack_publicize_connections":[],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"jetpack_shortlink":"https:\/\/wp.me\/p4wnIz-CI","_links":{"self":[{"href":"http:\/\/blogs.terrorware.com\/geoff\/wp-json\/wp\/v2\/posts\/2400","targetHints":{"allow":["GET"]}}],"collection":[{"href":"http:\/\/blogs.terrorware.com\/geoff\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/blogs.terrorware.com\/geoff\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/blogs.terrorware.com\/geoff\/wp-json\/wp\/v2\/users\/3"}],"replies":[{"embeddable":true,"href":"http:\/\/blogs.terrorware.com\/geoff\/wp-json\/wp\/v2\/comments?post=2400"}],"version-history":[{"count":4,"href":"http:\/\/blogs.terrorware.com\/geoff\/wp-json\/wp\/v2\/posts\/2400\/revisions"}],"predecessor-version":[{"id":2586,"href":"http:\/\/blogs.terrorware.com\/geoff\/wp-json\/wp\/v2\/posts\/2400\/revisions\/2586"}],"wp:attachment":[{"href":"http:\/\/blogs.terrorware.com\/geoff\/wp-json\/wp\/v2\/media?parent=2400"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/blogs.terrorware.com\/geoff\/wp-json\/wp\/v2\/categories?post=2400"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/blogs.terrorware.com\/geoff\/wp-json\/wp\/v2\/tags?post=2400"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}