The New York Times regularly publishes compelling information displays online and in print. Even their weekly weather forecast is rich with information, showing the range of record, normal, actual and forecast temperatures (along with error bars!) for each day. Here we recreate the display for a recent week in Seattle.
Next: Oakland Crimespotting
<html>
<head>
<title>Weather</title>
<link type="text/css" rel="stylesheet" href="ex.css?3.1"/>
<script type="text/javascript" src="../protovis-r3.1.0.js"></script>
<script type="text/javascript" src="weather.js"></script>
<style type="text/css">
#fig {
width: 200px;
height: 250px;
}
</style>
</head>
<body><div id="center"><div id="fig">
<script type="text/javascript+protovis">
var w = 18, h = 3;
var vis = new pv.Panel()
.width(200)
.height(250);
/* Record range. */
var record = vis.add(pv.Bar)
.data(weather)
.bottom(function(d) d.record.low * h)
.height(function(d) (d.record.high - d.record.low) * h)
.left(function() this.index * w)
.width(w - 2)
.fillStyle("#ccc");
/* Normal range. */
record.add(pv.Bar)
.bottom(function(d) d.normal.low * h)
.height(function(d) (d.normal.high - d.normal.low) * h)
.fillStyle("#999");
/* White grid lines. */
vis.add(pv.Rule)
.data([20, 40, 60])
.bottom(function(d) d * h + 1)
.left(0).right(20)
.lineWidth(2).strokeStyle("white")
.anchor("right").add(pv.Label)
.text(function(d) d + "\u00b0");
/* Actual and forecast range. */
record.add(pv.Bar)
.visible(function(d) d.actual)
.bottom(function(d) d.actual.low * h)
.height(function(d) (d.actual.high - d.actual.low) * h)
.left(function() this.index * w + 3)
.width(w - 8)
.fillStyle("black")
.add(pv.Bar)
.visible(function(d) d.forecast)
.bottom(function(d) d.forecast.high.low * h)
.height(function(d) (d.forecast.high.high - d.forecast.high.low) * h)
.add(pv.Bar)
.bottom(function(d) d.forecast.low.low * h)
.height(function(d) (d.forecast.low.high - d.forecast.low.low) * h)
.add(pv.Bar)
.bottom(function(d) d.forecast.low.low * h)
.height(function(d) (d.forecast.high.high - d.forecast.low.low) * h)
.left(function() this.index * w + 3 + Math.floor((w - 8) / 3))
.width(Math.ceil((w - 8) / 3));
/* Day labels. */
record.anchor("top").add(pv.Label)
.top(16)
.text(function(d) d.day);
/* Title. */
vis.add(pv.Label)
.top(0).left(0)
.textBaseline("top")
.font("bold 10pt Sans-Serif")
.text("Seattle ");
vis.render();
</script>
</div></div></body>
</html>
var weather = [
{ day: "M",
record: { high: 62, low: 15 },
normal: { high: 50, low: 38 },
actual: { high: 48, low: 36 } },
{ day: "T",
record: { high: 62, low: 23 },
normal: { high: 50, low: 38 },
actual: { high: 50, low: 40 } },
{ day: "W",
record: { high: 61, low: 20 },
normal: { high: 50, low: 38 },
actual: { high: 55, low: 36 } },
{ day: "T",
record: { high: 67, low: 21 },
normal: { high: 50, low: 38 },
actual: { high: 51, low: 33 } },
{ day: "F",
record: { high: 61, low: 23 },
normal: { high: 50, low: 38 },
actual: { high: 50, low: 30 } },
{ day: "S",
record: { high: 67, low: 20 },
normal: { high: 50, low: 38 },
forecast: { high: { high: 53, low: 49 }, low: { high: 40, low: 35 } } },
{ day: "S",
record: { high: 63, low: 23 },
normal: { high: 50, low: 39 },
forecast: { high: { high: 55, low: 49 }, low: { high: 42, low: 37 } } },
{ day: "M",
record: { high: 61, low: 26 },
normal: { high: 51, low: 39 },
forecast: { high: { high: 53, low: 49 }, low: { high: 43, low: 40 } } },
{ day: "T",
record: { high: 61, low: 24 },
normal: { high: 51, low: 39 },
forecast: { high: { high: 52, low: 46 }, low: { high: 44, low: 40 } } },
{ day: "W",
record: { high: 63, low: 20 },
normal: { high: 51, low: 39 },
forecast: { high: { high: 53, low: 46 }, low: { high: 43, low: 38 } } }
];