|
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd"> |
|
<html lang="en"> |
|
<head> |
|
<title>Chicago Ward Remap Outlines</title> |
|
<script src="//d3js.org/d3.v2.min.js"></script> |
|
<script type="text/javascript" src="https://ajax.googleapis.com/ajax/libs/jquery/1.8/jquery.min.js"></script> |
|
<script type="text/javascript" src="https://raw.github.com/fryn/html5slider/master/html5slider.js"></script> |
|
<style type="text/css"> |
|
body { |
|
color: #333; |
|
} |
|
|
|
.years, #star { |
|
cursor: pointer |
|
} |
|
|
|
#wards path { |
|
stroke-width: 0.5; |
|
} |
|
|
|
.ward-outline { |
|
fill: white; |
|
stroke: black; |
|
} |
|
|
|
.ward-fill { |
|
fill: black; |
|
stroke: white; |
|
} |
|
|
|
</style> |
|
</head> |
|
<body> |
|
<div id="vis"></div> |
|
<script type="text/javascript"> |
|
$(function() { |
|
//http://stackoverflow.com/a/901144/678708 |
|
function getParameterByName(name) { |
|
name = name.replace(/[\[]/, "\\\[").replace(/[\]]/, "\\\]"); |
|
var regexS = "[\\?&]" + name + "=([^&#]*)"; |
|
var regex = new RegExp(regexS); |
|
var results = regex.exec(parent.window.location.href); |
|
if(results == null) |
|
return ""; |
|
else |
|
return decodeURIComponent(results[1].replace(/\+/g, " ")); |
|
} |
|
var alignToGrid = getParameterByName('map') == 1 ? false : true |
|
var geometryCache = [] |
|
var projection_scale = 199466 |
|
var projection_translate = [49047.505335748254, 25762.791692685558] |
|
var w = 960 |
|
var h = 425 |
|
var proj = d3.geo.mercator().scale(projection_scale).translate(projection_translate) |
|
var path = d3.geo.path().projection(proj) |
|
var t = proj.translate() |
|
var s = proj.scale() |
|
var wards_json = [] |
|
var map = d3.select("#vis").append("svg:svg").attr("width", w).attr("height", h).call(d3.behavior.zoom().scaleExtent([1, 8]).on("zoom", drawWards)) |
|
var wards = map.append("svg:g").attr("id", "wards") |
|
var originalBBoxes = [] |
|
|
|
d3.json("wards_2005.json", function(json) { |
|
wards_json.push([json.features.filter(function(d) { |
|
return parseInt(d.properties.WARD) |
|
}).sort(function(a, b) { |
|
return a.properties.WARD - b.properties.WARD |
|
})]) |
|
|
|
d3.json("wards_2015.json", function(json) { |
|
var wardIndex = 0 |
|
wards_json.push([json.features.map(function(d) { |
|
//pad the arrays so they have the same number of vertices so morphing doesn't create artifacts |
|
prevCoordinates = wards_json[0][0][wardIndex++].geometry.coordinates[0] |
|
coordinates = d.geometry.coordinates[0] |
|
for (i = prevCoordinates.length; i < coordinates.length; ++i) prevCoordinates.push(prevCoordinates[0]) |
|
for (i = coordinates.length; i < prevCoordinates.length; ++i) coordinates.push(coordinates[0]) |
|
return {'type': 'Feature', 'geometry': {'type': "Polygon", 'coordinates': [coordinates]}, 'properties': d.properties} |
|
}).sort(function(a, b) { |
|
return a.properties.WARD - b.properties.WARD |
|
})]) |
|
|
|
dataWards = wards.selectAll("path").data(wards_json[0][0], function(d) { |
|
return parseInt(d.properties.WARD) |
|
}) |
|
|
|
drawWards() |
|
|
|
//demo |
|
$(".years:last").trigger("click") |
|
}) |
|
}) |
|
|
|
function transformWard(d, i) { |
|
if (!alignToGrid) return |
|
//there isn't an easy way to absolutely position a path in a SVG |
|
x = (d.properties.WARD - 1) % 10 |
|
y = Math.floor((d.properties.WARD - 1) / 10) |
|
xOffset = 50 / 2 - this.getBBox().width / 2 |
|
xOffset = xOffset < 6 ? 6 : xOffset |
|
yOffset = 50 / 2 - this.getBBox().height / 2 |
|
yOffset = yOffset < 1 ? 36 : yOffset + 36 |
|
//need to know where we originally positioned it so we can move map relative to that original position |
|
if(originalBBoxes[i] == null) originalBBoxes[i] = this.getBBox() |
|
//calculations are from the top left i.e. 0,0 |
|
return "translate(" + (-originalBBoxes[i].x + xOffset + (proj.scale()/2000 * x)) + ", " + (-originalBBoxes[i].y + yOffset + (proj.scale()/2500 * y)) + ")" |
|
} |
|
|
|
function drawWards() { |
|
if (d3.event != null) { |
|
proj.translate([t[0] * d3.event.scale + d3.event.translate[0], t[1] * d3.event.scale + d3.event.translate[1]]) |
|
proj.scale(s * d3.event.scale) |
|
} |
|
|
|
//so wards aren't added when the map moves |
|
if($("#wards path").length == 0) { |
|
dataWards.enter().append("svg:path").attr("class","ward-outline").attr("d", function(d) { |
|
return path(d.geometry) |
|
}).attr("transform", transformWard).append("svg:title").text(function(d, i) { |
|
return d.properties.WARD |
|
}) |
|
} |
|
|
|
wards.selectAll("path").attr("d", function(d, i) { |
|
return path(geometryCache[i] == null ? d.geometry : geometryCache[i]) |
|
}).attr("transform", transformWard).on("mouseover", function(){ |
|
if($(this).attr("class") == "ward-outline"){ |
|
$(this).attr("class", "ward-fill") |
|
} else { |
|
$(this).attr("class", "ward-outline") |
|
} |
|
}).on("mouseout", function(){ |
|
if($(this).attr("class") == "ward-outline"){ |
|
$(this).attr("class", "ward-fill") |
|
} else { |
|
$(this).attr("class", "ward-outline") |
|
} |
|
}).on("click", function(){ |
|
if($(this).attr("class") == "ward-outline"){ |
|
$("#wards path").attr("class", "ward-outline") |
|
$(this).attr("class", "ward-fill") |
|
} else { |
|
$("#wards path").attr("class", "ward-fill") |
|
$(this).attr("class", "ward-outline") |
|
} |
|
}) |
|
} |
|
|
|
$("#star").toggle(function() { |
|
alignToGrid = false |
|
drawWards() |
|
}, function() { |
|
alignToGrid = true |
|
drawWards() |
|
}) |
|
|
|
$("#morphs").change(function() { |
|
val = $(this).val() |
|
wards.selectAll("path").attr("d", function(d, i) { |
|
//so the shape is maintained when scaling/translating the map |
|
geometryCache[i] = {'type': "Polygon", 'coordinates': [d3.interpolate(wards_json[0][0][d.properties.WARD - 1].geometry.coordinates[0], wards_json[1][0][d.properties.WARD - 1].geometry.coordinates[0])(val)]} |
|
return path(geometryCache[i]) |
|
}) |
|
}) |
|
|
|
$(".years").click(function(){ |
|
d3.select("#morphs").transition().ease("sin").duration(2000).tween("withchange", function() { |
|
return function(t) { |
|
$(this).trigger("change") |
|
}; |
|
}).attr("value", $(this).text() == "2005-2014" ? 0 : 1) |
|
}) |
|
}) |
|
</script> |
|
<div style="text-align:center;font-size: 19px;"> |
|
<span class="years">2005-2014</span> |
|
<input id="morphs" type="range" min="0" max="1" step=".01" value="0" style="vertical-align: bottom"/> |
|
<span class="years">2015-2025</span> |
|
<br><span id="star" style="color:#C00000;font-size:32px;">✶</span> |
|
</div> |
|
</body> |
|
</html> |