前回の投稿で地図を描いて色を塗りましたが、勉強も兼ねて、もう少しいい感じにしたいと思います。
前回は、値が255以上はみんな同じ真っ赤になっていましたが、scaleを使ってみます。
var cscale = d3.scale.sqrt()
.domain([0, d3.max(category, function(d) {
var m = 0;
for (var key in d.data) {
if (m < d.data[key] - 0) {
m = d.data[key] - 0;
}
}
return m;
})])
.range([255, 0]);
domain()
で実際の値の範囲を指定し、range()
で返してほしい値の範囲を指定します。
最小値が255を、最大値が0を返して欲しいので、[255, 0]
と指定します。
また、scaleにはlinear等、何種類かありますが、小さい値の変化もわかるようにしたいので、sqrtを使うことにしました。
これで、前回は
var color = 255 - d.data[key];
if (color < 0) {
color = 0;
}
としていたところを、
var color = cscale(d.data[key]);
とすると、255以上の値も差がわかるようになりました。
スケール超便利〜。
でも色の変化だけだとわかりにくいし平方根スケールなので、棒グラフも重ねて表示することにします。
棒グラフには線形スケールを使い、地図を描画したエリア内に重ねて半透明の棒を描画するようにします。
棒をrectで描いて、立体的に見せるために、polygonで側面部分を描くことにしました。
さらにアニメーションで下からニョキニョキ伸びるようにします。盛りだくさん!
長くなりますが、全部書いておきます。
var pref = <?php echo json_encode($prefecture, JSON_UNESCAPED_UNICODE); ?>;
var pref_flip = <?php echo json_encode(array_flip($prefecture), JSON_UNESCAPED_UNICODE); ?>;
var category = <?php echo json_encode($category, JSON_UNESCAPED_UNICODE); ?>;
var width = 900,
height = 960,
prefh = 50,
axisw = 40,
dr = 500;
var bw = width / 47 / 4;
var cscale = d3.scale.sqrt()
.domain([0, d3.max(category, function(d) {
return getMaxValue(d.data);
})])
.range([255, 0]);
var bscale = d3.scale.linear()
.domain([0, d3.max(category, function(d) {
return getMaxValue(d.data);
})])
.range([height, bw]);
var yAxis = d3.svg.axis()
.scale(bscale)
.orient("right");
var svg = d3.select("body").append("svg")
.attr("width", width + axisw)
.attr("height", height + prefh);
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_flip[d.properties.name_local];
})
.attr("d", path);
});
svg.selectAll("text")
.data(category)
.enter()
.append("text")
.attr("class", "category-label")
.attr("x", 720)
.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 = cscale(d.data[key]);
var x = width - key * bw * 4;
var y = bscale(d.data[key]);
d3.select(".pref" + key)
.transition()
.duration(dr)
.style("fill", "rgb(255, " + color + ", " + color + ")");
var rct = svg.append("rect")
.attr("class", "brect")
.attr("x", x)
.attr("y", height)
.attr("width", bw * 2)
.attr("height", 0)
.attr("fill", "rgba(102, 204, 102, 0.3)");
rct.transition()
.duration(dr)
.attr("y", y)
.attr("height", height - y);
var plg = svg.append("polygon")
.attr("class", "bar")
.attr("points",
x + "," + height
+ " " + (x + bw) + "," + (height - bw)
+ " " + (x + bw * 3) + "," + (height - bw)
+ " " + (x + bw * 3) + "," + (height - bw)
+ " " + (x + bw * 2) + "," + height
+ " " + (x + bw * 2) + "," + height)
.attr("fill", "rgba(102, 204, 102, 0.5)");
plg.transition()
.duration(dr)
.attr("points",
x + ',' + y
+ ' ' + (x + bw) + ',' + (y - bw)
+ ' ' + (x + bw * 3) + ',' + (y - bw)
+ ' ' + (x + bw * 3) + ',' + (height - bw)
+ ' ' + (x + bw * 2) + ',' + height
+ ' ' + (x + bw * 2) + ',' + y);
svg.append("text")
.attr("class", "bar-label")
.attr("x", width - key * bw * 4 + bw)
.attr("y", height)
.attr("writing-mode", "tb")
.attr("font-size", bw)
.text(pref[key]);
}
d3.select(this).style("fill", "#f66");
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(" + width + ", 0)")
.call(yAxis);
})
.on("mouseout", function(d) {
for (var i = 1; i < 48; i++) {
d3.select(".pref" + i)
.transition()
.style("fill", "#fff");
}
d3.selectAll(".bar").remove();
d3.selectAll(".brect").remove();
d3.selectAll(".bar-label").remove();
d3.selectAll(".axis").remove();
d3.select(this).style("fill", "black");
});
function getMaxValue(p) {
var m = 0;
for (var key in p) {
if (m < p[key] - 0) {
m = p[key] - 0;
}
}
return m;
}
cssには
.axis path,
.axis line {
fill: none;
stroke: #aaa;
shape-rendering: crispEdges;
}
.axis text {
font-family: sans-serif;
font-size: 10px;
}
を追加します。
前回は、都道府県名からIDを出したかったので、キーと値を反転した配列をprefとして使っていましたが、棒グラフのラベルに都道府県名を出すために元の配列も必要なので、prefとpref_flipの両方を用意しています。
svgのxy座標は左上が(0, 0)で右下に向かって値が大きくなっていくので少しややこしいですが、scaleを適切に設定すれば、なんとかなると思います。(上記の変数xとyは、棒グラフのrectの左上の点の座標になっています。)
polygonを
transition()
でアニメーションにする場合は、初期値のpointsで指定したのと同じ順番で移動後のpointsを指定します。
上の例だと、1番目に指定した(x, height)
が(x, y)
に移動、2番目に指定した(x + bw, height - bw)
が(x + bw, y - bw)
に移動、、、という感じで動きます。下に表示した都道府県名のtextは、writing-modeをtbにして縦書きにしています。
色々できて、なんか楽しくなってきました。