This dataset is from the Missing Migrants data uploaded to Kaggle, and contains information about people who have gone missing while travelling along migration routes. This data originates from the Missing Migrants Project.
The map data used in the right-hand legend is taken from Natural Earth, using the Land polygons dataset. The shapefile was processed into sets of geodetic polygons for use in the visualization.
forked from curran's block: Data Table Summary
forked from dbeach24's block: Missing Migrants
forked from dbeach24's block: Missing Migrants Vis
xxxxxxxxxx
<html>
<head>
<meta charset="utf-8">
<title>Data Summary</title>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript" src="mapdata.json"></script>
<style>
.tick text {
fill: #9b9b9b;
font-size: 16pt;
font-family: sans-serif;
}
.tick line {
stroke: #bfbfbf;
}
.topMarker {
opacity: 0.5;
fill: #a80000;
}
.botMarker {
opacity: 0.5;
fill: #c69f00;
}
.landform {
fill: #007000;
opacity: 0.2;
}
.areaLabel {
font-size: 10pt;
font-family: sans-serif;
fill: #7f7f7f;
}
.areaNumber {
font-size: 10pt;
font-family: sans-serif;
fill: #ffffff;
}
</style>
</head>
<body>
<svg width="960" height="500"></svg>
<script>
const margin = {
left: 30,
right: 50,
top: 30,
bottom: 30
};
const xValue = d => d.date;
const xTicks = 5;
const yValue = d => d.lat;
const topSizeValue = d => d.dead;
const topSizeLabel = 'Dead';
const botSizeValue = d => d.missing;
const botSizeLabel = 'Missing';
const svg = d3.select('svg');
const width = svg.attr('width');
const height = svg.attr('height');
const innerWidth = width - margin.left - margin.right;
const innerHeight = height - margin.top - margin.bottom;
const g = svg.append('g')
.attr('transform', `translate(${margin.left},${margin.top})`);
const xAxisG = g.append('g')
.attr('transform', `translate(0, ${innerHeight})`);
const yAxisG = g.append('g')
.attr('transform', `translate(${innerWidth - 39}, 0)`);
const sizeAxisG = g.append('g')
.attr('transform', `translate(${innerWidth - 150}, ${innerHeight - 20})`);
const topG = g.append('g');
const botG = g.append('g');
const xScale = d3.scaleTime();
const yScale1 = d3.scaleLinear();
const yScale2 = d3.scaleLinear();
const yScale3 = d3.scaleLinear();
const yScales = [yScale1, yScale2, yScale3];
const sizeScale = d3.scaleSqrt();
const xScale1 = d3.scaleLinear();
const xScale2 = d3.scaleLinear();
const xScale3 = d3.scaleLinear();
const xScales = [xScale1, xScale2, xScale3];
const xAxis = d3.axisTop()
.scale(xScale)
.ticks(xTicks)
.tickPadding(2)
.tickSize(innerHeight);
function row(d) {
d.missing = +d.missing;
d.dead = +d.dead;
// convert from dd-mm-yyyy
const dp = d.date.split('/');
d.date = new Date(`${dp[1]}/${dp[0]}/${dp[2]}`);
d.lat = +d.lat;
d.lon = +d.lon;
// compute region (Americas / EMEA / Asia)
var regionId, regionName;
if(d.lon < -50) {
regionId = 0;
regionName = 'Americas';
} else if(d.lon < 75) {
regionId = 1;
regionName = 'EMEA';
} else {
regionId = 2;
regionName = 'Asia';
}
d.regionId = regionId;
d.regionName = regionName;
return d;
}
function drawPolys(g, data, xScale, yScale, classAttr) {
for(var i = 1; i < data.length; i++) {
const poly = data[i];
const sx = xScale(poly[0][0]);
const sy = yScale(poly[0][1]);
var d = `M ${sx} ${sy} `;
for(var j = 1; j < poly.length; j++) {
const x = xScale(poly[j][0]);
const y = yScale(poly[j][1]);
d += `L ${x} ${y} `;
}
d += `L ${sx} ${sy}`;
g.append('path').attr('d', d).attr('class', classAttr);
}
}
function topSemiCircle(cx, cy, r) {
return `M ${cx} ${cy} m ${-r} 0 a ${r} ${r} 0 0 1 ${2*r} 0`;
}
function botSemiCircle(cx, cy, r) {
return `M ${cx} ${cy} m ${r} 0 a ${r} ${r} 0 0 1 ${-2*r} 0`;
}
const padH = 10;
const padInnerH = innerHeight - (padH * 2);
// Load and summarize the data.
d3.csv('MissingMigrantsProject.csv', row, data => {
xScale
.domain([new Date("2014-01-01"), new Date("2017-12-31")])
.range([0, innerWidth]);
yScale1
.domain([0, 40])
.range([padInnerH*0.4, 0]);
yScale2
.domain([0, 50])
.range([padInnerH*0.8 + padH, padInnerH*0.4 + padH]);
yScale3
.domain([0, 25])
.range([padInnerH + padH*2, padInnerH*0.8 + padH*2]);
const scalefactor = s => (s.domain[1] - s.domain[0]) / (s.range[1] - s.range[0]);
const ys1dpp = scalefactor(yScale1);
const ys2dpp = scalefactor(yScale2);
const ys3dpp = scalefactor(yScale3);
console.log(ys1dpp);
console.log(ys2dpp);
console.log(ys3dpp);
const xf = 1.3;
xScale1
.domain([-180, -50])
.range([-65*xf, 65*xf]);
xScale2
.domain([-25, 75])
.range([-62.5*xf, 62.5*xf]);
xScale3
.domain([75, 180])
.range([-52.5*xf, 52.5*xf]);
sizeScale
.domain(d3.extent(data, topSizeValue))
.range([0, 30]);
topG.selectAll('path').data(data)
.enter().append('path')
.attr('d', function (d) {
const cx = xScale(xValue(d));
const cy = yScales[d.regionId](yValue(d));
const r = sizeScale(topSizeValue(d));
return topSemiCircle(cx, cy, r);
})
.attr('class', 'topMarker');
botG.selectAll('path').data(data)
.enter().append('path')
.attr('d', function (d) {
const cx = xScale(xValue(d));
const cy = yScales[d.regionId](yValue(d));
const r = sizeScale(botSizeValue(d));
return botSemiCircle(cx, cy, r);
})
.attr('class', 'botMarker');
xAxisG.call(xAxis);
xAxisG.select(".domain").remove();
sizeAxisG.append('path')
.attr('d', topSemiCircle(0, 0, sizeScale(300)))
.attr('class', 'topMarker');
sizeAxisG.append('path')
.attr('d', botSemiCircle(0, 0, sizeScale(300)))
.attr('class', 'botMarker');
sizeAxisG.append('text')
.attr('x', -10)
.attr('y', -4)
.text('300')
.attr('class', 'areaNumber');
sizeAxisG.append('text')
.attr('x', -10)
.attr('y', 12)
.text('300')
.attr('class', 'areaNumber');
sizeAxisG.append('text')
.attr('x', 20)
.attr('y', -4)
.text('Dead')
.attr('class', 'areaLabel');
sizeAxisG.append('text')
.attr('x', 20)
.attr('y', 12)
.text('Missing')
.attr('class', 'areaLabel');
drawPolys(yAxisG, mapdata.map1, xScales[0], yScales[0], 'landform');
drawPolys(yAxisG, mapdata.map2, xScales[1], yScales[1], 'landform');
drawPolys(yAxisG, mapdata.map3, xScales[2], yScales[2], 'landform');
});
</script>
</body>
</html>
https://d3js.org/d3.v4.min.js