各インスタンスのUser数を面積として表示。
国コードの実際国名を知りたい方は、下記サイトの対応表をみてください。
license: mit | |
height: 1060 |
各インスタンスのUser数を面積として表示。
国コードの実際国名を知りたい方は、下記サイトの対応表をみてください。
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<title>Mastodon instances TreeMap</title> | |
<style> | |
body { | |
font: 10px sans-serif; | |
margin: 3px; | |
position: relative; | |
} | |
.node { | |
position: absolute; | |
overflow: hidden; | |
padding: 3px; | |
border: 1px solid #666; | |
box-shadow: 0 2px 3px rgba(0,0,0,0.4); | |
box-sizing: border-box; | |
} | |
.node--internal { | |
font-weight: bold; | |
} | |
.node--hover { | |
padding: 2px; | |
border: solid 1px #000; | |
} | |
#info { | |
padding: 4px; | |
margin: auto; | |
position: absolute; | |
top:0px; | |
right: 0px; | |
width: 150px; | |
height: 20px; | |
background-color:white; | |
z-index: 999999; | |
} | |
</style> | |
<body> | |
<div id="info">update</div> | |
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/4.3.0/d3.min.js"></script> | |
<script> | |
var width = 960, | |
height = 1060, | |
offset = 3; | |
var format = d3.format(",d"); | |
var normalization = d3.scaleLinear(); | |
var color = d3.scaleOrdinal(d3.schemeCategory20); | |
var stratify = d3.stratify() | |
.parentId(function(d) { /*console.log(d.id.substring(0, d.id.lastIndexOf("."))); */return d.id.substring(0, d.id.lastIndexOf(".")); }); | |
var stratify2 = d3.stratify() | |
.parentId(function(d) { return d.id.substring(0, d.id.lastIndexOf(",")); }); | |
var treemap = d3.treemap() | |
.size([width, height]) | |
.paddingInner(1) | |
.paddingOuter(offset) | |
.paddingTop(function(d) { return d.depth < 3 ? 19 : offset; }) | |
.round(true); | |
d3.json("https://shimizu-mastodon-instances.s3-ap-northeast-1.amazonaws.com/mastodon/Latest.json", function(error, data) { | |
if (error) throw error; | |
var list = []; | |
d3.select("#info").text("Update: "+data.update) | |
var parents = d3.nest() | |
.key(function(d){ return d.properties.location.country }) | |
.map(data.features).keys().map(function(key){ return {id:"mastodon instances,"+key.toLowerCase(), country:key}}) | |
data.features | |
.filter(function(d){ return +d.properties.Users > 10 ;}) | |
.forEach(function(d){ | |
var obj = {}; | |
obj.id = "mastodon instances," + d.properties.location.country + "," + d.properties.Instance; | |
obj.id = obj.id.toLowerCase() | |
obj.country = d.properties.location.country; | |
obj.instance = d.properties.Instance; | |
obj.users = +d.properties.Users | |
list.push(obj); | |
}); | |
var base = ([{id:"mastodon instances"}]).concat(parents.concat(list)) | |
base.sort(function(a,b){ | |
if(a.id < b.id) return -1; | |
if(a.id > b.id) return 1; | |
return 0; | |
}); | |
var root = stratify2(base) | |
.sum(function(d) { return d.users; }) | |
.sort(function(a, b) { return b.height - a.height || b.value - a.value; }); | |
cascade(treemap(root)); | |
d3.select("body") | |
.selectAll(".node") | |
.data(root.descendants()) | |
.enter().append("div") | |
.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--left"); }) | |
.attr("title", function(d) { return d.id.substring(d.id.lastIndexOf(",") + 1).split(/(?=[A-Z][^A-Z])/g).join("\u200b") + "\n Users:" + format(d.value); }) | |
.style("left", function(d) { return d.x0 + "px"; }) | |
.style("top", function(d) { return d.y0 + "px"; }) | |
.style("width", function(d) { return d.x1 - d.x0 + "px"; }) | |
.style("height", function(d) { return d.y1 - d.y0 + "px"; }) | |
.style("background-color", function(d){ return color(d.data.country)}) | |
.each(function(d) { d.node = this; }) | |
.on("mouseover", hovered(true)) | |
.on("mouseout", hovered(false)) | |
.html(function(d) { | |
var text = d.id.substring(d.id.lastIndexOf(",") + 1).split(/(?=[A-Z][^A-Z])/g).join("\u200b"); | |
var html = (d.data.instance) ? ['<a target="_blank" href="https://', d.data.instance, '">' + text + "</a>"].join("") : text ; | |
return html; | |
}); | |
}); | |
//*/ | |
function cascade(root) { | |
return root.eachAfter(function(d) { | |
if (d.children) { | |
d.heightRight = 1 + d3.max(d.children, function(c) { return c.x1 === d.x1 - offset ? c.heightRight : NaN; }); | |
d.heightBottom = 1 + d3.max(d.children, function(c) { return c.y1 === d.y1 - offset ? c.heightBottom : NaN; }); | |
} else { | |
d.heightRight = | |
d.heightBottom = 0; | |
} | |
}).eachBefore(function(d) { | |
d.x1 -= 2 * offset * d.heightRight; | |
d.y1 -= 2 * offset * d.heightBottom; | |
}); | |
} | |
function hovered(hover) { | |
return function(d) { | |
d3.selectAll(d.ancestors().map(function(d) { return d.node; })) | |
.classed("node--hover", hover); | |
}; | |
} | |
</script> |