Beijing Air Quality Data recorded at the US Embassy in Beijing over the past 8 years and fed into Mike's Calendar block.
Data is from US State Department and is not fully validated or verified.
Beijing Air Quality Data recorded at the US Embassy in Beijing over the past 8 years and fed into Mike's Calendar block.
Data is from US State Department and is not fully validated or verified.
<!DOCTYPE html> | |
<meta charset="utf-8"> | |
<style> | |
body { | |
font: 18px sans-serif; | |
shape-rendering: crispEdges; | |
} | |
.day { | |
fill: #f0f0f0; | |
stroke: #ccc; | |
} | |
.month { | |
fill: none; | |
stroke: #000; | |
stroke-width: 2px; | |
} | |
.tooltip { | |
background-color: #fff; | |
padding: 0px 10px; | |
font-family: sans-serif; | |
width: 800px; | |
} | |
.tooltip_body { | |
margin: 5px 0; | |
font-size: 18px; | |
} | |
h5 { | |
font-size: 18px; | |
margin-left: 10px; | |
} | |
</style> | |
<body> | |
<h5>Concentration of Particluate Matter less than 2.5 micrometers in diameter</h5> | |
<script src="//d3js.org/d3.v3.min.js"></script> | |
<script> | |
var width = 960, | |
height = 136, | |
cellSize = 17, | |
margin = 14; | |
var format = d3.time.format("%x"); | |
var color = d3.scale.threshold() | |
.domain([51, 101, 151, 201, 301]) | |
.range(["#1a9850", "#fee08b", "#f46d43", "#d73027", "#a50026", "#67001f"]); | |
var tooltip = d3.select("body") | |
.append("div") | |
.attr("class", "tooltip") | |
.style("position", "absolute") | |
.style("z-index", "10") | |
.style("visibility", "hidden"); | |
var svg = d3.select("body").selectAll("svg") | |
.data(d3.range(2008, 2016)) | |
.enter().append("svg") | |
.attr("width", width) | |
.attr("height", height + margin) | |
.append("g") | |
.attr("transform", "translate(" + ((width - cellSize * 53) / 2) + "," + (height - cellSize * 7 - 1) + ")"); | |
svg.append("text") | |
.attr("transform", "translate(-6," + cellSize * 3.5 + ")rotate(-90)") | |
.style("text-anchor", "middle") | |
.text(function(d) { return d; }); | |
var rect = svg.selectAll(".day") | |
.data(function(d) { return d3.time.days(new Date(d, 0, 1), new Date(d + 1, 0, 1)); }) | |
.enter().append("rect") | |
.attr("class", "day") | |
.attr("width", cellSize) | |
.attr("height", cellSize) | |
.attr("x", function(d) { return d3.time.weekOfYear(d) * cellSize; }) | |
.attr("y", function(d) { return d.getDay() * cellSize; }) | |
.datum(format); | |
svg.selectAll(".month") | |
.data(function(d) { return d3.time.months(new Date(d, 0, 1), new Date(d + 1, 0, 1)); }) | |
.enter().append("path") | |
.attr("class", "month") | |
.attr("d", monthPath); | |
var data = {}, remaining = 8; // parallel loading csv code from here: https://groups.google.com/forum/#!msg/d3-js/3Y9VHkOOdCM/YnmOPopWUxQJ | |
d3.csv("/d/4c8f05129838a63ec90930f8c46262f0/Beijing_2008_HourlyPM2.5_created20140325.csv", function(data_2008) { | |
data.yr_2008 = data_2008; | |
if (!--remaining) draw(); | |
}) | |
d3.csv("/d/4c8f05129838a63ec90930f8c46262f0/Beijing_2009_HourlyPM25_created20140709.csv", function(data_2009) { | |
data.yr_2009 = data_2009; | |
if (!--remaining) draw(); | |
}) | |
d3.csv("/d/4c8f05129838a63ec90930f8c46262f0/Beijing_2010_HourlyPM25_created20140709.csv", function(data_2010) { | |
data.yr_2010 = data_2010; | |
if (!--remaining) draw(); | |
}) | |
d3.csv("/d/4c8f05129838a63ec90930f8c46262f0/Beijing_2011_HourlyPM25_created20140709.csv", function(data_2011) { | |
data.yr_2011 = data_2011; | |
if (!--remaining) draw(); | |
}) | |
d3.csv("/d/4c8f05129838a63ec90930f8c46262f0/Beijing_2012_HourlyPM2.5_created20140325.csv", function(data_2012) { | |
data.yr_2012 = data_2012; | |
if (!--remaining) draw(); | |
}) | |
d3.csv("/d/4c8f05129838a63ec90930f8c46262f0/Beijing_2013_HourlyPM2.5_created20140325.csv", function(data_2013) { | |
data.yr_2013 = data_2013; | |
if (!--remaining) draw(); | |
}) | |
d3.csv("/d/4c8f05129838a63ec90930f8c46262f0/Beijing_2014_HourlyPM25_created20150203.csv", function(data_2014) { | |
data.yr_2014 = data_2014; | |
if (!--remaining) draw(); | |
}) | |
d3.csv("/d/4c8f05129838a63ec90930f8c46262f0/Beijing_2015_HourlyPM25_created20160201.csv", function(data_2015) { | |
data.yr_2015 = data_2015; | |
if (!--remaining) draw(); | |
}) | |
function draw() { | |
data = d3.entries(data); | |
data.forEach(function(dataset) { | |
dataset.value = dataset.value.filter(function(d) { // only want valid readings | |
return d["QC Name"] == "Valid" && d["Value"] != "-999"; | |
}); | |
}); | |
var flatData = d3.merge(data.map(function(d) { return d.value; })); | |
var dailyAvg = d3.nest() | |
.key(function(d) { | |
return format(new Date(d.Year, d.Month - 1, d.Day)); | |
}) | |
.rollup(function(hourlyReadings) { | |
if (hourlyReadings.length != 24) { | |
return "N/A"; | |
} | |
return d3.sum(hourlyReadings, function(d) { return +d.Value; }) / 24 | |
}) | |
.map(flatData) | |
var key = { "#1a9850": "Good", "#fee08b": "Moderate", "#f46d43": "Unhealthy for sensitive groups", | |
"#d73027": "Unhealthy", "#a50026": "Very Unhealthy", "#67001f": "Hazardous" }; | |
var calBounds = { "cal2008": { top: 150 }, "cal2009": { top: 255 }, "cal2010" : { top: 409 }, | |
"cal2011": { top: 563 }, "cal2012": { top: 718 }, "cal2013" : { top: 870 }, | |
"cal2014" : { top: 1024 }, "cal2015": { top: 1179 } }; | |
var calTooltipTop = { "cal2008": 60, "cal2009": 217, "cal2010": 372, "cal2011": 525, | |
"cal2012": 679, "cal2013": 833, "cal2014": 987, "cal2015": 1142 }; | |
d3.keys(calBounds).forEach(function(cal) { | |
calBounds[cal].bottom = calBounds[cal].top + 114; | |
}); | |
rect | |
.style("fill", function(d) { // d here is the date sring for each day | |
if (dailyAvg[d] && dailyAvg[d] !== "N/A") { | |
return color(dailyAvg[d]); | |
} | |
}) | |
.on("mouseover", function(d) { | |
tooltip.html(""); | |
tooltip.append("pre").attr("class", "tooltip_body"); | |
tooltip.select(".tooltip_body") | |
.text( | |
"Date: " + d + "\t Reading: " + (dailyAvg[d] && dailyAvg[d] !== "N/A" ? d3.round(dailyAvg[d], 2) + " µg/cu PM2.5 \t" + "Air Quality: " + key[color(dailyAvg[d])] : "No Data") | |
); | |
return tooltip.style("visibility", "visible"); | |
}) | |
.on("mousemove", function() { | |
var y = d3.event.pageY; | |
var currentCal = ""; | |
d3.keys(calBounds).forEach(function(cal) { | |
if (y >= calBounds[cal].top && y <= calBounds[cal].bottom) { | |
currentCal = cal; | |
} | |
}); | |
return tooltip.style("top", calTooltipTop[currentCal] + "px").style("left", "45px"); | |
}) | |
} | |
function monthPath(t0) { | |
var t1 = new Date(t0.getFullYear(), t0.getMonth() + 1, 0), | |
d0 = t0.getDay(), w0 = d3.time.weekOfYear(t0), | |
d1 = t1.getDay(), w1 = d3.time.weekOfYear(t1); | |
return "M" + (w0 + 1) * cellSize + "," + d0 * cellSize | |
+ "H" + w0 * cellSize + "V" + 7 * cellSize | |
+ "H" + w1 * cellSize + "V" + (d1 + 1) * cellSize | |
+ "H" + (w1 + 1) * cellSize + "V" + 0 | |
+ "H" + (w0 + 1) * cellSize + "Z"; | |
} | |
</script> |