Built with blockbuilder.org
forked from GitNoise's block: 2020/W4: Bridges for Prosperity - Rwanda
xxxxxxxxxx
<head>
<meta charset="utf-8">
<script src="https://d3js.org/d3.v5.min.js"></script>
<script src="https://d3js.org/topojson.v1.min.js"></script>
<script src="https://d3js.org/d3-geo.v1.min.js"></script>
<script src="https://d3js.org/d3-geo-projection.v2.min.js"></script>
<script src="https://d3js.org/d3-array.v2.min.js"></script>
<style>
:root {
--red: rgba(250, 74, 77);
--blue: rgba(0,0,0);
--yellow: rgba(238, 190, 16);
--red_w_opacity: rgba(250, 74, 77, 0.5);
--blue_w_opacity: rgba(0, 0, 0, 0.4);
--yellow_w_opacity: rgba(238, 190, 16, 0.4);
}
* {
box-sizing: border-box;
}
body {
margin:0;
font-family: sans-serif;
background:rgba(0,0,0,0.5);
}
#container {
display: flex;
flex-wrap: wrap;
width: 100%;
}
.item {
width: calc(33% - 64px);
position: relative;
margin: 32px;
padding: 4px 8px;
}
.details {
margin-top: 8px;
padding-top: 8px;
}
.noBridges {
font-size: 1.2em;
font-weight: bold;
padding: 2px 3px;
}
.completed {
background: var(--yellow_w_opacity);
padding: 3px 5px;
display: inline-block;
border-radius: 4px;
width: calc(50%-2px);
border: 1px solid var(--yellow)
}
.inProgress {
background: var(--red_w_opacity);;
padding: 3px 5px;
display: inline-block;
border-radius: 4px;
width: calc(50%-2px);
margin-left: 4px;
border: 1px solid var(--red)
}
svg.country path {
--filter: url(#glow);
}
.countryName {
position: absolute;
margin: 4px;
padding: 5px;
text-transform: uppercase;
font-weight: bold;
font-family: verdana;
font-size: 16px;
color: white;
}
.cumulative {
fill: var(--blue_w_opacity);
}
svg.country path {
stroke: var(--blue);
fill: var(--blue_w_opacity);
}
circle {
r: 6;
stroke: var(--red);
fill: var(--red_w_opacity);
}
circle.completed {
stroke: var(--yellow);
fill: var(--yellow_w_opacity);
}
.chart {
margin-top: 4px;
}
.yaxis path, .yaxis line {
stroke: none;
}
.minichartHeading {
margin-top: 16px;
font-weight: bold;
}
.minichartSubHeading {
font-style: italic;
color: rgba(0,0,0,0.6)
}
</style>
</head>
<body>
<svg id="effects" height="0">
<defs>
<filter id="glow">
<!--Blur effect-->
<feGaussianBlur stdDeviation="10" result="blur3" />
<!--Lighting effect-->
<feSpecularLighting result="spec3" in="blur3" specularConstant="1.2" specularExponent="7" lighting-color="#FFF">
<!--Light source effect-->
<feSpotLight x="400" y="400" z="900" limitingConeAngle="9" />
</feSpecularLighting>
<!--Composition of inputs-->
<feComposite in="SourceGraphic" in2="spec3" operator="arithmetic" k1="0" k2="1" k3="1" k4="0" />
</filter>
</defs>
</svg>
<div id="container">
</div>
<script>
const geo = d3.json('world_admin.json');
const data = d3.csv('bridges.csv');
Promise.all([geo, data]).then(values => {
const countryData = d3.group(values[1], d => d.Country);
const geoData = values[0];
const mapData = topojson.feature(geoData, geoData.objects.world_admin);
const countryArray = Array.from(countryData.keys()).map(key => {
return {
key,
feature: mapData.features
.find(d => d.properties.ADMIN === key),
data: countryData.get(key)
};
});
draw(countryArray);
});
const draw = data => {
var svg = d3.select("#container")
.selectAll('div')
.data(data)
.join(
enter =>
{
enter.append('div')
.classed('item', true)
.each((datum,i, arr) => {
const div = d3.select(arr[i]);
div.append('div')
.classed('countryName', true)
.html(datum.key)
const size =
div.node().getBoundingClientRect().width - 16;
const projection = d3.geoCylindricalStereographic();
projection.fitSize(
[size, size],
datum.feature);
const geoPath = d3.geoPath(projection);
const svg = div.append('svg').classed("country", true)
.attr('width', size)
.attr('height', size);
svg
.append('path')
.attr('d', () => geoPath(datum.feature))
svg.selectAll('circle')
.data(datum.data)
.enter()
.append('circle')
.classed('completed', d => d.Stage === 'Complete')
.attr('cx', d => projection([d["GPS (Longitude)"],d["GPS (Latitude)"]])[0])
.attr('cy', d => projection([d["GPS (Longitude)"],d["GPS (Latitude)"]])[1]);
const details = div.append('div').classed("details", true);
const noBridges = datum.data.length;
const noCompleted = datum.data.filter(d => d.Stage === 'Complete').length;
const noInProgress = noBridges - noCompleted;
details.append('div').classed('noBridges', true)
.html(`${noBridges} Bridges`);
details.append('div').classed('completed', true)
.html(`${noCompleted} completed`);
details.append('div').classed('inProgress', true)
.html(`${noInProgress} in progress`);
var margin = {top: 10, right: 30, bottom: 20, left: 30},
width = size - margin.left - margin.right,
height = size/2 - margin.top - margin.bottom;
miniChart(details, datum.data, margin, width, height, 750);
})
});
}
const miniChart = (elem, data, margin, width, height, maxY) => {
const extent = [2000, 2020];
data = data.sort((a, b) => d3.descending(
a["B2P Fiscal Year"],
b["B2P Fiscal Year"]));
let cumulative = 0;
const newData = d3.range(extent[0], extent[1] + 1, 1).map(d => {
const currCumulative = cumulative;
const obj = data.find(f => +f["B2P Fiscal Year"] === d);
const val = obj ? +obj["Span (m)"] : 0;
cumulative += val;
return {
year: d,
value: val + currCumulative
};
});
elem.append('div').classed('minichartHeading', true)
.html('Total bridge span')
elem.append('div').classed('minichartSubHeading', true)
.html('Total: ' + cumulative + 'm')
var chart = elem
.append("svg").classed('chart', true)
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform",
"translate(" + margin.left + "," + margin.top + ")");
var x = d3.scaleLinear()
.domain(extent)
.range([ 0, width ]);
chart.append("g")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x).ticks(3));
var y = d3.scaleLinear()
.domain([0, maxY])
.range([ height, 0 ]);
chart.append("g").classed("yaxis", true)
.call(d3.axisLeft(y).tickValues(d3.range(0, maxY + 250, 250)));
chart.append("path")
.classed('cumulative', true)
.datum(newData)
.attr("stroke-width", 1.5)
.attr("d", d3.area()
.x(d => x(d.year))
.y0(y(0))
.y1(d => y(d.value))
)
}
</script>
</body>
https://d3js.org/d3.v5.min.js
https://d3js.org/topojson.v1.min.js
https://d3js.org/d3-geo.v1.min.js
https://d3js.org/d3-geo-projection.v2.min.js
https://d3js.org/d3-array.v2.min.js