Skip to content
Permalink
Branch: master
Find file Copy path
Find file Copy path
Fetching contributors…
Cannot retrieve contributors at this time
229 lines (199 sloc) 6.6 KB
<!DOCTYPE html>
<html>
<head>
<style>
body {
font-family: sans-serif;
}
p {
max-width:600px;
}
.subtitle {
font-style:italic;
color:#666;
}
.borough-label {
fill: #111;
}
.borough-label-outline{
stroke: #FFF;
stroke-width: 4px;
stroke-linejoin: round;
}
line.tick{
stroke: #000;
stroke-width: 1px;
}
</style>
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://unpkg.com/@aftertheflood/londonsquared@0.1.1/dist/index.js"></script>
</head>
<body>
<h1>London penalty charge notices by borough</h1>
<p class="subtitle">2018 (thousands)</p>
<p>This example illustrates how you might use the After the Flood <a href="https://github.com/aftertheflood/londonsquared">London Squared</a> module for D3 to visualise a few variable accross all London boroughs.</p>
<div class="vizualisation-container"></div>
</body>
<script>
function drawPCNData(boroughProfiles){
const width = 600;
const height = 525;
const svg = d3.select('.vizualisation-container')
.append('svg')
.attr('width', width)
.attr('height', height);
d3.csv('data/parking-enforcement-activity-2018.csv')
.then((data)=>{
const categories = ['Total parking PCNs', 'Bus lane PCNs', 'Moving traffic PCNs'];
const choroplethProperty = 'Population density (per hectare) 2015';
const mergedData = mergeCSVs(data, boroughProfiles, 'Code', 'Code');
const LS = atf.londonSquared()
.data(mergedData, d=>d.Code) //the second argument is an accessor for the local authority code associated with the data row. by default this is d=>d.code
.width(width)
.height(height);
svg.call(LS); // draw the cartogram shapes
// construct the necessary scales
const valueScale = d3.scaleLinear()
.domain([0, data.reduce((accumulator, currentRow)=>{
const rowMax = d3.max(categories, category=> Number(currentRow[category]));
return Math.max(accumulator, rowMax);
}, 0)])
.range([0, LS.blockSize()]);
const categoryScale = d3.scaleBand()
.domain(categories)
.range([0, LS.blockSize()])
.padding([0.1]);
const categoryColourScale = d3.scaleOrdinal()
.domain(categories)
.range(['rgb(153, 107, 195)','rgb(56, 106, 197)','rgb(93, 199, 76)']);
const colourBracket = ["#FFFFFF","#666666"];
const densityScale = d3.scaleSequential(d3.interpolateRgbBasis(colourBracket))
.domain([0,150]);
// define the gradient fill
svg.select('defs')
.append("linearGradient")
.attr("id", "linear-gradient")
.attr("x1", "0%")
.attr("y1", "0%")
.attr("x2", "100%")
.attr("y2", "0%")
.call(gradient=>{
gradient.append('stop')
.attr('offset', '0%')
.attr('stop-color', colourBracket[0]);
gradient.append('stop')
.attr('offset', '100%')
.attr('stop-color', colourBracket[1]);
});
// ordinalcreate a legend for the category colours
svg.append('g')
.attr('class', 'legend-ordinal')
.attr('transform',(d,i)=>`translate(0, 15)`)
.selectAll('g')
.data(categoryColourScale.domain())
.enter()
.append('g')
.attr('transform',(d,i)=>`translate(0, ${i*20})`)
.call(function(parent){
parent.append('rect')
.attr('width', 15)
.attr('height', 15)
.attr('x', 0)
.attr('y', 0)
.attr('fill', d=>categoryColourScale(d));
parent.append('text')
.attr('dx', 25)
.attr('dy', 15)
.text(d=>d);
});
// create a legend for the choropleth
svg.append('g')
.attr('class', 'legend-continuous')
.attr('transform','translate(350, 475)')
.call(function(parent){
const legendWidth = 200;
const tickLength = 20;
const blockHeight = 15;
const blockY = 10;
parent.append('text')
.text(choroplethProperty);
parent.append('rect')
.attr('width', legendWidth)
.attr('height', blockHeight)
.attr('x', 0)
.attr('y', blockY)
.attr('fill', 'url(#linear-gradient)');
parent.append('line')
.attr('class','tick')
.attr('x1', '0')
.attr('x2', '0')
.attr('y1', blockY)
.attr('y2', blockY+tickLength);
parent.append('line')
.attr('class', 'tick')
.attr('x1', legendWidth)
.attr('x2', legendWidth)
.attr('y1', blockY)
.attr('y2', blockY+tickLength);
parent.append('text')
.attr('y', blockHeight * 3)
.text(densityScale.domain()[0])
parent.append('text')
.attr('y', blockHeight * 3)
.attr('x', legendWidth)
.attr('text-anchor', 'end')
.text(densityScale.domain()[1])
});
// add the data to the map
LS.masked()
.call(function(parent){
categories.forEach((category)=>{
parent.append('rect')
.attr('x',categoryScale(category))
.attr('y',d=>(LS.blockSize()-valueScale(d.data[category])))
.attr('width',categoryScale.bandwidth())
.attr('height',d=>valueScale(d.data[category]))
.attr('fill',categoryColourScale(category));
})
});
LS.foreground()
.call(function(parent){
parent.append('text')
.attr('class', 'borough-label-outline')
.attr('x', 5)
.attr('y', 15)
.text(d => d.LSAbbreviation);
parent.append('text')
.attr('class', 'borough-label')
.attr('x', 5)
.attr('y', 15)
.text(d => d.LSAbbreviation);
});
LS.background()
.call(function(parent){
parent.attr('fill', d=> densityScale(d.data[choroplethProperty]));
});
})
.catch(err=>{
console.log(err);
});
}
function mergeCSVs(data1, data2, key1, key2){
const merged = [];
const data2Lookup = data2.reduce((lookup, row)=>{
lookup[row[key2]] = row;
return lookup;
}, {});
data1.forEach(row=>{
merged.push(Object.assign(row, data2Lookup[row[key1]]));
});
return merged;
}
window.onload = ()=>{
d3.csv('data/london-borough-profiles.csv')
.then((data)=>{
drawPCNData(data);
});
}
</script>
</html>
You can’t perform that action at this time.