都道府県別のアクセス数みたいなものを、種類別に地図上で塗り分ける、というのをD3.jsでやってみようと思います。
ちなみにD3.jsはぜんぜん知らなかったので、とりあえず基本のチュートリアルと地図のチュートリアルを読みました。
ツールのインストール
地図データの変換とかをするためのツールをインストールします。(Macです。)
$ brew install gdal
$ brew install node
$ npm install -g topojson
地図データのダウンロードと変換
Natural Earthから都道府県レベルの地図データをダウンロードします。
解凍するとファイルが色々ありますが、ne_10m_admin_1_states_provinces.shp というファイルを指定して日本のデータのGeoJSONファイルを作ります。
$ ogr2ogr -f GeoJSON -where "adm0_a3 = 'JPN'" pref.json ne_10m_admin_1_states_provinces.shp
pref.jsonというファイルができました。
このファイルからTopoJSONファイルを作るのですが、なぜか静岡県の名前がnullになっているので、pref.jsonファイルを開いて、"name":"Shizuoka"
になっている行の"name_local":null
を"name_local":"静岡県"
に修正しておきます。
(ちなみに1個目の"name":null
になってる行もよくわからないけど消して良さそう。)
TopoJSONファイルを作ります。
$ topojson -p name -p name_local -p latitude -p longitude -o japan.json pref.json
japan.jsonというファイルができました。
表示データの準備
表示したい都道府県別のデータは、ID => 都道府県名の配列と、ID => アクセス数の配列の2つがPHPであるので、それぞれ使いやすい形にしてJSONにしておきます。
<?php
$pref = array(1 => '北海道', 2 => '青森県', ... , 47 => '沖縄県');
$category = array(
array(
'name' => 'カテゴリー1',
'data' => array(1 => 291, 2 => 19, ... , 47 => 3)
),
...
array(
'name' => 'カテゴリー2',
'data' => array(1 => 111, 2 => 39, ... , 47 => 24)
),
);
?>
<script type="text/javascript">
var pref = <?php echo json_encode(array_flip($pref), JSON_UNESCAPED_UNICODE); ?>;
var category = <?php echo json_encode($category, JSON_UNESCAPED_UNICODE); ?>;
それぞれ、
var pref = {"北海道":1,"青森県":2, ... ,"沖縄県":47};
var category = [{"name":"カテゴリー1","data":{"1":291,"2":19, ... , "47":3}},{"name":"カテゴリー2","data":{"1":111,"2":39, ... , "47":24}}];
のようになります。
地図データに漢字の都道府県名が入っているので、そこからIDを引けるようにしておきます。
D3.js
まず地図を表示。
var width = 900,
height = 960;
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var projection = d3.geo.mercator()
.center([136, 35.5])
.scale(2000)
.translate([width / 2, height / 2]);
var path = d3.geo.path()
.projection(projection);
d3.json("japan.json", function(error, japan) {
var topo = topojson.object(japan, japan.objects.pref).geometries;
svg.selectAll(".pref")
.data(topo)
.enter()
.append("path")
.attr("class", function(d) {
return "pref pref" + pref[d.properties.name_local];
})
.attr("d", path);
});
各都道府県のパスにpref
というクラスと、都道府県別のIDが付いたクラス(北海道ならpref1
沖縄ならpref47
)を付けておきます。
cssで白地図にします。
<style type="text/css">
.pref {
fill: #fff;
stroke: #aaa;
}
</style>
地図の横にカテゴリー名を表示して、そのテキストをマウスオーバーした時に、該当するカテゴリーの数値情報が地図上の色でわかるようにします。
数値が多い都道府県がより濃い赤になるようにしました。(255以上の値はみんな同じ色になりますが。)
svg.selectAll("text")
.data(category)
.enter()
.append("text")
.attr("class", "category-label")
.attr("x", width - 160)
.attr("y", function(d, i) {
return i * 20 + 220;
})
.text(function(d) {
return d.name;
})
.on("mouseover", function(d) {
for (var key in d.data) {
var color = 255 - d.data[key];
if (color < 0) {
color = 0;
}
d3.select(".pref" + key)
.transition()
.style("fill", "rgb(255, " + color + ", " + color + ")");
}
d3.select(this).style("fill", "#f66");
})
.on("mouseout", function(d) {
for (var i = 1; i < 48; i++) {
d3.select(".pref" + i)
.transition()
.style("fill", "#fff");
}
d3.select(this).style("fill", "black");
});
『カテゴリー2』のテキストの上にマウスを置くと、このようになります。
地図描くのもD3.jsを使えばそんなに難しくないな、と思いました。