Skip to content

Commit

Permalink
PE page
Browse files Browse the repository at this point in the history
  • Loading branch information
paulmwatson committed Apr 18, 2024
1 parent 2b3c6a1 commit 2f04a09
Show file tree
Hide file tree
Showing 8 changed files with 625 additions and 394 deletions.
161 changes: 161 additions & 0 deletions assets/js/embeds/chart-bubbles/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,161 @@
// Get JSON data from data-public-entities-bubbles attribute
const data = JSON.parse(
document.getElementById("vis").attributes["data-public-entities-bubbles"]
.value
);

function bubbleChart() {
const width = 300;
const height = 300;

// location to centre the bubbles
const centre = { x: width / 2, y: height / 2 };

// strength to apply to the position forces
const forceStrength = 0.03;

// these will be set in createNodes and chart functions
let svg = null;
let bubbles = null;
let nodes = [];

// charge is dependent on size of the bubble, so bigger towards the middle
function charge(d) {
return Math.pow(d.radius, 2.0) * 0.01;
}

// create a force simulation and add forces to it
const simulation = d3
.forceSimulation()
.force("charge", d3.forceManyBody().strength(charge))
.force("center", d3.forceCenter(centre.x, centre.y))
.force(
"x",
d3
.forceX()
.strength(forceStrength)
.x(centre.x)
)
.force(
"y",
d3
.forceY()
.strength(forceStrength)
.y(centre.y)
)
.force(
"collision",
d3.forceCollide().radius((d) => d.radius + 1)
);

// force simulation starts up automatically, which we don't want as there aren't any nodes yet
simulation.stop();

// set up colour scale
const fillColour = d3
.scaleOrdinal()
.domain([1, 2])
.range(["#76b643", "#ed9e31"]);

// data manipulation function takes raw data from csv and converts it into an array of node objects
// each node will store data and visualisation values to draw a bubble
// rawData is expected to be an array of data objects, read in d3.csv
// function returns the new node array, with a node for each element in the rawData input
function createNodes(rawData) {
// use max size in the data as the max in the scale's domain
// note we have to ensure that size is a number
const maxSize = d3.max(rawData, (d) => +d[1]);

// size bubbles based on area
const radiusScale = d3
.scaleSqrt()
.domain([0, maxSize])
.range([0, 80]);

// use map() to convert raw data into node data
const myNodes = rawData.map((d) => ({
...d,
radius: radiusScale(+d[1]),
size: +d[1],
x: Math.random() * 900,
y: Math.random() * 800,
}));

return myNodes;
}

// main entry point to bubble chart, returned by parent closure
// prepares rawData for visualisation and adds an svg element to the provided selector and starts the visualisation process
let chart = function chart(selector, rawData) {
// convert raw data into nodes data
nodes = createNodes(rawData);

// create svg element inside provided selector
svg = d3
.select(selector)
.append("svg")
.attr("width", width)
.attr("height", height);

// bind nodes data to circle elements
const elements = svg
.selectAll(".bubble")
.data(nodes, (d) => d[3])
.enter()
.append("g");

// html tooltip showing text on mouseover
elements
.append("title")
.text((d) => `${d[2]} R${parseInt(d[1]).toLocaleString()}`);

bubbles = elements
.append("circle")
.classed("bubble", true)
.attr("r", (d) => d.radius)
.attr("fill", (d) => fillColour(d[0]))
.on("mouseover", function(d) {
d3.select(this)
.attr("stroke", "rgba(0, 0, 0, 0.3)")
.attr("stroke-width", 2)
.style("cursor", "pointer");
})
.on("mouseout", function(d) {
d3.select(this).attr("stroke", "none");
})
.on("click", function(d) {
window.location.href = `/${
window.location.pathname.split("/")[1]
}/national/public-entities/${d[4]}`;
});

// set simulation's nodes to our newly created nodes array
// simulation starts running automatically once nodes are set
simulation
.nodes(nodes)
.on("tick", ticked)
.restart();
};

// callback function called after every tick of the force simulation
// here we do the actual repositioning of the circles based on current x and y value of their bound node data
// x and y values are modified by the force simulation
function ticked() {
bubbles.attr("cx", (d) => d.x).attr("cy", (d) => d.y);
}

// return chart function from closure
return chart;
}

// new bubble chart instance
let myBubbleChart = bubbleChart();

// function called once promise is resolved and data is loaded from csv
// calls bubble chart function to display inside #vis div
//function display(data) {
myBubbleChart("#vis", data);
//}

// load data
//d3.data('nodes-data.csv').then(display);
1 change: 1 addition & 0 deletions assets/js/scripts.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,3 +57,4 @@ import './components/Share/scripts.js';
import './scenes/homepage/Hero/scripts.js';

import './embeds/equitable-share/index.js';
import './embeds/chart-bubbles/index.js';
13 changes: 13 additions & 0 deletions assets/scss/components/universal/Page/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
}
}

.Page-pre-title,
.Page-title,
.Page-mainHeading {
color: #3f3f3f;
Expand All @@ -77,6 +78,18 @@
}
}

.Page-pre-title {
font-size: 1em;
color: #979797;
margin-bottom: 0;
margin-top: 25px;
font-weight: normal;

a {
color: #5e5d5d;
}
}

.Page-subHeading {
margin: 0 0 10px;
font-size: $font-size-medium;
Expand Down
1 change: 1 addition & 0 deletions assets/scss/styles.scss
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
@import 'utilities/scss/animation/background-pulse';
@import 'utilities/scss/animation/pop';

@import 'utilities/scss/utility-classes/styles';
@import 'utilities/scss/utility-classes/u-sReadOnly';
@import 'utilities/scss/utility-classes/u-listReset';
@import 'utilities/scss/utility-classes/u-listItemReset';
Expand Down
102 changes: 102 additions & 0 deletions assets/scss/utilities/scss/utility-classes/styles.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
.is-width-100-percent {
width: 100%;
}

.is-text-transform-uppercase {
text-transform: uppercase;
}

.is-padding-left-40 {
padding-left: 40px;
}

.is-padding-right-40 {
padding-right: 40px;
}

.is-grid-2fr-1fr {
display: grid;
grid-template-columns: 2fr 1fr;
grid-template-rows: 1fr;
grid-column-gap: 1em;
grid-row-gap: 0px;
}

.is-font-size-1-5-em {
font-size: 1.5em;
}

.is-font-size-1-em {
font-size: 1em;
}

.is-text-align-center {
text-align: center;
}

.is-orange-circle {
background-color: #ed9e31;
border-radius: 50%;
width: 1em;
height: 1em;
display: inline-block;
vertical-align: -0.15em;
margin-right: 0.25em;
}

.Section.is-padding-less {
@media ($mobile-breakpoint) {
padding: 20px;
}

@media ($tablet-breakpoint) {
padding: 20px;
}
}

.is-margin-0 {
margin: 0;
}

.has-border-bottom-1 {
tr {
&:not(:last-child) {
td {
border-bottom: 1px solid #e5e5e5;
}
}

th {
border-bottom: 1px solid #e5e5e5;
}
}
}

.has-line-height-2 {
tr {
td, th {
line-height: 2em;
}
}
}

.has-font-size-0-75-em {
th,
td {
font-size: 0.75em;
}
}

.is-padding-bottom-0 {
padding-bottom: 0 !important;
}

.is-text-no-wrap {
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}

.is-text-overlofw-ellipsis {
white-space: nowrap;
}
Loading

0 comments on commit 2f04a09

Please sign in to comment.