An interactive grid map of London. Hovering over each constituency rectangle transforms the geometry into the constituency area (using Flubber for smooth animation) which have been translated to their position in the grid map.
forked from tlfrd's blocks: London Constituency Grid Layout, London Grid with Geo Shapes
TODO:
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="https://d3js.org/topojson.v2.min.js"></script>
<script src="https://unpkg.com/flubber@0.3.0"></script>
<script src="london-grid.js"></script>
<style>
body {
margin: 0;
}
.constituency-label {
font-family: monospace;
pointer-events: none;
}
.constituency {
fill: white;
fill-opacity: 0;
stroke: black;
}
</style>
</head>
<body>
<script>
var cfg = {
gridLength: 11,
gridHeight: 10,
padding: 10
}
var margin = {top: 50, right: 200, bottom: 50, left: 200};
var width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var rectWidth = (width / cfg.gridLength) - cfg.padding,
rectHeight = (height / cfg.gridHeight) - cfg.padding;
var url = "topo_wpc_london.json";
function calculateCoords(d) {
var x = d.x * (rectWidth + cfg.padding);
var y = d.y * (rectHeight + cfg.padding);
return [x, y];
}
var londonGridLookup = {};
londonGrid.forEach(function(d) {
londonGridLookup[d.ons_code] = d;
});
var constituencies = svg.append("g").attr("class", "constituencies");
var geoLondonExtent;
d3.json(url, function(topoLondon) {
var geoLondon = topojson.feature(topoLondon, topoLondon.objects.wpc).features;
geoLondonExtent = topojson.feature(topoLondon, topoLondon.objects.wpc);
var rectangles = constituencies.selectAll("path")
.data(geoLondon)
.enter().append("path")
.attr("class", "constituency");
rectangles
.attr("d", d => d.rectangle = rectPath(londonGridLookup[d.id].position))
.on("mouseover", function(d) {
d.geoArea = localProjection(d, londonGridLookup[d.id].position);
d3.select(this)
.raise()
.transition()
.attrTween("d", () => flubber.interpolate(d.rectangle, d.geoArea))
})
.on("mouseout", function(d) {
d3.select(this)
.transition()
.attrTween("d", () => flubber.interpolate(d.geoArea, d.rectangle));
})
var text = constituencies.selectAll("text")
.data(geoLondon)
.enter().append("text")
.attr("class", "constituency-label")
.attr("x", d => calculateCoords(londonGridLookup[d.id].position)[0] + rectWidth / 2)
.attr("y", d => calculateCoords(londonGridLookup[d.id].position)[1] + rectHeight / 2)
.attr("dy", rectHeight / 8)
.attr("text-anchor", "middle")
.text(d => d.properties.PCON13NM.slice(0, 2));
});
function localProjection(geometry, position) {
var centroid = d3.geoPath().centroid(geometry);
var projection = d3.geoAlbers()
.center(centroid)
.rotate([0, 0])
.fitExtent([[0, 0], [width, height]], geoLondonExtent);
var path = d3.geoPath()
.projection(projection);
var projectedCentroid = path.centroid(geometry);
var x = calculateCoords(position)[0] + rectWidth / 2;
var y = calculateCoords(position)[1] + rectHeight / 2;
projection.translate([x, y])
return path(geometry);
}
function rectPath(position) {
var x1 = calculateCoords(position)[0],
y1 = calculateCoords(position)[1],
x2 = calculateCoords(position)[0] + rectWidth,
y2 = calculateCoords(position)[1] + rectHeight;
var coord1 = [x1, y1],
coord2 = [x2, y1],
coord3 = [x2, y2],
coord4 = [x1, y2],
coord5 = [x1, y1];
var coordinates = [coord1, coord2, coord3, coord4, coord5];
var pathGen = d3.geoPath().projection(null);
var rectangle = pathGen({
type:"Polygon",
coordinates: [coordinates]
});
return rectangle;
}
</script>
</body>
https://d3js.org/d3.v4.min.js
https://d3js.org/topojson.v2.min.js
https://unpkg.com/flubber@0.3.0