D3.jsでヒートマップっぽいものを作ってみた
概要
地域データを扱うことが多いので,可視化の方法としてD3.jsを勉強しました. (可視化は手段なんで,RでもTablueauでも状況に合わせて使い分ければいいと思います)
D3.js(Data Driven Documents)は
ウェブブラウザで動的コンテンツを描画するJavaScriptライブラリ(Wikipediaより)
です.
dotinstallをざっと見とくと理解が早いと思います.
今回は以下の記事をベースに地図に「都道府県別 100万人あたりの常設映画館数」をプロットしてみました. qiita.com
完成図はこちら. 情報源:政府統計の総合窓口 GL01010101 (東京が一番かと思いきや福岡が多いんですね.スクリーンの数とか,映画館の来場回数とか,DVDやテレビなど配給方法の割合とか色々気になりますが,今回は関係ないので触れません)
準備
ツール
# ShapeかGeoJSONへの変換に利用する $ brew install gdal # GeoJSONからTopoJSONへの変換に利用する $ npm install -g topojson # 最新版のV2系では,Qiitaで使用されているtopojsonというコマンドが4つのコマンドに分割されている. # V2ではgeo2topoが該当するが,今回はV1を指定してインストールする $ npm install -g topojson@1.6.27 # kokoじゃなくてもいいんだけど # 今回,外部ファイルからデータを読み込むんで,直接htmlファイルを開くだけだと外部ファイルを読み込めなず # サーバにファイルを置いてアクセスする必要がある.kokoはそのための手段. $ brew install node $ npm install -g koko
データ
Natural Earthから都道府県レベルの地図データをダウンロードします.Shapeファイルなんで,GeoJSON -> TopoJSONと変換して使います.
# ne_10m_admin_1_states_provinces.shpというShapeファイルが入っているので,pref.jsonというGeoJSONに変換する $ ogr2ogr -f GeoJSON -where "adm0_a3 = 'JPN'" pref.json ne_10m_admin_1_states_provinces.shp # GeoJSONからjapan.jsonというTopoJSONに変換する $ topojson -p name -p name_local -p latitude -p longitude -o japan.json pref.json
実装
こんな感じです. ちなみに2017/02/19時点でD3.jsの最新版はv4なんですが,アニメーションを設定しようとするとエラーになったのでhttps://d3js.org/d3.v3.min.jsを指定しています.
<!DOCTYPE html> <html lang="ja"> <head> <meta charset="utr-8"> <title></title> <style type="text/css"> .pref { fill: #fff; stroke: #aaa; } </style> </head> <body> <script src="https://d3js.org/d3.v3.min.js"></script> <script src="http://d3js.org/topojson.v0.min.js"></script> <script> var w = 1300; var h = 960; var svg = d3.select("body") .append("svg") .attr("width", w) .attr("height", h); // 地図の投影図法を設定する. var projection = d3.geo.mercator() .center([136, 35.5]) .scale(2000) .translate([w / 2, h / 2]); // GeoJSONからpath要素を作る. var path = d3.geo.path() .projection(projection); var pref = { "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":"沖縄県" }; // TopoJSONを読み込む. d3.json("japan.json", function(error, japan) { console.log(japan); var topo = topojson.object(japan, japan.objects.pref).geometries; svg.selectAll(".pref") .data(topo) .enter() .append("path") // キーとバリューを逆にしてしまったので回避策. .attr("class", function(d) { var id = Object.keys(pref).filter((key) => { return pref[key] === d.properties.name_local }); return "pref pref" + id; }) .attr("d", path); }); var category = [{"name":"常設映画館数(人口100万人当たり)(館)", "data":{"1":10.4, "2":13.6, "3":14.0, "4":6.0, "5":13.5, "6":9.7, "7":7.2, "8":10.3, "9":8.1, "10":10.6, "11":4.1, "12":5.6, "13":21.4, "14":6.5, "15":5.6, "16":4.7, "17":6.9, "18":16.5, "19":5.9, "20":13.3, "21":6.4, "22":10.8, "23":7.5, "24":13.7, "25":9.2, "26":7.7, "27":6.6, "28":13.7, "29":3.6, "30":8.2, "31":24.4, "32":5.7, "33":5.2, "34":20.1, "35":13.5, "36":6.5, "37":18.3, "38":15.1, "39":13.6, "40":36.7, "41":6.0, "42":20.2, "43":31.2, "44":12.0, "45":12.6, "46":7.8, "47":9.1}}]; var max = 40; var hoge = Math.floor(255 / 40); // 右上にあった常設映画館数というテキストをマウスオーバーすると // 映画館数によって都道府県の色が濃くなるよう施す. svg.selectAll("text") .data(category) .enter() .append("text") .attr("class", "category-label") .attr("x", w - 360) .attr("y", function(d, i) { return i * 20 + 200; }) .text(function(d) { return d.name; }) .on("mouseover", function(d) { for (var key in d.data) { var color = 255 - hoge * d.data[key]; if (color < 0) { color = 0; } d3.select(".pref" + key) .transition() .style("fill", "rgb(" + color + ", " + color + " ,255)"); } d3.select(this).style("fill", "#66f"); }) .on("mouseout", function(d) { for (var i = 1; i < 48; i++) { d3.select(".pref" + i) .transition() .style("fill", "#fff"); } d3.select(this).style("fill", "gray"); }); </script> </body> </html>
動作確認
$ koko -o