Skip to content

Instantly share code, notes, and snippets.

@stilist
Last active August 29, 2015 14:24
Show Gist options
  • Save stilist/b4022bd55d7922c66ac6 to your computer and use it in GitHub Desktop.
Save stilist/b4022bd55d7922c66ac6 to your computer and use it in GitHub Desktop.
US air quality
<!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