自製ベクトルタイルをS3でホストしてGitHub Pagesから公開するまで
国土数値情報(バス停留所データ)ベクトルタイルの作成
国土数値情報(バス停留所データ)ベクトルタイルを作成するために、GeoRubyによるShapefileの文字コードのShapefileからUTF-8への変換(Late 2013) - 世界の測量 で準備したUTF-8文字コードのShapefileデータを、vagrant-webmapsに入っているtilestache-seed.pyを使ってGeoJSON Tilesに変換した。コマンドラインオプションは、次のRakefileに記載している。
task :default do sh "tilestache-seed.py -b 34.710641 134.877354 35.769141 135.958849 -c tilestache.cfg -l point_test 12 13 14 15 16" end
また、Rakefileが参照しているtilestache.cfgファイルの内容は次の通り。
{"cache": { "name": "Disk", "path": "/vagrant", "dirs": "portable", "gzip": [] }, "layers": { "point_test": { "provider": {"name": "vector", "driver": "ESRI Shapefile", "verbose": true, "parameters": { "file": "/vagrant/point_test/src/P11-10_26-jgd-g_BusStop_utf8.shp"}, "properties": ["P11_001"] } } } }
ズームレベル13,14,15あたりまではそれなりに現実的な計算時間で手に入る。しかし、ズームレベル16を入手するために必要な時間は馬鹿らしいほど長い。この解決方法としては、ベクトルタイル作成アルゴリズムを根本的にMapReduce式に改善する*1か、クライアントサイドでの提示に overzooming の技術を導入すべきである。overzooming の導入を、将来のエントリで紹介することになるだろう。
タイルが出来た段階でのローカルホストでのテスト
タイルが出来た段階でのローカルホストでのテストには、Sinatraを使った。次のようなRakefileに必要な処理を埋め込んだ。なぜわざわざSinatraを使うかというと、file: スキームURLに対しては、ウェブブラウザがCORSせず、結果として file: スキームの上では、ベクトルタイルが表示されないからである。
require 'rubygems' require 'sinatra/base' class App < Sinatra::Base get '/' do File.open('index.html').read end get '/leaflet-hash.js' do content_type "text/javascript" File.open('leaflet-hash.js').read end get '/TileLayer.GeoJSON.js' do content_type "text/javascript" File.open('TileLayer.GeoJSON.js').read end get '/*/*/*.geojson' do headers({"Access-Control-Allow-Origin" => "*"}) path = "#{params[:splat].join('/')}.geojson" if File.exist?(path) File.open(path).read else "{}" end end end task :default do App.run! end
この段階でのスクリーンショットが次である。
CORS設定onでAWS S3にアップロードする
完成したベクトルタイルのうち、ズームレベル13,14,15のものをふつうにS3にアップロードした。スタティックなウェブサイトとして使えるように設定しているbucketに置いたので、http://www.handygeospatial.info/2013/12/27/{z}/{x}/{y}.geojsonという名前で参照できる状態になっている。ここで留意すべきなのは、GeoJSONタイルデータはCORS設定して置いておかないと、原因が分かりにくいエラーになやまされるということ。
AWS S3のCORS設定については、Enabling Cross-Origin Resource Sharing - Amazon Simple Storage Service に解説されている。具体的には、手元のS3 Bucketでは、次のように設定した。
ウェブ地図サイトをGitHub Pagesアップロード
ウェブ地図サイト(下記index.html及び各配布元から頂いたTileLayer.GeoJSON.js及びleaflet-has.jsの写し)をGitHub Pagesにアップロードした。
<!doctype html> <html> <head> <meta charset='UTF-8'> <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no"> <meta name="apple-mobile-web-app-capable" content="yes"/> <meta name="apple-mobile-web-app-status-bar-style" content="black-translucent" /> <link rel="apple-touch-icon" href="https://si0.twimg.com/profile_images/666603054/r.png"/> <title>vectiles from tilestache</title> <link rel='stylesheet' href='http://leafletjs.com/dist/leaflet.css'> <script src='http://leafletjs.com/dist/leaflet.js'></script> <script src="http://ajax.googleapis.com/ajax/libs/jquery/1.9.1/jquery.min.js"></script> <script src='TileLayer.GeoJSON.js'></script> <script src='leaflet-hash.js'></script> <style> html, body, #mapdiv {margin: 0; padding: 0; width: 100%; height: 100%;} </style> </head> <body> <div id='mapdiv'/> <script> icon = L.icon({iconUrl: 'http://handygeospatial.github.io/mapsites/2013/12/13/maki/bus-24.png'}); map = new L.Map('mapdiv', {zoom: 13, center: [35.0249, 135.7658]}); hash = new L.Hash(map); map.addLayer(new L.TileLayer( 'http://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {attribution: '地理院タイル'})); map.addLayer(new L.TileLayer.GeoJSON( 'http://www.handygeospatial.info/2013/12/27/{z}/{x}/{y}.geojson', {attribution: '国土数値情報(バス停留所データ) 国土交通省', minZoom: 12, maxZoom: 15}, {style: { // not used "clickable": true, "color": "#F0D", "fillColor": "#0FD", "weight": 1.0, "opacity": 0.3, "fillOpacity": 0.2 }, onEachFeature: function(feature, layer) { layer.bindPopup(feature.properties.P11_001); layer.setIcon(icon); } })); </script> </body> </html>
サイト
完成したいサイトは、次の通り
http://handygeospatial.github.io/mapsites/2013/12/27/
スクリーンショットも残しておく。
*1:tilestache-seed.pyは、サーバサイドベクトルタイルサーバのseed機能であるから、タイル区画ごとにデータベースを検索するモデルでも合理的であるが、本来、(静的な)ベクトルタイルの作成を目的とするのであれば、ポイントデータは1レコードはかならず1タイルに落ちるのであるから、MapReduce式にストリーム処理するほうが合理的である。