Demonstrating jenks natural breaks implemented in simple-statistics.
Rendered by d3js, based on an example by Mike Bostock.
forked from tmcw's block: Jenks Natural Breaks with simple-statistics and d3
Data from the 2016 SF Land Use map
xxxxxxxxxx
<meta charset="utf-8">
<style>
body {
background: #323d4d;
color: #fff;
font:normal 14px sans-serif;
}
#form {
position: absolute;
top: 10px;
left: 10px;
}
input {
margin-right: 10px;
}
.choro {
fill: #000;
stroke: #323d4d;
stroke-linejoin: round;
}
path {
-webkit-transition: fill 200ms linear;
}
.caption {
font-weight: bold;
fill: #fff
}
.key text {
fill: #fff
}
.key path {
display: none;
}
.key line {
stroke: #fff;
shape-rendering: crispEdges;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script src="simple_statistics.js"></script>
<script src="//d3js.org/queue.v1.min.js"></script>
<script src="//d3js.org/topojson.v0.min.js"></script>
<script>
var width = 960,
height = 550,
breaks = 5,
legendCopy = 'Hate Groups Per 10m',
scales = {},
path = d3.geo.path();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
queue()
.defer(d3.json, "data.v2.json")
.defer(d3.csv, "groups.v2.csv")
.await(ready)
function ready(err, us, groups) {
console.log("err", err)
console.log("us", us)
console.log("groups", groups)
var rateById = {};
groups.forEach(function(d) {
rateById[d["id"]] = +d["Per 10m"]; });
scales.quantile = d3.scale.quantile()
.domain([0, d3.max(d3.values(rateById))])
.range(["#4f5c6d","#77565a","#9d5048","#c54936","#ec4524"]);
scales.jenks = d3.scale.quantile()
.domain(ss.jenks(groups.map(function(d) { return +d["Per 10m"]; }), breaks - 1))
.range(["#4f5c6d","#77565a","#9d5048","#c54936","#ec4524"]);
var choro = svg.append("g")
.attr("transform", "translate(0,40)")
.attr("class", "choro")
.selectAll("path")
.data(topojson.object(us, us.objects.states).geometries)
.enter().append("path")
.attr("id", function(d) {return d.id})
.attr("d", path);
d3.selectAll('input').on('change', function() {
setScale(this.id);
});
var g = svg.append("g")
.attr("class", "key")
.attr("transform", "translate(440,20)");
var color = d3.scale.quantile()
.domain(scales.jenks.quantiles())
.range(["#4f5c6d","#77565a","#9d5048","#c54936","#ec4524"]);
// A position encoding for the key only.
var x = d3.scale.linear()
.domain([0, d3.max(d3.values(rateById))])
.range([0, 400]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(13)
.tickValues(color.domain())
g.selectAll("rect")
.data(color.range().map(function(d, i) {
return {
x0: i ? x(color.domain()[i - 1]) : x.range()[0],
x1: i < color.domain().length ? x(color.domain()[i]) : x.range()[1],
z: d
};
}))
.enter().append("rect")
.attr("height", 8)
.attr("x", function(d) { return d.x0; })
.attr("width", function(d) { return d.x1 - d.x0; })
.style("fill", function(d) { return d.z; });
g.call(xAxis).append("text")
.attr("class", "caption")
.attr("y", -6)
.text(legendCopy);
function setScale(s) {
choro.style("fill", function(d) { return scales[s](rateById[d.id]); })
var breaksArr = (s ==="jenks")? scales[s].domain() : scales[s].quantiles()
color = d3.scale.quantile()
.domain(breaksArr)
.range(["#4f5c6d","#77565a","#9d5048","#c54936","#ec4524"]);
x = d3.scale.linear()
.domain([0, d3.max(d3.values(rateById))])
.range([0, 400])
xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.tickSize(13)
.tickValues(color.domain())
g.selectAll("rect")
.data(color.range().map(function(d, i) {
return {
x0: i ? x(color.domain()[i - 1]) : x.range()[0],
x1: i < color.domain().length ? x(color.domain()[i]) : x.range()[1],
z: d
};
}))
.transition()
.attr("x", function(d) { return d.x0; })
.attr("width", function(d) { return d.x1 - d.x0; })
.style("fill", function(d) { return d.z; })
g.transition().call(xAxis)
}
setScale('jenks');
}
</script>
https://d3js.org/d3.v3.min.js
https://d3js.org/queue.v1.min.js
https://d3js.org/topojson.v0.min.js