|
<!DOCTYPE html> |
|
<style> |
|
body { |
|
font: 16px sans-serif; |
|
} |
|
</style> |
|
<body> |
|
<div class="buttons"> |
|
<input name="resume" type=button value="Resume" onclick="resume()" /> |
|
<input name="pause" type=button value="Pause" onclick="pause()" /> |
|
<input name="ff" type=button value="Step Forward" onclick="stepForward()" /> |
|
<input name="rw" type=button value="Step Back" onclick="stepBack()" /> |
|
</div> |
|
<p>Current bandwith: <span id="text-span"></span></p> |
|
|
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script src="https://d3js.org/topojson.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-contour.v1.min.js"></script> |
|
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> |
|
<script type="text/javascript"> |
|
|
|
var margin = {top: 20, right: 20, bottom: 20, left: 20}, |
|
outerWidth = 960, |
|
outerHeight = 600, |
|
width = outerWidth - margin.left - margin.right, |
|
height = outerHeight - margin.top - margin.bottom; |
|
|
|
var topo; |
|
var bandwidth = 5; |
|
var interval = 1000; |
|
|
|
var svg = d3.select("body").append("svg") |
|
.attr("width", width) |
|
.attr("height", height); |
|
|
|
var bottomLayer = svg.append("g") |
|
.attr("class", "bottom-layer"); |
|
|
|
var color = d3.scaleSequential(d3.interpolateYlGnBu) |
|
.domain([0, 0.00375]); |
|
|
|
var projection = d3.geoEquirectangular() |
|
.rotate([88 + 20 / 60, -36 - 40 / 60]); |
|
|
|
var path = d3.geoPath() |
|
.projection(projection); |
|
|
|
d3.queue() |
|
.defer(d3.json, "west_side.topojson") |
|
.defer(d3.json, "west_side_homicides.topojson") |
|
.await(ready); |
|
|
|
function ready(error, community, homicides) { |
|
if (error) throw error; |
|
|
|
// start with unit projection |
|
projection |
|
.scale(1) |
|
.translate([0, 0]); |
|
|
|
// then translate and scale according to topoJSON's bbox |
|
var b = path.bounds(topojson.feature(community, community.objects.west_side)), |
|
s = 0.95 / Math.max((b[1][0] - b[0][0]) / width, (b[1][1] - b[0][1]) / height), |
|
t = [(width - s * (b[1][0] + b[0][0])) / 2, (height - s * (b[1][1] + b[0][1])) / 2]; |
|
|
|
projection |
|
.scale(s) |
|
.translate(t); |
|
|
|
svg.append("clipPath") |
|
.attr("id", "boundary") |
|
.append("path") |
|
.datum(topojson.feature(community, community.objects.west_side)) |
|
.attr("d", path); |
|
|
|
svg.append("path") |
|
.datum(topojson.feature(community, community.objects.west_side)) |
|
.attr("d", path) |
|
.attr("stroke", "black") |
|
.attr("fill", "none") |
|
.attr("stroke-width", 2); |
|
|
|
topo = topojson.feature(homicides, homicides.objects.west_side_homicides).features; |
|
update(bandwidth); |
|
} |
|
|
|
function update(bandwidth) { |
|
svg.selectAll(".contours") |
|
.remove(); |
|
|
|
svg.append("g", "g") |
|
.attr("fill", "none") |
|
.attr("stroke", "steelblue") |
|
.attr("stroke-linejoin", "round") |
|
.selectAll("path") |
|
.data(d3.contourDensity() |
|
.x(d => projection(d.geometry.coordinates)[0]) |
|
.y(d => projection(d.geometry.coordinates)[1]) |
|
.size([width, height]) |
|
.bandwidth(bandwidth) |
|
(topo)) |
|
.enter().append("path") |
|
.attr("class", "contours") |
|
.attr("clip-path", "url(#boundary)") |
|
.attr("fill", d => color(d.value)) |
|
.attr("opacity", 0.25) |
|
.attr("d", d3.geoPath()); |
|
|
|
d3.select("#text-span") |
|
.html(bandwidth); |
|
} |
|
var currId; |
|
|
|
function Animation(callback, interval) { |
|
var animationId, active; |
|
|
|
this.pause = () => { |
|
active = false; |
|
clearInterval(animationId); |
|
} |
|
|
|
this.resume = () => { |
|
active = true; |
|
animationId = setInterval(callback, interval); |
|
} |
|
|
|
this.active = () => active; |
|
|
|
this.resume(); |
|
} |
|
|
|
function pause() { |
|
if (timer.active()) { |
|
timer.pause(); |
|
} |
|
} |
|
|
|
function resume() { |
|
if (!timer.active()) { |
|
timer.resume(); |
|
} |
|
} |
|
|
|
function stepForward() { |
|
pause(); |
|
if (bandwidth < 50) { |
|
bandwidth += 1; |
|
} |
|
update(bandwidth); |
|
} |
|
|
|
function stepBack() { |
|
pause(); |
|
if (bandwidth > 5) { |
|
bandwidth -= 1; |
|
} |
|
update(bandwidth); |
|
} |
|
|
|
var timer = new Animation(function () { |
|
if (bandwidth == 50) { |
|
bandwidth = 5; |
|
} else { |
|
bandwidth += 1; |
|
} |
|
update(bandwidth); |
|
}, interval); |
|
|
|
</script> |
|
</body> |