(This chart is a part of the d3-charts
collection available here.)
A map of the 14,815 people who sought asylum in Denmark in 2014.
This chart is based on the data here.
The chart is by me, ndarville, @pessimism.
Not included:
xxxxxxxxxx
<!--
- Scale SVG dynamically/responsively
- Implement threshold.js - use inline or import as function
- d3.tip
- Convert geojson to topojson: https://github.com/mbostock/world-atlas
-->
<html lang="en">
<head>
<meta charset="utf-8">
<style>
svg {
margin-left: auto; margin-right: auto;
display: block;
}
line,
rect {
shape-rendering: crispEdges;
}
.states {
fill: none; /** Hides TopoJSON artifacts */
stroke: #000;
}
.counties {}
path.country,
path.state {
stroke-width: .5;
stroke: #FFF;
}
path.country:hover,
path.state:hover {
fill: pink !important;
}
/** State chart threshold BEGIN */
text {
font: 10px sans-serif;
}
.caption {
font-weight: bold;
}
.key path {
display: none;
}
.key line {
stroke: #000;
/** shape-rendering: crispEdges; */
}
/** State chart threshold END */
/** Bar chart BEGIN */
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
rect.bar:hover {
fill: orange !important;
}
/** Bar chart END */
.divider {
stroke: #FFF;
opacity: 0.7;
}
/** Grid lines BEGIN */
/** https://www.d3noob.org/2013/01/adding-grid-lines-to-d3js-graph.html */
.grid .tick {
stroke: lightgrey;
opacity: 0.7;
}
.grid path {
stroke-width: 0;
}
/** Grid lines END */
</style>
<script src="d3.min.js?v=3.2.8"
type="text/javascript" charset="utf-8"></script>
<script src="d3.geo.projection.v0.min.js"
type="text/javascript" charset="utf-8"></script>
<!--
<script src="topojson.v1.min.js"
type="text/javascript" charset="utf-8"></script>
-->
</head>
<body>
<script type="text/javascript" charset="utf-8">
var width = 800,
height = 520,
padding = 30;
// Local settings
var dataset = "data.csv",
countryValue = "Country",
colors = ["#2b83ba", "#d7191c"],
// Config only for multicolour chart
multiColor = true,
dataValue = "Asylum-seekers",
min = 0,
max = 7185,
useLogScale = false;
var showLegend = multiColor === true ? true : false,
spacing = showLegend === true ? 30 : 0,
margin = {
top: spacing,
bottom: 0,
left: spacing,
right: spacing
};
margin.hor = margin.left + margin.right;
margin.ver = margin.top + margin.bottom;
var color = d3.scale.linear()
.range(colors)
.clamp(useLogScale)
.interpolate(d3.interpolateHcl);
var projection = d3.geo.kavrayskiy7()
.translate([width/2, height/2])
.scale(150); // Revise so it isn't a magic number
var path = d3.geo.path()
.projection(projection);
var svg = d3.select("body")
.append("svg")
.attr({
"width": width,
"height": height
});
// Load the data values
d3.csv(dataset, function(error, data) {
var countryList = data.map(function(d) { return d[countryValue]; }),
errorList = {};
data.map(function(d) { errorList[d[countryValue]] = d[dataValue] ? d[dataValue] : ""; });
if (multiColor === true) {
if (useLogScale === true) {
min = min === 0 ? 0 : Math.log(min),
max = max === 0 ? 0 : Math.log(max);
}
color.domain([min, max]);
}
// Geodata loaded into the csv scope
d3.json("ne_110m_admin_0_countries_lakes.geojson", function(json) {
var dataLength = data.length,
jsonLength = json.features.length;
// Data (values) forloop
for (var i = 0; i < dataLength; i++) {
var countryName = data[i][countryValue],
countryData = useLogScale !== true ? data[i][dataValue] : Math.log(data[i][dataValue]);
// JSON (geodata) forloop
for (var j = 0; j < jsonLength; j++) {
if (countryName === json.features[j].properties.name_long) {
json.features[j].properties.value = dataValue === "" ? true : countryData;
delete errorList[countryName];
break;
}
}
}
svg.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr({
"d": path,
"class": "country",
"transform": "translate(" + 0 + "," + margin.top + ")"
})
.style("fill", function(d) {
if (multiColor === true) {
return d.properties.value ? color(d.properties.value) : "#DDD";
} else {
return d.properties.value ? color.range()[1] : "#DDD";
}
});
// Error log
errorList = Object.keys(errorList);
if (errorList.length !== 0) {
console.log("An error occured trying to plot the following countries:");
console.log(errorList);
}
});
// TODO: Make threshold a gradient for non-discrete values
// TODO: Orient at the bottom instead
// ISSUES: With book shape file, Alabama disappears
// ISSUES: With official shape file, Minnesota disappears
if (showLegend === true) {
var thresholdSuffix = "",
// Find a better title
thresholdTitle = "No. of asylum-seekers",
thresholdIsDiscrete = true;
var threshold = d3.scale.threshold()
.domain(color.domain()) // doesn't work
.range(color.range());
var keyX = d3.scale.linear()
.domain(d3.extent(threshold.domain()))
.range([0, (width - margin.hor)/2]); // total key width
var keyXAxis = d3.svg.axis()
.scale(keyX)
.orient("bottom")
.tickSize((thresholdIsDiscrete === true) ? 13 : 0)
.tickValues(threshold.domain())
.tickFormat(function(d) { return thresholdIsDiscrete ? d + thresholdSuffix : ""; });
var g = svg.append("g")
.attr({
"class": "key", // Rename
"transform": "translate(" + margin.left + "," + margin.top + ")"
});
// Threshold bars
g.selectAll("rect")
// Iterate over the threshold range to pass values later
.data(threshold.range().map(function(d, i) {
return {
// Calculate horizontal starting point, i.e. x coordinate
x0: i ? keyX(threshold.domain()[i - 1]) : keyX.range()[0],
// Calculate horizontal endpoint
x1: i < threshold.domain().length ? keyX(threshold.domain()[i]) : keyX.range()[1],
// Return colour
z: d
};
}))
.enter()
.append("rect")
.attr({
"height": 8,
"x": function(d) { return d.x0; },
"width": function(d) { return d.x1 - d.x0; }
})
.style("fill", function(d) { return d.z; });
// Threshold title
g.call(keyXAxis)
.append("text")
.attr({
"class": "caption", // Rename
"y": -6
})
.text(thresholdTitle);
}
});
</script>
</body>
</html>