Insert cell
Insert cell
Insert cell
Insert cell
xLimit = 2060
Insert cell
chart = {
const svg = d3.create("svg").attr("viewBox", [0, 0, width, height]);

// The bars themselves
svg
.append("g")
.selectAll("g")
.data(series)
.join("g")
// Each indivdual bar
.attr("fill", (d, i) => barColours[i % 3])
.classed("bar-group",true)

.selectAll("rect")
.data(d => d)
.join("rect")
.attr("x", d => x(d[0]))
.attr("y", (d, i) =>y(d.data.force))
.attr("width", d => x(d[1]) - x(d[0]))
.attr("height", y.bandwidth())
// National average line
svg
.append("line")
.attr("x1", x(medianTotalCed))
.attr("y1", graph.margin.top + 2)
.attr("x2", x(medianTotalCed))
.attr("y2", height - graph.margin.bottom)
.style("stroke-width", 1)
.style("stroke-dasharray", ("1,2"))
.style("stroke", colours.primary)
.style("fill", "none")
// "Average" text
svg
.append("text")
.attr("x", x(medianTotalCed) + 5)
.attr("y", height - graph.margin.bottom - 5)
.text("Average")
.attr("text-anchor", "start")
.attr("font-family", graph.font)
.attr("font-size", graph.fontSize)
.style("fill", colours.secondary)
// Text - discharged
svg
.append("g")
.attr("fill", colours.primary)
.attr("text-anchor", "start")
.attr("font-family", "sans-serif")
.attr("font-size", graph.fontSize)
.selectAll("text")
.data(series[1])
.join("text")
.attr("x", d => x(d[0]))
.attr("y", (d, i) => y(d.data['force']) + y.bandwidth() / 2)
.attr("dy", "0.35em")
.attr("dx", 5)
.text(d => d[0])
// Text - unknown
svg
.append("g")
.attr("fill", colours.primary)
.attr("text-anchor", "start")
.attr("font-family", "sans-serif")
.attr("font-size", graph.fontSize)
.selectAll("text")
.data(series[1])
.join("text")
.attr("x", d => x(d[0]))
.attr("y", (d, i) => y(d.data['force']) + y.bandwidth() / 2)
.attr("dy", "0.35em")
.attr("dx", 5)
.text(d => d[0])
// Text - total
svg
.append("g")
.attr("fill", colours.primary)
.attr("text-anchor", "start")
.attr("font-family", graph.font)
.attr("font-size", graph.fontSize)
.selectAll("text")
.data(ced_by_force_2019.sort((a,b) => b.Total - a.Total))
.join("text")
.attr("x", d => x(d.Total))
.attr("y", (d, i) => y(d.force) + y.bandwidth() / 2)
.attr("dy", "0.35em")
.attr("dx", 5)
.text(d => d.Total)
.call(text => text.filter(d => d.Total > xLimit)
.text(d => d.Total + " →")
.attr("x", width - 10)
.attr("fill", "white")
.attr("text-anchor", "end"));

// Draw axes
svg.append("g").call(xAxis);
svg.append("g").call(yAxis);

return svg.node()
}
Insert cell
// taserUsePerHundredChart.update(order)
Insert cell
getCedPer100ByForce = force =>getCedPer100ByForceYear(force,"2019")
Insert cell
getCedPer100ByForce("Greater Manchester")
Insert cell
getCedLeaderboardPosition = (name) => cedLeaderBoard.findIndex((f) => f.force === name) + 1
Insert cell
getCedLeaderboardPosition("Greater Manchester")
Insert cell
Insert cell
Insert cell
cedLeaderBoard = ced_per_force_by_year.find(yvalue=>yvalue.label=="2019")
.data.sort((a,b)=>b.Total-a.Total)

Insert cell
getForceStats = (force) => taserUse(force)
Insert cell
// getPopByForce = force => {
// let pop = officerCounts.find(p => p["Force/Region"] === force);
// if (pop == undefined) {
// return NaN;
// } else {
// // Using March as closer to the reporting period for the taser stats
// return pop["March 2019"] / 100;
// }
// }
Insert cell
legend = swatches({
color: d3.scaleOrdinal(["Discharged", "Not recorded", "Not discharged"], barColours)
})
Insert cell
Insert cell
// This object provides the stacked bars
series = d3
.stack()
.keys(typesOfCedUse)(ced_by_force_2019)
.map(d => (d.forEach(v => (v.key = d.key)), d))
Insert cell
colour = d3.scaleOrdinal(colours).domain(ced_by_force_2019.columns)
Insert cell
format = x.tickFormat(20, series.format)
Insert cell
// Transition function to shuffle bars around https://observablehq.com/@d3/bar-chart-race-explained
// Needs to take a transition and returns an ordered transition between the current data sort and the previous.
bar = f=>f
Insert cell
x = d3
.scaleLinear()
.domain([0, xLimit])
.range([graph.margin.left, width - graph.margin.right])
Insert cell
y = d3
.scaleBand()
.domain(ced_by_force_2019.map(d => d['force']))
.rangeRound([graph.margin.top, height - graph.margin.bottom])
.padding(0.1)
Insert cell
xAxis = g =>
g
.attr("transform", `translate(0,${graph.margin.top})`)
.call(d3.axisTop(x).ticks(width / 80, series.format))
.call(g => g.select(".domain").remove())
Insert cell
Insert cell
height = Math.ceil((ced_by_force_2019.length + 0.1) * graph.barHeight) + graph.margin.top + graph.margin.bottom
Insert cell
Insert cell
Insert cell
barColours = [colours.primary, colours.primary50, colours.primary00]
Insert cell
ced_by_force_2019 = {
let data = ced_per_force_by_year.find(e=>e.label=="2019").data

data.columns = ['Police force', 'Total', 'Total discharge', 'Not stated', 'Total non-discharge'];
data.sort((a, b) => {
return (b.Total - a.Total)
})
return data;
}
Insert cell
Insert cell
// forces = ["Cleveland", "Durham", "Northumbria", "Cheshire", "Cumbria", "Greater Manchester",
// "Lancashire", "Merseyside", "Humberside", "North Yorkshire",
// "South Yorkshire", "West Yorkshire", "Derbyshire", "Leicestershire", "Lincolnshire",
// "Northamptonshire", "Nottinghamshire", "Staffordshire", "Warwickshire", "West Mercia",
// "West Midlands", "Bedfordshire", "Cambridgeshire", "Essex", "Hertfordshire",
// "Norfolk", "Suffolk", "City of London", "Metropolitan", "Hampshire",
// "Kent", "Surrey", "Sussex", "Thames Valley", "Avon & Somerset", "Devon & Cornwall",
// "Dorset", "Gloucestershire", "Wiltshire", "Dyfed-Powys", "Gwent", "North Wales", "South Wales"]
Insert cell
// officerCounts = d3.csv(
// "https://data.resistancelab.network/cleaned_data/police-population/officer-fte/2019.csv",
// d3.autoType
// )
Insert cell
// ced_by_region_recent = d3.csv(
// "https://data.resistancelab.network/cleaned_data/use-of-force/by-ced-type-force-region/april2018-march2019.csv",
// d3.autoType
// )
Insert cell
Insert cell
import { swatches } from "@d3/color-legend"
Insert cell
import {
getStats,
getCedPer100ByForceYear,
ced_per_force_by_year,
forces,
typesOfCedUse,
taserUse,
colours,
graph,
d3
} from '@resistancelab/resistance-lab-defaults'
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more