Skip to content

Instantly share code, notes, and snippets.

@curran
Last active September 26, 2015 22:50
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
Star You must be signed in to star a gist
Save curran/5a9767b5c23982c89632 to your computer and use it in GitHub Desktop.
Live Temperature by City

This program makes a bar chart and line chart from data in the Data Canvas - Sense Your City API and makes use of the Chiasm visualization runtime engine.

The line chart shows the temperature for all cities with available data for the past 24 hours, while the bar chart shows the current temperature of each city. The data is up to date, and updates every 5 minutes. Colors code the lines to the city.

Hovering over the line chart causes the bar chart to display data for the selected slice of time only (using Crossfilter).

The gear icon lets you open the configuration editor. Click on numbers and colors in the configuration editor for interactive widgets that let you configure the visualizations. For example, you can edit the color scale used by both visualizations, or edit the title text.

Draws from

web counter
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<!--
A data visualization editor.
Curran Kelleher March 2015
-->
<title>Visualization Editor</title>
<link rel="stylesheet" href="//curran.github.io/cdn/inlet/inlet.css">
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/codemirror/5.0.0/codemirror.css">
<link rel="stylesheet" href="styles.css">
<style>
</style>
</head>
<body>
<!-- The container for the runtime environment. -->
<div id="container"></div>
<!-- The gear icon in the upper right. -->
<!-- Image from http://simpleicon.com/gear-12.html -->
<img id="gear" src="gear.png">
<!-- Use RequireJS for module loading. -->
<script src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.16/require.min.js"></script>
<!-- Configure AMD modules. -->
<script src="requireJSConfig.js"></script>
<!-- Run the main program. -->
<script src="main.js"></script>
</body>
</html>
// This program loads the configuration file called "visConfig.json".
require(["d3", "model", "chiasm/runtime", "./senseYourCityData"],
function (d3, Model, Runtime, senseYourCityData) {
// Instantiate the Chiasm runtime within the container.
var runtime = Runtime(document.getElementById("container"));
// Attach the data API plugin to the runtime.
runtime.plugins.senseYourCityData = senseYourCityData;
// Load the visualization configuration.
d3.json("visConfig.json", function (err, config) {
runtime.config = config;
});
// Toggle visibility of the configuration editor.
d3.select("#gear").on("click", function(){
runtime.getComponent("editor", function(editor){
if(editor.size != "0px"){
editor.size = "0px";
} else {
editor.size = "325px";
}
});
});
});
// This is the RequireJS configuration that sets up module paths.
//
// This file is documented here:
// http://requirejs.org/docs/api.html#config
//
// Curran Kelleher March 2015
(function(){
// Use a fixed version of Chiasm, which provides the visualization runtime.
var chiasmPath = "//curran.github.io/cdn/chiasm-v0.1.4/client/src";
// Here's how to can use a local development version
// if this Gist is cloned into a sibling directory to the chiasm repo.
//var chiasmPath = "../../chiasm/client/src";
requirejs.config({
// Set up the Chiasm package.
// https://github.com/curran/chiasm
packages: [{
name: "chiasm",
location: chiasmPath + "/core"
}],
// Set up paths for Bower dependencies.
// Uses github.com/curran/cdn
paths: {
// AJAX library.
// http://jquery.com/
jquery: "//code.jquery.com/jquery-2.1.1.min",
// Visualization library.
// http://d3js.org/
d3: "//curran.github.io/cdn/d3-v3.5.5/d3.min",
// Reactive model library.
// https://github.com/curran/model
model: "//curran.github.io/cdn/model-v0.2.0/dist/model",
// Functional programming utilities.
// http://benmccormick.org/2014/11/12/underscore-vs-lodash/
lodash: "//cdnjs.cloudflare.com/ajax/libs/lodash.js/3.5.0/lodash.min",
// Asynchronous control flow.
// https://github.com/caolan/async
async: "//cdnjs.cloudflare.com/ajax/libs/async/0.9.0/async",
// Syntax-highlighted text editor for code.
// http://codemirror.net/
codemirror: "//curran.github.io/cdn/codemirror-v5.0.0",
// Provides interactive color picker and slider for CodeMirror.
// http://github.com/enjalot/Inlet.git
inlet: "//curran.github.io/cdn/inlet/inlet",
// Provides miltidimensional filtering.
// http://square.github.io/crossfilter/
crossfilter: "//cdnjs.cloudflare.com/ajax/libs/crossfilter/1.3.11/crossfilter.min",
// Configure paths for plugins loaded at runtime.
plugins: chiasmPath + "/plugins"
}
});
})();
// A Chiasm plugin for loading data from the Data Canvas Sense Your City API.
// http://map.datacanvas.org/#!/data
define(["model", "jquery", "lodash", "async"], function (Model, $, _, async){
return function (runtime) {
var model = Model({
publicProperties: []
}),
// See API documentation at http://map.datacanvas.org/#!/data
API_URL = "http://sensor-api.localdata.com/api/v1/aggregations.csv",
// List of all cities with available data.
cities = ["San Francisco", "Bangalore", "Boston", "Geneva", "Rio de Janeiro", "Shanghai", "Singapore"],
// The default parameters to pass into the API.
defaultParams = {
// Use averaging as the aggregation operator.
op: "mean",
// Include temperature only.
fields: "temperature",//,light,airquality_raw,sound,humidity,dust",
// Get data for every 5 minutes.
resolution: "5m",
}
// Fetches the latest data for a given city.
function getLatestDataForCity(city, callback){
// Get data for the last 24 hours.
// 1000 milliseconds/second, 60 seconds/minute, 5 minutes
var params = _.extend({
from: new Date(Date.now() - 1000 * 60 * 60 * 24).toISOString(),
before: new Date().toISOString(),
"over.city": city
}, defaultParams);
// Use jQuery to fetch the data.
// jQuery is used here rather than D3 because of its nice parameter syntax.
$.get(API_URL, params, function(csv) {
// Parse the CSV string.
callback(null, d3.csv.parse(csv, function(d){
// Parse ISO date strings into Date objects.
d.timestamp = new Date(d.timestamp);
// Parse strings into Numbers for numeric fields.
d.temperature = +d.temperature;
//d.light = +d.light
//d.airquality_raw = +d.airquality_raw
//d.sound = +d.sound
//d.humidity = +d.humidity
//d.dust = +d.dust
return d;
}));
});
};
// Fetches the current temperature across all cities.
function getLatestData(callback){
async.map(cities, getLatestDataForCity, function(err, results){
callback(err, _.flatten(results));
});
}
// Fetch the data and expose it to the model.
getLatestData(function(err, data){
model.data = data;
// Extract the most recent timestamp.
model.currentTimestamp = data[data.length - 1].timestamp;
});
// Provide a utility that rounds a given date to a 5-minute interval,
// for use with crossfilter.
model.when("selectedTimeApproximate", function(selectedTimeApproximate){
var selectedTime = new Date(selectedTimeApproximate);
selectedTime.setMilliseconds(0);
selectedTime.setSeconds(0);
// Round to 5 minute intervals to match with the data.
selectedTime.setMinutes(Math.round(selectedTime.getMinutes()/5)*5);
model.selectedTime = selectedTime;
});
return model;
};
});
/* Style the axes and labels for visualizations.
Curran Kelleher March 2015 */
/* Make the container fill the page. */
body {
background-color: black;
}
#container {
position: fixed;
left: 0px;
right: 0px;
top: 0px;
bottom: 0px;
}
#gear {
position: fixed;
right: 0px;
top: 0px;
background-color: gray;
border-radius: 20px;
}
#gear:hover {
background-color: lightgray;
}
#gear:active {
background-color: gray;
}
/* Tick mark labels */
.axis .tick text {
font: 8pt sans-serif;
fill: white;
}
/* Axis labels */
.axis text {
font: 14pt sans-serif;
fill: white;
}
/* Lines within axes. */
.axis path,
.axis line {
fill: none;
stroke: gray;
shape-rendering: crispEdges;
}
/* Lines within the line chart. */
.line {
fill: none;
stroke-width: 3px;
}
/* Style the title text at the top of the visualization. */
.title-text {
text-anchor: middle;
font: 20pt sans-serif;
fill: white;
}
.hover-line {
stroke: white;
stroke-width: 2px;
}
{
"layout": {
"plugin": "layout",
"state": {
"layout": {
"orientation": "horizontal",
"children": [
"editor",
{
"orientation": "vertical",
"children": [
"barChart",
"lineChart"
]
}
]
}
}
},
"editor": {
"plugin": "configEditor",
"state": {
"size": "0px"
}
},
"color": {
"plugin": "colorScale",
"state": {
"domain": [
"San Francisco",
"Bangalore",
"Boston",
"Geneva",
"Rio de Janeiro",
"Shanghai",
"Singapore"
],
"range": [
"#927400",
"#cc0006",
"#db008e",
"#8a3dff",
"#006aff",
"#00979b",
"#009100"
]
}
},
"barChart": {
"plugin": "barChart",
"state": {
"xColumn": "city",
"xAxisLabel": "City",
"yColumn": "temperature",
"yAxisLabel": "Temperature (°C)",
"sortColumn": "temperature",
"sortOrder": "descending",
"margin": {
"top": 32,
"right": 2,
"bottom": 45,
"left": 47
},
"xAxisLabelOffset": 2.128,
"yAxisLabelOffset": 1.4,
"colorDefault": "#059e00",
"title": "Temperature by City",
"titleOffset": -0.1617408,
"yDomainMin": 0
}
},
"lineChart": {
"plugin": "lineChart",
"state": {
"lineColumn": "city",
"colorColumn": "city",
"xColumn": "timestamp",
"xAxisLabel": "Time",
"yColumn": "temperature",
"yAxisLabel": "Temperature (°C)",
"margin": {
"top": 8,
"right": 2,
"bottom": 40,
"left": 47
},
"xAxisLabelOffset": 1.9,
"yAxisLabelOffset": 1.4,
"enableHoverLine": true
}
},
"senseYourCityData": {
"plugin": "senseYourCityData"
},
"crossfilter": {
"plugin": "crossfilter",
"state": {
"dimensions": [
"city",
"timestamp"
]
}
},
"links": {
"plugin": "links",
"state": {
"bindings": [
"senseYourCityData.data -> lineChart.data",
"senseYourCityData.data -> crossfilter.data",
"senseYourCityData.currentTimestamp -> lineChart.selectedX",
"lineChart.selectedX -> senseYourCityData.selectedTimeApproximate",
"senseYourCityData.selectedTime -> crossfilter.timestampFilter",
"crossfilter.cityTop -> barChart.data",
"color.domain -> lineChart.colorDomain",
"color.range -> lineChart.colorRange",
"color.domain -> barChart.colorDomain",
"color.range -> barChart.colorRange"
]
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment