const latestMeasurements = {};
airQualityData.data.forEach(record => {
const city = record.station.name;
if (!latestMeasurements[city] || record.time.iso > latestMeasurements[city].time.iso) {
latestMeasurements[city] = record;
}
});
const projection = d3.geoNaturalEarth1();
const pathGenerator = d3.geoPath().projection(projection);
const svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height);
const mapGroup = svg.append('g');
mapGroup.selectAll('path')
.data(d3.feature(mapData, mapData.objects.countries).features)
.enter()
.append('path')
.attr('d', pathGenerator)
.attr('stroke', '#999')
.attr('fill', '#eee');
const airQualityGroup = svg.append('g');
airQualityGroup.selectAll('circle')
.data(Object.values(latestMeasurements))
.enter()
.append('circle')
.attr('cx', d => projection([d.station.geo[1], d.station.geo[0]])[0])
.attr('cy', d => projection([d.station.geo[1], d.station.geo[0]])[1])
.attr('r', d => Math.sqrt(d.aqi) * 4)
.attr('fill', d => getAirQualityColor(d.aqi))
.attr('opacity', 0.8)
.attr('stroke', '#333')
.attr('stroke-width', 0.5)
.append('title')
.text(d => `${d.station.name}: ${d.aqi}`);
function getAirQualityColor(aqi) {
if (aqi <= 50) {
return '#00e400';
} else if (aqi <= 100) {
return '#ffff00';
} else if (aqi <= 150) {
return '#ff7e00';
} else if (aqi <= 200) {
return '#ff0000';
} else if (aqi <= 300) {
return '#8f3f97';
} else {
return '#7e0023';
}
}