Skip to content

Instantly share code, notes, and snippets.

@jaronheard
Last active September 18, 2017 04:36
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save jaronheard/e52696a255c870088cb2360f9cfa70d7 to your computer and use it in GitHub Desktop.
Save jaronheard/e52696a255c870088cb2360f9cfa70d7 to your computer and use it in GitHub Desktop.
City Weather Comparison
license: gpl-3.0
height: 700
scrolling: no
border: yes
module.exports = {
"extends": "airbnb-base"
};

Visualizing a year of the sky

What does the sky in your city look like over the course of a year? Was there a month where all you could see was clouds (Portland)? Or is your year full of so much sun it's almost too much (Los Angeles)?

Light areas indicate sunshine and clear sky, darker areas indicate increasing cloud cover.

This visualization shows cloud cover over the course of 2011 for three sample cities (more to come!). The cloud cover data is based on a satellite reanalysis from a database archived at NCDC.

More about the data

Integrated Surface Data (ISD) is digital data set DSI-3505, archived at the National Climatic Data Center (NCDC). The ISD database is composed of worldwide surface weather observations from over 20,000 stations, collected and stored from sources such as the Automated Weather Network (AWN), the Global Telecommunications System (GTS), the Automated Surface Observing System (ASOS), and data keyed from paper forms. More information

Inspiration

Inspired by the annual sunshine record I found through Edward Tufte's classic book The Visual Display of Quantative Information, in which Tufte cites F.J. Monkhouse and H.R. Wilkinson's book Maps and Diagrams as the original source.

