Data visualisation of termination figures from Scotland 2013-2017.
Figures from the Scottish Government's Sexual Health and Blood Borne Virus (SHBBV) open access Data Portal: https://hpsmicrosites.scot.nhs.uk/shbbv-framework-data-portal.aspx
Data visualisation of termination figures from Scotland 2013-2017.
Figures from the Scottish Government's Sexual Health and Blood Borne Virus (SHBBV) open access Data Portal: https://hpsmicrosites.scot.nhs.uk/shbbv-framework-data-portal.aspx
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<title>Scotland termination figures</title> | |
<meta charset="utf-8"> | |
<!-- Load d3.js --> | |
<script src="https://d3js.org/d3.v4.js"></script> | |
<!-- Color Scale --> | |
<script src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script> | |
<!-- Font --> | |
<link href="https://fonts.googleapis.com/css?family=Merriweather&display=swap" rel="stylesheet"> | |
<style> | |
body { | |
font-family: 'Merriweather', serif; | |
} | |
.yaxis { | |
font-family: 'Merriweather', serif; | |
} | |
.xaxis { | |
font-family: 'Merriweather', serif; | |
} | |
.ylabel { | |
font-size : 80%; | |
} | |
.caption { | |
font-size : 120%; | |
} | |
.subcaption { | |
font-size : 80%; | |
} | |
a { | |
text-decoration: none; | |
color : black; | |
} | |
a:hover { | |
text-decoration: underline; | |
} | |
a:active { | |
color: black; | |
} | |
a:visited { | |
color: black; | |
} | |
</style> | |
</head> | |
<body> | |
<!-- Initialize a menu for the measure --> | |
<select id="measureButton"></select> | |
<!-- Create a div where the graph will take place --> | |
<div id="my_dataviz"></div> | |
<p class="caption">Termination figures for Scotland by Local Authority 2010-2019.</p> | |
<p class="subcaption">Data from the <a href="https://beta.isdscotland.org/find-publications-and-data/population-health/sexual-health/termination-of-pregnancy-statistics/">Termination of Pregnancy report, Public Health Scotland Data and Intelligence, published 25 August 2020.</a>.</p> | |
<script> | |
var codesMap = new Map(); | |
// set the dimensions and margins of the graph | |
var margin = {top: 20, right: 100, bottom: 30, left: 70}, | |
width = 1200 - margin.left - margin.right, | |
height = 600 - margin.top - margin.bottom; | |
chartWidth = width / 2; | |
// append the svg object to the body of the page | |
var svg = d3.select("#my_dataviz") | |
.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 + ")"); | |
// create a tooltip | |
var Tooltip = d3.select("#my_dataviz") | |
.append("div") | |
.style("opacity", 0) | |
.attr("class", "tooltip") | |
.style("background-color", "white") | |
.style("border", "solid") | |
.style("border-width", "1px") | |
.style("border-radius", "0px") | |
.style("padding", "5px") | |
.style("position", "absolute") | |
.style("font-family", "Merriweather") | |
.style("font-size", "80%") | |
// create a tooltip for the map | |
var mapTooltip = d3.select("#my_dataviz") | |
.append("div") | |
.style("opacity", 0.1) | |
.attr("class", "tooltip") | |
.style("background-color", "white") | |
.style("border", "none") | |
.style("border-width", "1px") | |
.style("border-radius", "0px") | |
.style("padding", "5px") | |
.style("position", "absolute") | |
.style("font-family", "Merriweather") | |
.style("font-size", "80%") | |
// Three function that change the tooltip when user hover / move / leave a cell | |
var mouseover = function(d) { | |
Tooltip | |
.style("opacity", 1) | |
} | |
var mousemove = function(d) { | |
Tooltip | |
.html(d.year + ": " + d.value) | |
.style("left", (d3.mouse(this)[0]+70) + "px") | |
.style("top", (d3.mouse(this)[1]) + "px") | |
} | |
var mouseleave = function(d) { | |
Tooltip | |
.style("opacity", 0) | |
} | |
// Three function that change the map tooltip when user hover / move / leave a feature | |
var mapmouseover = function(d) { | |
mapTooltip | |
.style("opacity", 1) | |
d3.select(this) | |
.style('fill', colours.teal) | |
.style("opacity", d.properties.LAD13CD == selectedRegion ? 1 : 0.5) | |
} | |
var mapmousemove = function(d) { | |
mapTooltip | |
.html(codesMap.get(d.properties.LAD13CD)) | |
.style("left", (d3.mouse(this)[0] + 20) + "px") | |
.style("top", (d3.mouse(this)[1]) + "px") | |
} | |
var mapmouseleave = function(d) { | |
mapTooltip | |
.style("opacity", 0) | |
d3.select(this) | |
.style('fill', getRegionColour(d)) | |
.style("opacity", 1) | |
} | |
var mapmouseclick = function(d) { | |
selectedRegion = d.properties.LAD13CD; | |
optionsAndUpdate(); | |
updateRegionColours(); | |
} | |
var measuresList = [ | |
[ | |
"totals", | |
"Total number of terminations" | |
], | |
[ | |
"rates", | |
"Termination rate per 1000 women aged 15-44" | |
] | |
]; | |
var measures = new Map(measuresList); | |
var firstMeasure = "totals"; | |
var firstGroup = "S12000033"; | |
var projection = d3.geoMercator() | |
.scale(2600) | |
.translate([width, height / 2]) | |
.center([-4, 58]); | |
var geoGenerator = d3.geoPath() | |
.projection(projection); | |
function getRegionColour(d) { | |
return d.properties.LAD13CD == selectedRegion ? colours.teal : colours.grey; | |
} | |
function updateGeo(geojson) { | |
var u = d3.select('svg') | |
.selectAll('path') | |
.data(geojson.features); | |
u.enter() | |
.append('path') | |
.attr("class", "region") | |
.attr('d', geoGenerator) | |
.style("fill", getRegionColour) | |
.on("mouseover", mapmouseover) | |
.on("mousemove", mapmousemove) | |
.on("mouseleave", mapmouseleave) | |
.on("click", mapmouseclick) | |
} | |
function updateRegionColours() | |
{ | |
var u = d3.select('svg') | |
.selectAll('path.region') | |
.transition() | |
.duration(500) | |
.style("opacity", 1) | |
.style("fill", getRegionColour); | |
} | |
var colours = { teal : "#69b3a2", grey : "#aaa" }; | |
var rates_data; | |
var totals_data; | |
var line; | |
var dot; | |
var x; | |
var y; | |
var selectedRegion; | |
var yLabel; | |
var chartLabel; | |
var geojson; | |
function optionsAndUpdate() | |
{ | |
// recover the option that has been chosen | |
var selectedMeasure = d3.select("#measureButton").property("value") | |
// run the updateChart function with this selected option | |
update(selectedRegion, selectedMeasure) | |
} | |
// A function that updates the chart | |
function update(selectedGroup, selectedMeasure) { | |
// Create new data with the selection? | |
var theData = selectedMeasure == "totals" ? totals_data : rates_data; | |
var theMax = selectedMeasure == "totals" ? 2000 : 20; | |
var dataFilter = theData.map(function(d){return {year: d.year, value:d[selectedGroup]} }) | |
// Y axis | |
var y = d3.scaleLinear() | |
.domain( [0,theMax]) | |
.range([ height, 0 ]); | |
svg.select(".yaxis") | |
.transition() | |
.duration(1000) | |
.call(d3.axisLeft(y)); | |
// Give these new data to update line | |
line | |
.datum(dataFilter) | |
.transition() | |
.duration(1000) | |
.attr("d", d3.line() | |
.x(function(d) { return x(+d.year) }) | |
.y(function(d) { return y(+d.value) }) | |
) | |
dot | |
.data(dataFilter) | |
.transition() | |
.duration(1000) | |
.attr("cx", function(d) { return x(+d.year) }) | |
.attr("cy", function(d) { return y(+d.value) }) | |
// update y label | |
ylabel | |
.transition() | |
.duration(500) | |
.style("opacity",0) | |
ylabel | |
.transition() | |
.delay(500) | |
.duration(500) | |
.style("opacity",1) | |
.text(measures.get(selectedMeasure)); | |
// update chart label | |
chartLabel | |
.transition() | |
.duration(500) | |
.style("opacity",0) | |
chartLabel | |
.transition() | |
.delay(500) | |
.duration(500) | |
.style("opacity",1) | |
.text(codesMap.get(selectedRegion)); | |
} | |
//Read the data | |
d3.csv('sco_lad_codes_and_names.csv', function(lookup) | |
{ | |
lookup.reduce(function(map, obj) { | |
codesMap.set(obj.code, obj.name); | |
}, {}); | |
selectedRegion = lookup[0].code; | |
d3.json('sco_lad.json', function(err, json) { | |
d3.csv("termination_rates.csv", function(r_data) { | |
d3.csv("termination_totals.csv", function(t_data) { | |
geojson = json; | |
updateGeo(json); | |
totals_data = t_data; | |
rates_data = r_data; | |
// Create data usng initial selection | |
var theData = firstMeasure == "totals" ? totals_data : rates_data; | |
var theMax = firstMeasure == "totals" ? 2000 : 20; | |
var dataFilter = theData.map(function(d){return {year: d.year, value:d[firstGroup]} }) | |
// List of groups (here I have one group per column) | |
var allGroup = totals_data.columns.slice(1) | |
// add the options to the measures button | |
d3.select("#measureButton") | |
.append("option") | |
.text("show totals") | |
.attr("value", "totals"); | |
d3.select("#measureButton") | |
.append("option") | |
.text("show rates") | |
.attr("value", "rates"); | |
// Add X axis --> it is a date format | |
x = d3.scaleLinear() | |
.domain([2010,2019]) | |
.range([ 0, chartWidth ]); | |
svg.append("g") | |
.attr("class", "xaxis") | |
.attr("transform", "translate(0," + height + ")") | |
.call(d3.axisBottom(x).ticks(5).tickFormat(d3.format("d"))); | |
// Add Y axis | |
y = d3.scaleLinear() | |
.domain( [0,theMax]) | |
.range([ height, 0 ]); | |
svg.append("g") | |
.attr("class", "yaxis") | |
.call(d3.axisLeft(y)); | |
// Initialize line with group a | |
line = svg | |
.append('g') | |
.append("path") | |
.datum(dataFilter) | |
.attr("d", d3.line() | |
.x(function(d) { return x(+d.year) }) | |
.y(function(d) { return y(+d.value) }) | |
) | |
.attr("stroke", "black") | |
.style("stroke-width", 4) | |
.style("fill", "none") | |
// Initialize dots with group a | |
dot = svg | |
.selectAll('circle') | |
.data(dataFilter) | |
.enter() | |
.append('circle') | |
.attr("cx", function(d) { return x(+d.year) }) | |
.attr("cy", function(d) { return y(+d.value) }) | |
.attr("r", 7) | |
.style("fill", colours.teal) | |
.on("mouseover", mouseover) | |
.on("mousemove", mousemove) | |
.on("mouseleave", mouseleave) | |
ylabel = svg | |
.append("text") | |
.attr("class", "ylabel") | |
.attr("text-anchor", "end") | |
.attr("y", -margin.left) | |
.attr("dy", ".75em") | |
.attr("transform", "rotate(-90)") | |
.text(measures.get(firstMeasure)); | |
chartLabel = svg | |
.append("text") | |
.attr("class", "chartlabel") | |
.attr("y", 0) | |
.attr("dy", ".75em") | |
.attr("x", 0) | |
.attr("dx", "1em") | |
.text(codesMap.get(selectedRegion)); | |
// add a listener to measure selector | |
d3.select('#measureButton').on("change", optionsAndUpdate) | |
}) | |
}) | |
}) | |
}) | |
</script> | |
</body> | |
</html> |
MIT License | |
Copyright (c) 2019 stuwilmur | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in all | |
copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | |
SOFTWARE. |
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)
(Sorry about that, but we can’t show files that are this big right now.)