[D3+Leaflet]国土地理院さん渾身のベクトルタイルで遊んでみた。
FOSS4G 2014 Hokkaidoにて会場をザワつかせたベクトルタイルがとうとう公開されました。
私も去年のAdvent Calendarでちょこっとだけ紹介したことがあるのですが、それ以来あまり触っていなかったので、これを機にいろいろ遊んでみました。
国土地理院さんが用意してくれた上記サンプルを弄って遊んでいたのですが……サンプルで使われているTileLayer.GeoJSON.jsプラグインでは、オーバーレイされるPathを直接弄る方法が分からず右往左往。
(onEachFeatureのコールバック内でPath要素に直接アクセスできるかな? と思ったのですが、イベント発火時にはDOM上にpathが追加されていないのでアクセスできませんでした)
TileLayer.GeoJSON.jsでもスタイルの指定やフィルタリングなど行えるので特に問題ないのですが、せっかくのベクトルタイルデータ、できることなら道路の一本一本まで自分自身でコントロールしたい!
そう思って、いろいろ探してみたのですがタイルデータロード時にPath要素に直接アクセスする方法を結局みつけられなかったので、TileLayer.GeoJSON.jsを使わず、D3.jsを使って描画するTileLayer.d3GeoJSON.jsを作成しました。
D3.jsで描画しているので、オーバーレイされているPath全てにGeoJSONデータが束縛されています。
これで、いつも通りD3を使っていかようにも操作できますよ!
D3でベクトルタイルを描画する
今のところ、レイヤーコントローラーと連動しないなど、いたるところにバグがあります。例の如くChromeでしか動作確認してません。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
L.TileLayer.d3GeoJSON = L.TileLayer.extend({ tileNodes:null, onAdd : function(map) { this._map = map; L.TileLayer.prototype.onAdd.call(this,map); this._path = d3.geo.path().projection(function(d) { var point = map.latLngToLayerPoint(new L.LatLng(d[1],d[0])); return [point.x,point.y]; }); this.on("tileunload",function(d) { if (d.tile.xhr) d.tile.xhr.abort(); if (d.tile.nodes) d.tile.nodes.remove(); d.tile.nodes = null; d.tile.xhr = null; }); }, onRemove: function (map) { d3.selectAll(".d3-geojson-layer").remove(); }, _loadTile : function(tile,tilePoint) { var self = this; this._adjustTilePoint(tilePoint); if (!tile.nodes && !tile.xhr) { tile.xhr = d3.json(this.getTileUrl(tilePoint),function(error, geojson) { if (error) { console.log(error); } else { if (self.options.filter){ var tmp; tmp = geojson.features.filter(self.options.filter); geojson.features = tmp; } tile.xhr = null; tile.nodes = d3.select(map._container).select("svg").append("g") .attr("class", "d3-geojson-layer leaflet-zoom-hide"); tile.nodes.selectAll("path") .data(geojson.features).enter() .append("path") .attr("d", self._path) .attr(self.options.attr) .style(self.options.style) .on("click", self.options.onClick) .on("mouseover", self.options.onMouseover ) .on("mouseout", self.options.onMouseoute ) ; } }); } } }); |
こんな感じで使います
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 |
//ベクトルタイル設定 var vec = new L.TileLayer.d3GeoJSON( "http://cyberjapandata.gsi.go.jp/xyz/experimental_rdcl/{z}/{x}/{y}.geojson", { "attr":{ "class":function(d){ return d.properties.rID ; }, "stroke-width": 4, "stroke": "black" }, "style": { "cursor": "pointer" }, "layerName": "vectile", "onClick":function(d){ alert(JSON.stringify(d.properties, null, " ")); } } ) //地理院タイル(標準地図)設定 var std = new L.tileLayer( 'http://cyberjapandata.gsi.go.jp/xyz/std/{z}/{x}/{y}.png', { attribution: "地理院タイル(標準地図)", opacity: 0.4, }) var map = L.map("map", { center: [36.322356, 139.013057], zoom: 17, layers: [vec, std] }); map.options.maxZoom = 18; map.options.minZoom = 10 map._initPathRoot(); L.control.scale({imperial: false}).addTo(map); L.control.layers({}, { '地理院タイル(標準地図)': std, '道路中心線': vec }).addTo(map); |
遊んでみた
d3.jsを用いてベクトルタイルを描画。道路をクリックすると属性データが見れます。
example1
Path要素にマウスオーバーイベントを設定。国道、市区町村道等、都道府県道、それぞれでマウスオーバーしたときの色を変えてみました。
無駄にアニメーションします。……重いdeth。
example2
zoomレベルに応じて表示する道路を間引いたり線の太さを変えてみたりしました。
間引く道路は、幅(rnkWidth)の値をつかって判別しています。
example3
ベクトルタイルデータは、ラスタタイルに比べて利用する側が好き勝手に操作して遊ぶことができる面白いサービスです。
皆さんもぜひ活用してみてください。