index.html
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=utf-8"/>
<script type="text/javascript" src="//d3js.org/d3.v2.min.js"></script>
<style type="text/css">
circle {
stroke: #EFEDF5;
fill: #EFEDF5;
}
line {
stroke: #EFEDF5;
pointer-events: none
}
path{
stroke: black
}
</style>
</head>
<body>
<select id="colorSelect" onchange="setColor('menu')">
<option value="density">Density</option>
<option value="equity">Equity</option>
<option value="area">Area</option>
<option value="statecode">State</option>
<option value="green">Green</option>
<option value="unpaved">Unpaved</option>
</select>
<div id="chart">
</div>
<script type="text/javascript">
statesKey = {"AZ": 9,"CA": 9,"CO": 8,"DC": 3,"FL": 4,"GA": 4,"IL": 5,"MA": 1,"MI": 5,"NC": 4,"NM": 6,"NV": 9,"NY": 2,"OH": 5,"OR": 10,"PA": 3,"TN": 4,"TX": 6,"VA": 3,"WA": 10,"WI": 5};
w = 960,
h = 960,
links = [],
voronoiVertices = [],
color = d3.scale.quantize().domain([7000, 10000]).range(d3.range(2, 9));
var numVertices = 50;
d3.json("neighborhoods.json", function(json) {
vertices = json;
sizeMin = d3.min(vertices, function (d) { return d["area"];});
sizeMax = d3.max(vertices, function (d) { return d["area"];});
sizeMed = (sizeMin + sizeMax ) / 2;
var prevEventScale = 1;
xMin = d3.min(vertices, function (d) { return d["x"];});
xMax = d3.max(vertices, function (d) { return d["x"];});
yMin = d3.min(vertices, function (d) { return d["y"];});
yMax = d3.max(vertices, function (d) { return d["y"];});
sizeRamp = d3.scale.linear().domain([sizeMin,sizeMed,sizeMax]).range([-50,-250,-1000]);
xRamp = d3.scale.linear().domain([xMin,xMax]).range([225,700]).clamp(true)
yRamp = d3.scale.linear().domain([yMin,yMax]).range([700,225]).clamp(true)
for ( n in vertices) {
vertices[n]["x"] = xRamp(vertices[n]["x"]);
vertices[n]["y"] = yRamp(vertices[n]["y"]);
vertices[n]["statecode"] = statesKey[vertices[n]["state"]];
}
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h)
.attr("class", "Purples")
circleClip = svg.append("svg:clipPath").attr("id", "clipper").append("circle").attr("cx", w / 2).attr("cy", h / 2).attr("r", 325);
force = self.force = d3.layout.force()
.charge(function (d, i) { return sizeRamp(vertices[i].area)})
.size([w, h])
.on("tick", update);
force.nodes(vertices).start();
circle = svg.selectAll("g.nodes");
path = svg.selectAll("path");
link = svg.selectAll("line");
})
function update(e) {
voronoiVertices = vertices.map(function(o){return [o.x, o.y, o]})
path = path.data(d3.geom.voronoi(voronoiVertices))
path.enter().append("path")
.attr("class", function(d, i) { return "q"+color(d3.geom.polygon(d).area())+"-9"; })
.attr("d", function(d) { return "M" + d.join("L") + "Z"; })
.attr("clip-path", "url(#clipper)")
.style("stroke", "white")
.style("stroke-width", 3)
path.attr("class", function(d, i) { return "q"+color(d3.geom.polygon(d).area())+"-9"; })
.attr("d", function(d) { return "M" + d.join("L") + "Z"; })
.attr("clip-path", "url(#clipper)")
path.exit().remove();
circle = circle.data(vertices)
circleEnter = circle.enter().append("g")
.call(force.drag)
.attr("class", "nodes")
.attr("r", 0)
.attr("transform", function (d) { return "translate("+d.x+","+d.y+")"});
circleEnter.append("circle")
.transition().duration(1000).attr("r", 5);
text1 = circleEnter.append("text")
.attr("opacity", 0)
.style("fill", "white")
.style("stroke", "white")
.style("stroke-width", "3")
.text(function (d,i) {return vertices[i].name})
.transition().duration(1000).attr("opacity", .75);
text2 = circleEnter.append("text")
.attr("opacity", 0)
.style("fill", "black")
.text(function (d,i) {return vertices[i].name})
.transition().duration(1000).attr("opacity", 1);
circle.attr("transform", function (d) { return "translate("+d.x+","+d.y+")"});
circle.exit().transition().attr("opacity", 0).remove();
links = []
d3.geom.delaunay(voronoiVertices).forEach(function(d) {
links.push(edge(d[0], d[1]));
links.push(edge(d[1], d[2]));
links.push(edge(d[2], d[0]));
});
link = link.data(links)
link.enter().append("line")
link.attr("x1", function(d) { return d.source[2].x; })
.attr("y1", function(d) { return d.source[2].y; })
.attr("x2", function(d) { return d.target[2].x; })
.attr("y2", function(d) { return d.target[2].y; })
.style("stroke", "none")
link.exit().remove()
circleClip.attr("r", function() { return d3.max(vertices, function (d) { return d["x"] - (w / 2) + (w / 20);}) } );
}
function edge(a, b) {
return {
source: a,
target: b
};
}
function setColor(inputString) {
if (inputString == "menu") {
inputString = document.getElementById("colorSelect").value;
}
colorMin = d3.min(vertices, function (d) { return d[inputString];});
colorMax = d3.max(vertices, function (d) { return d[inputString];});
colorMed = (colorMin + colorMax ) / 2;
colorRamp = d3.scale.linear().domain([1,100,400]).range(["green","yellow","purple"]);
path.style("fill", function(d, i) { return colorRamp(vertices[i][inputString])})
text1.text(function (d,i) {return vertices[i][inputString]})
text2.text(function (d,i) {return vertices[i][inputString]})
}
</script>
</body>
</html>