概要
地域データを扱うことが多いので,可視化の方法としてD3.jsを勉強しました.
(可視化は手段なんで,RでもTablueauでも状況に合わせて使い分ければいいと思います)
D3.js(Data Driven Documents)は
ウェブブラウザで動的コンテンツを描画するJavaScriptライブラリ(Wikipediaより)
です.
dotinstallをざっと見とくと理解が早いと思います.
今回は地図に「都道府県別 100万人あたりの常設映画館数」をプロットしてみました.
完成図はこちら.
情報源:e-Stat
(東京が一番かと思いきや福岡が多いんですね.スクリーンの数とか,映画館の来場回数とか,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="utf-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