Last active
August 29, 2015 14:24
-
-
Save stilist/b4022bd55d7922c66ac6 to your computer and use it in GitHub Desktop.
US air quality
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!doctype html> | |
<meta charset='utf-8'> | |
<style> | |
.data-datapoint {fill:transparent; stroke-width:1px;} | |
.aqi1 {stroke:#00e400;} | |
.aqi2 {stroke:#ffff00;} | |
.aqi3 {stroke:#ff7e00;} | |
.aqi4 {stroke:#ff0000;} | |
.aqi5 {stroke:#99004c;} | |
.aqi6 {stroke:#7e0023;} | |
svg {background-color:#666e78;} | |
.land {fill:#1d2b1d;} | |
</style> | |
<body> | |
<script src='http://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js'></script> | |
<script src='http://cdnjs.cloudflare.com/ajax/libs/underscore.js/1.8.3/underscore-min.js'></script> | |
<script src='https://cdnjs.cloudflare.com/ajax/libs/topojson/1.6.19/topojson.min.js'></script> | |
<script> | |
// Available data types: ozone (O3), fine particles (PM25), coarse dust (PM10), | |
// carbon monoxide (CO), nitrogen dioxide (NO2), sulfur dioxide (SO2) | |
var data_types = ['O3', 'PM25', 'PM10', 'CO', 'NO2', 'SO2'] | |
var height = 600 | |
var map_url = '/mbostock/raw/4090846/us.json' | |
var symbol_types = ['circle', 'cross', 'diamond', 'square', 'triangle-down', | |
'triangle-up'] | |
var width = 960 | |
// **************************************************************************** | |
function getDataUrl() { | |
var now = new Date().toISOString().split(':')[0] | |
var base = 'http://www.airnowapi.org/aq/data/' | |
// per http://qr.ae/7Zohpm | |
var bounding_box = [-124.848974, 24.396308, -66.885444, 49.384358].join(',') | |
var params = { | |
API_KEY: '00F1F0C9-7A2E-440B-8A45-085BDDC5C0BB', | |
BBOX: bounding_box, | |
dataType: 'B', | |
endDate: now, | |
format: 'application/json', | |
parameters: data_types.join(','), | |
startDate: now | |
} | |
var out_params = _.map(params, function (v, k) { return k + '=' + v }). | |
join('&') | |
var url = base + '?' + out_params | |
// The AirNow servers don’t set CORS headers, so the XHR fails. Work around | |
// it by proxying through YQL: http://stackoverflow.com/a/8579158/672403 | |
// manual URL escaping (spaces have to be `+`, not `%20`, or YQL errors) | |
var yql_query = 'select * from json where url="'.replace(/\s/g, '+') | |
yql_query = yql_query.replace('=', '%3D') | |
yql_query = yql_query + encodeURIComponent(url) + '"' | |
var proxied = 'http://query.yahooapis.com/v1/public/yql?q=' + yql_query + | |
'&format=json' | |
return proxied | |
} | |
// **************************************************************************** | |
var svg = d3.select('body').append('svg'). | |
attr('height', height). | |
attr('width', width) | |
var project = d3.geo.albersUsa(). | |
scale(1000). | |
translate([width / 2, height / 2]) | |
var path = d3.geo.path(). | |
projection(project) | |
var quantize = d3.scale.quantize(). | |
domain([0, 100]). | |
range(d3.range(8)) | |
function renderDataseries(data, datatype, symbol_name) { | |
var symbol = d3.svg.symbol() | |
var symbol_size = 12 * 2 | |
var g = svg.append('g'). | |
attr('class', 'data data--' + datatype) | |
g.selectAll('path'). | |
data(data). | |
enter(). | |
append('path'). | |
attr('transform', function (d) { | |
var coords = project([d.lng, d.lat]) | |
return 'translate(' + coords[0] + ',' + coords[1] + ')' | |
}). | |
attr('d', symbol.type(symbol_name).size(symbol_size)). | |
attr('class', function (d) { return 'data-datapoint aqi' + quantize(d.aqi) }) | |
} | |
function renderData(data) { | |
var symbols = _.object(_.keys(data), symbol_types) | |
_.each(data, function (series, datatype) { | |
renderDataseries(series, datatype, symbols[datatype]) | |
}) | |
} | |
function renderMap() { | |
var map = svg.append('path') | |
d3.json(map_url, function (error, data) { | |
if (error) throw error; | |
map.datum(topojson.feature(data, data.objects.land)). | |
attr('class', 'land'). | |
attr('d', path) | |
}) | |
} | |
function render(data) { | |
renderMap() | |
renderData(data) | |
d3.select(self.frameElement).style('height', height + 'px') | |
} | |
// **************************************************************************** | |
var data_url = getDataUrl() | |
d3.text(data_url, 'text/plain', function (response) { | |
if (!response) throw new Error('invalid data') | |
var parsed = {} | |
_.each(data_types, function (d) { | |
var key = d | |
switch (d) { | |
case 'PM25': key = 'PM2.5' ; break | |
case 'O3': key = 'OZONE' ; break | |
} | |
parsed[key] = [] | |
}) | |
var json = JSON.parse(response) | |
if (!json.query.results) throw new Error('invalid data') | |
var data = json.query.results.json.json | |
_.each(data, function (item) { | |
parsed[item.Parameter].push({ | |
aqi: parseInt(item.AQI, 10), | |
concentration: parseFloat(item.Value), | |
lat: parseFloat(item.Latitude), | |
lng: parseFloat(item.Longitude), | |
unit: item.Unit | |
}) | |
}) | |
render(parsed) | |
}) | |
</script> | |
</body> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment