Skip to content

Instantly share code, notes, and snippets.

@theon
Created January 13, 2013 19:28
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 theon/4525788 to your computer and use it in GitHub Desktop.
Save theon/4525788 to your computer and use it in GitHub Desktop.
Example of a cubism chart used to chart moisture levels from an arduino plant watering system that writes plant moisture levels to a cube server. See: http://theon.github.com/plant-watering-with-arduino.html
<html>
<head>
<script type="text/javascript" src="https://raw.github.com/mbostock/d3/master/d3.min.js"></script>
<script type="text/javascript" src="https://raw.github.com/square/cubism/master/cubism.v1.min.js"></script>
<style>
.time-series {
font-family: "Helvetica Neue", Helvetica, sans-serif;
margin: 30px auto;
width: 800px;
position: relative;
}
.time-series .axis {
font: 10px sans-serif;
pointer-events: none;
z-index: 2;
}
.time-series .axis text {
-webkit-transition: fill-opacity 250ms linear;
}
.time-series .axis path {
display: none;
}
.time-series .axis line {
stroke: #000;
shape-rendering: crispEdges;
}
.time-series .axis.top {
background-image: linear-gradient(top, #fff 0%, rgba(255,255,255,0) 100%);
background-image: -o-linear-gradient(top, #fff 0%, rgba(255,255,255,0) 100%);
background-image: -moz-linear-gradient(top, #fff 0%, rgba(255,255,255,0) 100%);
background-image: -webkit-linear-gradient(top, #fff 0%, rgba(255,255,255,0) 100%);
background-image: -ms-linear-gradient(top, #fff 0%, rgba(255,255,255,0) 100%);
top: 0px;
}
.time-series .axis.bottom {
background-image: linear-gradient(bottom, #fff 0%, rgba(255,255,255,0) 100%);
background-image: -o-linear-gradient(bottom, #fff 0%, rgba(255,255,255,0) 100%);
background-image: -moz-linear-gradient(bottom, #fff 0%, rgba(255,255,255,0) 100%);
background-image: -webkit-linear-gradient(bottom, #fff 0%, rgba(255,255,255,0) 100%);
background-image: -ms-linear-gradient(bottom, #fff 0%, rgba(255,255,255,0) 100%);
bottom: 0px;
}
.time-series .horizon {
border-bottom: solid 1px #000;
overflow: hidden;
position: relative;
}
.time-series .horizon {
border-top: solid 1px #000;
border-bottom: solid 1px #000;
}
.time-series .horizon + .horizon {
border-top: none;
}
.time-series .horizon canvas {
display: block;
}
.time-series .horizon .title,
.time-series .horizon .value {
bottom: 0;
line-height: 30px;
margin: 0 6px;
position: absolute;
text-shadow: 0 1px 0 rgba(255,255,255,.5);
white-space: nowrap;
font-size: 120%;
}
.time-series .horizon .title {
left: 0;
}
.time-series .horizon .value {
right: 0;
}
.time-series .line {
background: #000;
opacity: .2;
z-index: 2;
}
@media all and (max-width: 1439px) {
.time-series { margin: 0px auto; }
.time-series .axis { position: static; }
.time-series .axis.top, .time-series .axis.bottom { padding: 0; }
}
</style>
<script type="text/javascript">
var contexts = [];
var moistureHeight = 300;
var moistureExtent = 1023;
function renderTimeSeries(expression, title, container, extent, step, colours) {
var context = cubism.context()
.serverDelay(0)
.clientDelay(0)
.step(step) //3e5 5 minute
.size(800);
// 1e4 - 10-second
// 6e4 - 1-minute
// 3e5 - 5-minute
// 36e5 - 1-hour
// 864e5 - 1-day
contexts.push(context);
var horizon = context.horizon();
horizon.height(moistureHeight);
horizon.title(title);
horizon.extent(extent);
horizon.colors(colours);
var cube = context.cube("http://54.247.0.0");
var metric = cube.metric(expression);
var metrics = [
metric
];
d3.select(container).selectAll(".axis")
.data(["top", "bottom"])
.enter().append("div")
.attr("class", function(d) { return d + " axis"; })
.each(function(d) { d3.select(this).call(context.axis().ticks(12).orient(d)); });
d3.select(container).selectAll(".horizon")
.data(metrics)
.enter().insert("div", ".bottom")
.attr("class", "horizon")
.call(horizon);
context.on("focus", function(i) {
d3.selectAll(container + " .value").style("right", i == null ? null : context.size() - i + "px");
var val = parseInt(metric.valueAt(parseInt(i)));
if(!isNaN(val)) {
d3.selectAll(container + " .value").text(val);
}
});
}
function addRules() {
for(var i=0; i<contexts.length; i++) {
d3.selectAll(".time-series").append("div")
.attr("class", "rule")
.call(contexts[i].rule());
}
}
function drawWaterLine() {
var canvas = document.getElementById("moisture-time-series").getElementsByTagName("canvas")[0];
var ctx = canvas.getContext("2d");
ctx.strokeStyle = "#F66";
ctx.lineWidth = 1;
var amount = (moistureExtent - 350) * (moistureHeight / moistureExtent);
ctx.moveTo(0, amount);
ctx.lineTo(1000, amount);
ctx.stroke();
}
</script>
</head>
<body>
<div id="moisture-time-series" class="time-series">
<script type="text/javascript">
renderTimeSeries("1023 - (sum(moisture(moisture)) / sum(moisture))", "Soil Moisture", "#moisture-time-series", [0, moistureExtent], 3e5, ["#31a354", "#E9967A"]);
</script>
</div>
<div id="watering-time-series" class="time-series">
<script type="text/javascript">
renderTimeSeries("max(moisture(watered))", "Watering Events", "#watering-time-series", [0, 1], 3e5, ["#08519c", "#6baed6"]);
</script>
</div>
<script type="text/javascript">
addRules();
setTimeout(drawWaterLine, 1000);
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment