リアルタイム・ベクトルタイル
リアルタイム・ベクトルタイルの実験をした。結果は http://handygeospatial.github.io/mapsites/2014/02/25/ 。
タイルデータサーバ
heroku で実装した。http://rtvt.herokuapp.com/xyz/11/1852/749.geojson といったアドレスにアクセスすれば、タイル内10点のランダムデータを返すというものだ。
heroku の具体的な使い方については、 http://i2bskn.hateblo.jp/entry/2013/06/11/000625 を参考にさせていただいた。
各ファイルの内容を紹介する。
app.rb
メインプログラム。
require 'bundler/setup' require 'sinatra/base' require 'json' class App < Sinatra::Base def latlng(z, x, y) n = 2.0 ** z lng = 360.0 * x / n - 180.0 lat_rad = Math::atan(Math::sinh(Math::PI * (1 - 2 * y / n))) lat = 180.0 * (lat_rad / Math::PI) {:lat => lat, :lng => lng} end def extent(z, x, y) lower_corner = latlng(z, x, y + 1) upper_corner = latlng(z, x + 1, y) {:lat => lower_corner[:lat], :lng => lower_corner[:lng], :width => upper_corner[:lng] - lower_corner[:lng], :height => upper_corner[:lat] - lower_corner[:lat]} end def geojson(n, z, x, y) e = extent(z, x, y) json = {:type => 'FeatureCollection', :features => []} n.times {|i| json[:features] << {:type => 'Feature', :geometry => {:type => 'Point', :coordinates => [e[:lng] + rand * e[:width], e[:lat] + rand * e[:height]]}, :properties => {:number => i + 1}} } JSON.dump(json) end get '/' do 'Hi.' end get '/xyz/*.*' do headers['Access-Control-Allow-Origin'] = '*' (z, x, y) = params[:splat][0].split('/').map{|v| v.to_i} ext = params[:splat][1] geojson(10, z, x, y) end end
Gemfile
source 'https://rubygems.org' ruby '2.0.0' gem 'sinatra' gem 'json'
Procfile
web: bundle exec rackup config.ru -p $PORT
config.rb
$:.unshift(File.dirname(__FILE__)) require 'app' run App
ウェブアプリケーション
http://handygeospatial.github.io/mapsites/2014/02/25/ に置いた。ソースは次の通り。
index.html
<!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" /> <title>リアルタイム・ベクトルタイル</title> <link rel='stylesheet' href='http://leafletjs.com/dist/leaflet.css'> <script src='http://leafletjs.com/dist/leaflet.js'></script> <script src="leaflet-hash.js"></script> <script src="TileLayer.GeoJSON.js"></script> <style> html, body, #mapdiv {margin: 0; padding: 0; width: 100%; height: 100%;} </style> </head> <body> <div id='mapdiv'/> <script> function render_properties(prop) { var s = '' for(name in prop) { s += "<tr><td>" + name + "</td><td>" + prop[name] + "</td></tr>"; } return "<table border='1'>" + s + "</table>"; } icon = L.icon({iconUrl: 'http://cyberjapan.jp/symbols/129.bmp', iconSize: [20, 20]}); map = new L.Map('mapdiv', {zoom: 14, center: [35.6850, 139.7512]}); hash = new L.Hash(map); map.addLayer(new L.TileLayer( 'http://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', {attribution: '地理院タイル'})); layer = new L.TileLayer.GeoJSON( 'http://rtvt.herokuapp.com/xyz/{z}/{x}/{y}.geojson', {attribution: 'リアルタイム・ベクトルタイル', unloadInvisibleTiles: true, minZoom: 11, maxZoom: 18}, {style: {"clickable": true}, onEachFeature: function(feature, layer) { layer.bindPopup(render_properties(feature.properties)); layer.setIcon(icon); } }); map.addLayer(layer); refresh = function() {layer.redraw(); setTimeout(refresh, 3000)}; refresh(); </script> </body> </html>