<head>
<link rel="stylesheet" type="text/css" href="style.css">
<script type="text/javascript" src="https://d3js.org/d3.v4.min.js"></script>
<script type="text/javascript" src="https://d3js.org/d3-scale-chromatic.v1.min.js"></script>
</head>
<script type="text/javascript" src="script.js"></script>
<body>
<select id="cities" name="cities">
<option value="portland">Portland</option>
<option value="la">Los Angeles</option>
<option value="ny">New York</option>
</select>
<br>
<input id="clicker" type="submit">
<h1 id="cityTitle"></h1>
<br>
<svg width="960" height="500"></svg>
</body>
/* eslint-env node, browser */
/* global d3 */
window.onload = function onload() {
const cityURLs = {};
cityURLs.portland = 'https://gist.githubusercontent.com/jaronheard/99be6944675abdeb7b02e15f3430114c/raw/2d169f545a4d95762a500cd8b17418273eafa051/portland2011cloudcover.csv';
cityURLs.portlandSunriseSunset =
'https://gist.githubusercontent.com/jaronheard/0f7ed3b23e56d01fba20c2d4934ce645/raw/2783170b18313f6d9d2cd0fe750bfdbdfd1c56ea/portland2011sunrisesunset.csv';
cityURLs.portlandWeather = 'https://gist.githubusercontent.com/jaronheard/700042270c68fece9043a4d406a74bfb/raw/bf52c81394c6ba32dda085ecf383775ce84d666d/portland2011weather.txt';
cityURLs.la = 'https://gist.githubusercontent.com/jaronheard/11178a52980eaa8d3f8066617cb34921/raw/0ad3046c3f480a709a02c0caccc7295a9c9f4dd8/la2011cloudcover.csv';
cityURLs.laSunriseSunset =
'https://gist.githubusercontent.com/jaronheard/b44a19296d197b2c9f78a8fc28a14629/raw/29ec51fbbe0c17962e258365c5585f86a770c4cb/la2011sunrisesunset.csv';
cityURLs.nySunriseSunset = 'https://gist.githubusercontent.com/jaronheard/c9a7dc7666b88310802c1817f58d8c12/raw/9674f8af27eb1c9755b35631a2ad0af01e0f13ea/ny2011sunrisesunset.csv';
cityURLs.ny = 'https://gist.githubusercontent.com/jaronheard/9f3b768b63a94b29ebfccce5b13c74fe/raw/63041e04b341c5c826ef6d4e924bcfb3fd89dc06/ny2011cloudcover.csv';
const cityTitles = {};
cityTitles.portland = '🌤: PORTLAND';
cityTitles.la = '🌤: LOS ANGELES';
cityTitles.ny = '🌤: NEW YORK';
const now = new Date(1970, 0, 1);
const parseTime = d3.timeParse('%m-%d-%Y-%H:%M');
const parseUTCTime = d3.utcParse('%m-%d-%Y-%H:%M');
const parseSunDate = d3.timeParse('%m-%d-%Y');
const formatHour = d3.utcFormat('%-I:%M %p');
const formatMonth = d3.utcFormat('%B');
const shiftToUTCHours = function shiftToUTCHours(t, adjustment) {
const d = new Date(1970, 0, 1);
const h = (t.getUTCHours() + adjustment) - (d.getTimezoneOffset() / 60);
const m = t.getUTCMinutes();
return new Date(1970, 0, 1, h, m);
};
const svg = d3.select('svg');
const margin = {
top: 20,
right: 0,
bottom: 20,
left: 70,
};
const width = svg.attr('width') - margin.left - margin.right;
const height = svg.attr('height') - margin.top - margin.bottom;
const g = svg.append('g').attr('transform', `translate(${margin.left},${margin.top})`);
const x = d3.scaleUtc()
.rangeRound([0, width + 10]);
const y = d3.scaleUtc()
.domain([d3.utcDay.floor(now), d3.utcDay.ceil(now)])
.rangeRound([0, height - 32]);
function initializeWeatherData() {
const ssv = d3.dsvFormat(' ');
d3.request(cityURLs.portlandWeather)
.mimeType('text/plain')
.response((xhr) => {
const dirtyTxt = xhr.responseText;
const cleanTxt = dirtyTxt.split('\n').map(z => z.slice(13)).join('\n');
const cleanerTxt = cleanTxt.replace(/ +/g, ' ');
return ssv.parse(cleanerTxt);
})
.get((data) => {
console.log('space-delimited test data:');
console.log(data[395]);
});
}
function processSunData(d) {
const sunData = d;
sunData.day = parseSunDate(d.Date);
sunData.rise = parseUTCTime(`${d.Date}-${d.Sunrise}`);
sunData.set = parseUTCTime(`${d.Date}-${d.Sunset}`);
return sunData;
}
function processCloudData(d) {
const cloudChunks = 4;
const cloudData = d;
const c = parseInt(d.CloudCover, 10);
cloudData.cloud = Math.round(c / (200 / cloudChunks)) * (200 / cloudChunks);
cloudData.time = parseUTCTime(d.Date);
cloudData.timeLocal = parseTime(d.Date);
return cloudData;
}
function updateData() {
const city = document.getElementById('cities').value;
const title = document.getElementById('cityTitle');
title.innerHTML = cityTitles[city];
d3.csv(cityURLs[city], processCloudData, (error, data) => {
if (error) throw error;
const date0 = data[0].time;
const date1 = data[data.length - 1].time;
x.domain([date0, date1]).nice();
const cover = g.selectAll('.cover')
.data(data);
cover.attr('fill', d => d3.interpolateGreys(d.cloud / 100));
cover.enter().append('rect')
.attr('width', 5)
.attr('height', 20)
.attr('x', d => x(d.timeLocal))
.attr('y', (d) => {
const yToFit = y(shiftToUTCHours(d.time, 0));
return yToFit;
})
.attr('class', 'cover')
.attr('fill', d => d3.interpolateGreys(Math.round(d.cloud / 1) / 100));
cover.exit().remove();
d3.csv(cityURLs[`${city}SunriseSunset`], processSunData, (error, data) => {
if (error) throw error;
console.log(data);
const setArea = d3.area()
.x(d => x(d.day))
.y1(d => y(shiftToUTCHours(d.set, 0)))
.y0(d => y(d3.utcDay.ceil(shiftToUTCHours(d.set, 0))) + 20);
const riseArea = d3.area()
.x(d => x(d.day))
.y1(d => y(shiftToUTCHours(d.rise, 0)))
.y0(d => y(d3.utcDay.floor(shiftToUTCHours(d.rise, 0))));
const riseLine = d3.line()
.x(d => x(d.day))
.y(d => y(shiftToUTCHours(d.rise, 0)));
const setLine = d3.line()
.x(d => x(d.day))
.y(d => y(shiftToUTCHours(d.set, 0)));
g.selectAll('path').remove();
g.append('path')
.datum(data)
.attr('d', setArea)
.attr('class', 'sunriseareapath');
g.append('path')
.datum(data)
.attr('d', riseArea)
.attr('class', 'sunriseareapath');
g.append('path')
.datum(data)
.attr('d', riseLine)
.attr('class', 'sunriseline');
g.append('path')
.datum(data)
.attr('d', setLine)
.attr('class', 'sunriseline');
g.selectAll('g').remove();
g.append('g')
.attr('class', 'axis axis--x')
.attr('transform', `translate(0,${height})`)
.call(d3.axisBottom(x)
.tickFormat(formatMonth)
.tickSize(-height)
.tickPadding(-10))
.selectAll('text')
.attr('text-anchor', 'start')
.attr('x', 10)
.attr('dy', null);
g.append('g')
.attr('class', 'axis axis--y')
.call(d3.axisLeft(y)
.tickFormat(formatHour)
.tickSize(-width)
.tickPadding(10));
});
});
}
document.getElementById('clicker').addEventListener('click', updateData);
initializeWeatherData();
};
body {
background-color: black;
color: white;
}
input {
width: 20%;
border: 2px solid black;
border-radius: 0px;
background-color: black;
color: white;
}
select {
width: 20%;
border: 2px solid black;
border-radius: 0px;
background-color: black;
color: white;
}
h1 {
font-family: sans-serif;
}
.data {
fill: steelblue;
}
.timerect {
fill: steelblue;
}
.axis--y .tick line {
stroke: #fff;
stroke-opacity: 0.0;
}
.axis--x .tick line {
stroke: #fff;
stroke-opacity: 0.0;
}
.axis .domain {
display: none;
}
.sunriseareapath {
fill: black;
fill-opacity: 1.0;
}
.sunriseline {
fill: none;
stroke: black;
stroke-width: 2px;
}
.axis text {
font-family: sans-serif;
font-size: 11px;
fill: white;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment