|
<!DOCTYPE html> |
|
<head> |
|
<meta charset="utf-8"> |
|
<script src="https://d3js.org/d3.v4.min.js"></script> |
|
<script src="https://d3js.org/topojson.v1.min.js"></script> |
|
<script src="https://unpkg.com/d3-tip@0.7.1/index.js"></script> |
|
<style> |
|
/* From http://bl.ocks.org/Caged/6476579 */ |
|
.d3-tip { |
|
line-height: 1; |
|
font-weight: bold; |
|
padding: 12px; |
|
background: rgba(0, 0, 0, 0.8); |
|
color: #fff; |
|
border-radius: 2px; |
|
} |
|
circle:hover { |
|
stroke: red; |
|
} |
|
</style> |
|
</head> |
|
<body> |
|
<svg width="960" height="1200"></svg> |
|
<script> |
|
const svg = d3.select('svg'); |
|
const pathsLayer = svg.append('g'); |
|
const worldPath = pathsLayer.append('path') |
|
.attr('fill', 'white'); |
|
const bangladeshPath = pathsLayer.append('path') |
|
.attr('fill', '#efefef') |
|
.attr('stroke', '#cccccc'); |
|
const locationsLayer = svg.append('g'); |
|
const sitesLayer = svg.append('g'); |
|
const firesLayer = svg.append('g'); |
|
|
|
const colorScale = d3.scaleOrdinal().range(d3.schemeCategory20); |
|
|
|
const campsColorScale = d3.scaleOrdinal() |
|
.range(['#addefe', '#0072BC']); |
|
|
|
const projection = d3.geoOrthographic() |
|
.rotate([267.6180000000006, -21.166000000000054, 0]) |
|
.scale(70000); |
|
const geoPath = d3.geoPath().projection(projection); |
|
const dragSensitivity = 0.002; |
|
|
|
// This function drives the radius of the circles. |
|
const rValue = d => d.Pop_Total; |
|
|
|
// Color by date. |
|
const colorValue = d => d.acq_date; |
|
|
|
const campsColorValue = d => d.UNHCR_Managed; |
|
|
|
const commaFormat = d3.format(','); |
|
const tip = d3.tip() |
|
.attr('class', 'd3-tip') |
|
.offset([-10, 0]) |
|
.html(d => { |
|
if(d.Location_Name){ |
|
return [ |
|
d.Location_Name, |
|
': Refugee population ', |
|
commaFormat(rValue(d)) |
|
].join(''); |
|
} else { |
|
return 'Fire detected on ' + d.acq_date; |
|
} |
|
}); |
|
svg.call(tip); |
|
|
|
d3.queue(3) |
|
.defer(d3.json, 'world-110m.json') |
|
.defer(d3.json, 'BGD.json') |
|
.defer(d3.csv, '20171008-rohingya-sites-location-in-coxs-bazar-with-unhcr.csv') |
|
//.defer(d3.csv, 'myanmar-fires.csv') |
|
.defer(d3.csv, 'fire_nrt_V1_18619.csv') |
|
.defer(d3.csv, 'fire_nrt_M6_18618.csv') |
|
.await(ready); |
|
|
|
function ready(err, world, bangladesh, sites, fires1, fires2){ |
|
const fires = fires1.concat(fires2); |
|
|
|
const worldFeature = topojson.feature(world, world.objects.land); |
|
const bangladeshFeature = topojson.feature(bangladesh, bangladesh.objects.BGD_Admin2); |
|
const bangladeshLocations = topojson.feature(bangladesh, bangladesh.objects.BGD_Locations); |
|
|
|
// Parse values in CSV data. |
|
sites.forEach(d => { |
|
d.Pop_Total = +d.Pop_Total; |
|
d.Pop_Prior_Aug_2017 = +d.Pop_Prior_Aug_2017; |
|
d.Inflow = +d.Inflow; |
|
d.Latitude = +d.Latitude; |
|
d.Longitude = +d.Longitude; |
|
d.location = [d.Longitude, d.Latitude]; |
|
}); |
|
|
|
// Make sure UNHCR sites stand out on top in Z order. |
|
sites.sort((a, b) => d3.ascending(campsColorValue(a), campsColorValue(b))); |
|
|
|
// Parse values for fires data. |
|
fires.forEach(d => { |
|
d.latitude = +d.latitude; |
|
d.longitude = +d.longitude; |
|
d.location = [d.longitude, d.latitude]; |
|
}); |
|
|
|
const rScale = d3.scaleSqrt() |
|
.domain([0, d3.max(sites, rValue)]) |
|
.range([0, 30]); |
|
|
|
function render(){ |
|
worldPath.attr('d', geoPath(worldFeature)); |
|
bangladeshPath.attr('d', geoPath(bangladeshFeature)); |
|
|
|
const siteCircles = sitesLayer.selectAll('circle').data(sites); |
|
const siteCirclesEnter = siteCircles |
|
.enter().append('circle') |
|
.attr('fill', d => campsColorScale(campsColorValue(d))) |
|
.attr('fill-opacity', 0.4) |
|
.attr('stroke', d => campsColorScale(campsColorValue(d))); |
|
siteCirclesEnter |
|
.merge(siteCircles) |
|
.attr('cx', d => projection(d.location)[0]) |
|
.attr('cy', d => projection(d.location)[1]) |
|
.attr('r', d => rScale(rValue(d))) |
|
.on('mouseover', tip.show) |
|
.on('mouseout', tip.hide); |
|
|
|
const fireCircles = firesLayer.selectAll('circle').data(fires); |
|
const fireCirclesEnter = fireCircles |
|
.enter().append('circle') |
|
.attr('fill', 'red') |
|
.attr('fill-opacity', 0.4); |
|
fireCirclesEnter |
|
.merge(fireCircles) |
|
.attr('cx', d => projection(d.location)[0]) |
|
.attr('cy', d => projection(d.location)[1]) |
|
.attr('r', 5) |
|
.attr('fill', d => colorScale(colorValue(d))) |
|
.on('mouseover', tip.show) |
|
.on('mouseout', tip.hide); |
|
|
|
// This code works but the locations available are useless as context. |
|
//const locationsData = bangladeshLocations.features.map(d => ({ |
|
// location: d.geometry.coordinates, |
|
// name: d.properties.LocName |
|
//})); |
|
//const locationText = locationsLayer.selectAll('text').data(locationsData); |
|
//const locationTextEnter = locationText |
|
// .enter().append('text') |
|
// .attr('fill', 'black'); |
|
//locationTextEnter |
|
// .merge(locationText) |
|
// .attr('x', d => projection(d.location)[0]) |
|
// .attr('y', d => projection(d.location)[1]) |
|
// .text(d => d.name); |
|
} |
|
|
|
render(); |
|
svg.call(d3.drag().on('drag', () => { |
|
const { dx, dy } = d3.event; |
|
const [x, y] = projection.rotate(); |
|
projection.rotate([x + dx * dragSensitivity, y - dy * dragSensitivity]); |
|
console.log(projection.rotate()); |
|
render(); |
|
})); |
|
} |
|
|
|
</script> |
|
</body> |