
駅すぱあと路線図API Hacks! D3.jsを使ってSVGをオーバーレイする。
新しい地図サービスが始まったとき、誰もが気になるのは「この地図サービスでD3.jsは使えるのだろうか?」という点でしょう。心配ありません。駅すぱあと路線図でD3.jsは使えますよ!
日本全国シームレスな鉄道路線図API「駅すぱあと路線図」、ヴァル研究所が無償提供を開始
駅すぱあと路線図APIのアクセスキーを取得したので、さっそく遊んでみました。
とりあえず、D3を使うまでの解説を載せておきます。
※ exampleはフリープランを利用して作成しているため1日100セッションを越えると動作しなくなります。
ゲットスタート
駅すぱあと路線図APIは、汎用地図ライブラリのleaflet.jsを利用しているとのことなので、まずはベーシックなサンプルを作成してD3.jsを使うのに必要となるオブジェクトにアクセスできるか確認します。
開発用のドキュメントやサンプルコードは下記ページにて公開されています。
駅すぱあと路線図 – 駅すぱあとワールド
アクセスキーは、上記ページよりフリープランに申し込むことで取得できます。
| 1 2 3 4 5 6 7 8 9 | var rosen function init() {   rosen = new Rosen("map", {         apiKey: "***" // アクセスキーはサンプル用です。実際にご利用されるときは書き換えてください。   }) } window.addEventListener('load', init) | 
路線図が表示されているページで開発者ツールのコンソールを開き「L」を入力実行するとグローバルにleafletオブジェクトが読み込まれていることが確認できます。
また、mapオブジェクトはrosen._mapに保存されていることが解りました。
この二つのオブジェクトにアクセスできれば、leaflet.jsの機能やD3.jsをオーバーレイすることが問題なくできます。
座標取得
駅すぱあと路線図は、デフォルメされた日本地図上に路線が描画されています。そのためleaflet上で使用されている座標も一般的な緯度経度ではない可能性があります。
そこで、mapオブジェクトにクリックイベントを仕込み、クリックした場所の座標を取得し確認します。
| 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | var output = document.querySelector("#output") var rosen function init() {     rosen = new Rosen("map", {          apiKey: "***" // アクセスキーはサンプル用です。実際にご利用されるときは書き換えてください。     });     //クリックした位置の座標を取得する     rosen._map.on('click', function(e) {         output.value = e.latlng.lat + ", " + e.latlng.lng     }); } window.addEventListener('load', init)   | 
東京駅をクリックすると、[-0.378570556640625, 0.4677581787109375]という座標が取得できました。
とりあえず位置座標を取得できたので、次はD3.jsを使ってSVGをオーバーレイしてみます。
D3でサークルを載せる
上記コードで取得した座標を元に、D3.jsを使ってサークルを描画してみます。
leafletのlatLngToLayerPointメソッドを使うことで、位置座標から画面上の座標へと変換できるので、その値を用いてSVG上にサークルを描画します。
| 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 | var output = document.querySelector("#output") var rosen var tokyoLatLng = [-0.378570556640625, 0.4677581787109375] function init() { 	rosen = new Rosen("map", {    // "map"は<div>のidと一致させる 		apiKey: "***" // アクセスキーはサンプル用です。実際にご利用されるときは書き換えてください。 	});     var width = document.querySelector("#map").clientWidth     var height = document.querySelector("#map").clientHeight     /leafletのoverlayerにsvg要素を追加する 	var svg = d3.select(rosen._map.getPanes().overlayPane).append("svg")         .attr("width", width).attr("height", height) 	var g = svg.append("g").attr("class", "leaflet-zoom-hide")     var circle = g.selectAll(".tokyo")         .data([tokyoLatLng]) 	//アークル要素を追加 	circle.enter().append("circle") 		.attr("class", "tokyo") 		.attr("fill", "red") 		.attr("fill-opacity", 0.5) 		.attr("r", 100)     var update = function() { 		//サークル要素の位置をアップデート 		g.select(".tokyo").attr("transform", function(d) { 			var pos = rosen._map.latLngToLayerPoint(d)             return "translate("+ pos.x +","+ pos.y +")"; 		}) 	}     rosen._map.on("viewreset zoom", update) 	update() } window.addEventListener('load', init)  | 
Trufでバッファを生成する
さて、サークルを表示することはできましたが、あくまで単なる円なのでパン・ズームに連動しません。
そこでturf.jsを使って100mバッファのGeoJSONを生成しD3.jsを使って表示してみます。
駅すぱあと用に生成したカスタムプロジェクションをgeoPath関数にバインドすることで、GeoJSONをオーバーレイすることができます。
| 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 | var output = document.querySelector("#output") var rosen //バッファ生成 var pointGeojson = turf.point([-0.378570556640625, 0.4677581787109375]) var bufferGeojson = turf.buffer(pointGeojson, 100, "meters") function init() {     rosen = new Rosen("map", {    // "map"は<div>のidと一致させる 		apiKey: "***" // アクセスキーはサンプル用です。実際にご利用されるときは書き換えてください。     }) 	//駅すぱあと路線図用のプロジェクションを生成する     var projectPoint = function(x, y) {         var point = rosen._map.latLngToLayerPoint([x,y])         this.stream.point(point.x, point.y)     }     var transform = d3.geoTransform({point: projectPoint})     var path = d3.geoPath().projection(transform)     var svg = d3.select(rosen._map.getPanes().overlayPane).append("svg")     var g = svg.append("g").attr("class", "leaflet-zoom-hide")     var buffer = g.append("path").datum(bufferGeojson)             .attr("fill", "red")             .attr("fill-opacity", 0.5)     var update = function() { 		//leafletの描画領域に追従するようにsvgのサイズを変更する 		var bounds = path.bounds(bufferGeojson) 		var topLeft = bounds[0] 		var bottomRight = bounds[1] 		svg .attr("width", bottomRight[0] - topLeft[0]) 			.attr("height", bottomRight[1] - topLeft[1]) 			.style("left", topLeft[0] + "px") 			.style("top", topLeft[1] + "px"); 		g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")") 		//バッファをアップデート         buffer.attr("d", path) 	}     rosen._map.on("viewreset zoom", update) 	update() } window.addEventListener('load', init)    | 
GeoJSONエディタを作成する
GeoJSONをオーバーレイすることができたので、あとは駅すぱあと路線図に独自のGeoJSONを作成できれば、ほぼD3でやりたいことはできそうです。
そこで、駅すぱあと路線図用のGeoJSONを作成するために、leaflet.drawプラグインをつかってエディタを作成します。
これで、路線図上をトレースしてGeoJSONを作成できるようになります。
※ なお、地図をトレースするのはいろいろと問題があるかもしれません。怒られたり訴えられたり殴られたりしたら止めます。
| 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 | var geo = "" var output = document.querySelector("#output")     var geo = "" var rosen function init() {     rosen = new Rosen("map", {            apiKey: "***" // アクセスキーはサンプル用です。実際にご利用されるときは書き換えてください。     })     //エディットレイヤーを生成       var drawnItems = new L.FeatureGroup()     rosen._map.addLayer(drawnItems);     //コントロールにエディタを追加する     var drawControl = new L.Control.Draw({         edit: {             featureGroup: drawnItems         }     })     rosen._map.addControl(drawControl)     //エディット後処理     rosen._map.on('draw:created', function (e) {         geo = e.layer.toGeoJSON()         //lat,lngの順番が逆になっているので修正する         geo.geometry.coordinates[0].forEach(function(d){             var tmp0 = d[0]             var tmp1 = d[1]             d[0] = tmp1             d[1] = tmp0         })         output.value = JSON.stringify(geo)     })       } window.addEventListener('load', init)  | 
drawプラグインで生成されるGeoJSONはcoordinatesの中身がlonglat形式になっているので、latlong形式に変更して出力しています。
とりあえず、群馬県のポリゴンを作成してみました。
D3を使ってGeojson表示
上記エディタで作成したGeoJSONをD3を使って路線図上にオーバーレイします。
基本的にはturf.jsで生成したGeoJSONを表示するのとかわりません。
| 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 54 55 56 57 58 | var rosen function init(args) { 	//geojsonの読み込み     d3.json("../gunma.geojson?1", main)     } function main(geojson) {     rosen = new Rosen("map", {    // "map"は<div>のidと一致させる       apiKey: "***" // アクセスキーはサンプル用です。実際にご利用されるときは書き換えてください。     }); 	//駅スパート路線図用のプロジェクションを生成     var projectPoint = function(x, y) {         var point = rosen._map.latLngToLayerPoint([x,y])         this.stream.point(point.x, point.y);     }     var transform = d3.geoTransform({point: projectPoint})     var path = d3.geoPath().projection(transform)     var width = document.querySelector("#map").clientWidth     var height = document.querySelector("#map").clientHeight     var svg = d3.select(rosen._map.getPanes().overlayPane).append("svg")     var g = svg.append("g").attr("class", "leaflet-zoom-hide")     g.append("path").datum(geojson)             .attr("fill", "red")             .attr("fill-opacity", 0.5)     var update = function() { 		var bounds = path.bounds(geojson) 		var topLeft = bounds[0] 		var bottomRight = bounds[1] 		svg .attr("width", bottomRight[0] - topLeft[0]) 			.attr("height", bottomRight[1] - topLeft[1]) 			.style("left", topLeft[0] + "px") 			.style("top", topLeft[1] + "px") 		g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")")         g.select("path").attr("d", path) 	}     rosen._map.on("viewreset zoom", update) 	update() } window.addEventListener('load', init)    | 
無事、地図上に群馬県のポリゴンが表示されました。
これで、D3.jsを使って路線図上にさまざまなビジュアライゼーションをオーバーレイできることが確認できました。






“駅すぱあと路線図API Hacks! D3.jsを使ってSVGをオーバーレイする。” への1件の返信
現在コメントは受け付けていません。