Public
Edited
Feb 20, 2023
Insert cell
Insert cell
{
const width = 960;
const height = 500;
const mapData = await FileAttachment("110m.json").json();
const airQualityData = await fetch('https://api.waqi.info/map/bounds/').then(response => response.json());
console.log(airQualityData);

}

Insert cell
// Filter the air quality data to include only the latest measurements for each city
const latestMeasurements = {};
airQualityData.data.forEach(record => {
const city = record.station.name;
if (!latestMeasurements[city] || record.time.iso > latestMeasurements[city].time.iso) {
latestMeasurements[city] = record;
}
});

// Create a projection and a path generator for the map
const projection = d3.geoNaturalEarth1();
const pathGenerator = d3.geoPath().projection(projection);

// Append an SVG element to the page and set its size
const svg = d3.select('body')
.append('svg')
.attr('width', width)
.attr('height', height);

// Create a group element for the map features
const mapGroup = svg.append('g');

// Draw the map features
mapGroup.selectAll('path')
.data(d3.feature(mapData, mapData.objects.countries).features)
.enter()
.append('path')
.attr('d', pathGenerator)
.attr('stroke', '#999')
.attr('fill', '#eee');

// Create a group element for the air quality data
const airQualityGroup = svg.append('g');

// Draw the air quality data as circles on the map
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'; // Good
} else if (aqi <= 100) {
return '#ffff00'; // Moderate
} else if (aqi <= 150) {
return '#ff7e00'; // Unhealthy for Sensitive Groups
} else if (aqi <= 200) {
return '#ff0000'; // Unhealthy
} else if (aqi <= 300) {
return '#8f3f97'; // Very Unhealthy
} else {
return '#7e0023'; // Hazardous
}
}
Insert cell

Purpose-built for displays of data

Observable is your go-to platform for exploring data and creating expressive data visualizations. Use reactive JavaScript notebooks for prototyping and a collaborative canvas for visual data exploration and dashboard creation.
Learn more