Skip to content

Instantly share code, notes, and snippets.

@stuwilmur
Last active September 16, 2020 15:45
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save stuwilmur/3487056775bd83ad986d5efd0c1c72f0 to your computer and use it in GitHub Desktop.
Save stuwilmur/3487056775bd83ad986d5efd0c1c72f0 to your computer and use it in GitHub Desktop.
termination-statistics
<!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.
Display the source blob
Display the rendered blob
Raw
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

View raw

(Sorry about that, but we can’t show files that are this big right now.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment