Skip to content

Instantly share code, notes, and snippets.

@postfalk
Last active August 29, 2015 14:15
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 postfalk/1bd48e3923520747222c to your computer and use it in GitHub Desktop.
Save postfalk/1bd48e3923520747222c to your computer and use it in GitHub Desktop.
Ecoengine Visualization Example: Sensors Line Chart
@charset "utf-8";
/* CSS Document */
#sensor_select, #interval_select {
margin-top: 10px;
};
h1 a {
color:#157ab5;
text-decoration:none;
}
h1 a:hover{
text-decoration:none;
}
.x_axis path,
.x_axis line,
.y_axis path,
.y_axis line
{
fill: none;
stroke: #5B6770;
stroke-width: 1.5px;
shape-rendering: crispEdges;
}
.x_axis text,
.y_axis text
{
font-size: 12px;
font-family: "Helvetica Neue",Helvetica,Arial,sans-serif;
color: #555555;
}
.line {
fill: none;
stroke: #E04E39;
stroke-width: 2px;
}
.circle {
fill: #E04E39;
fill-opacity: 1;
stroke: #E04E39;
stroke-width: 1.5px;
}
.area {
fill: #E04E39;
}
rect.pane {
fill: none;
pointer-events: all;
}
div.tooltip_box {
position: absolute;
padding: 8px;
padding-left: 25px;
font: 15px;
pointer-events: none;
}
.selectors {
margin-left: 25px;
}
.well{
padding-bottom: 55px;
}
select{
width:auto;
}
<!-- this is used with jsfiddle only -->
<html>
<head>
</head>
<body>
<div class="selectors">
<select id="sensor_select">
</select>
<select id="interval_select">
<option class="interval_selector" value="hours">Hours</option>
<option class="interval_selector" value="days">Days</option>
<option class="interval_selector" value="weeks" selected>Weeks</option>
<option class="interval_selector" value="months">Months</option>
<option class="interval_selector" value="years">Years</option>
</select>
</div>
<div id="canvas" class="container">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script>
</body>
</html>
window.onload = (function () {
"use strict";
var base_url = 'https://ecoengine.berkeley.edu/api/sensors/',
d3canvas;
function create_url(interval, sensor, start, end) {
return base_url + sensor + '/aggregate/?format=json&page_size=10000&min_date=' + start + "&max_date=" + end + "&interval=" + interval;
}
function D3Obj() {
var data, timestring, tooltime,
margin = {top: 20, right: 20, bottom: 30, left: 50},
width = 900 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom,
x = d3.time.scale().range([0, width]),
y = d3.scale.linear().range([height, 0]),
xAxis = d3.svg.axis().scale(x).orient("bottom"),
yAxis = d3.svg.axis().scale(y).orient("left"),
format = d3.time.format("%Y-%m-%dT%H:%M:%S"),
svg = d3.select("#canvas").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")"),
rect = svg.append("svg:rect")
.attr("class", "pane")
.attr("width", width)
.attr("height", height),
div = d3.select("#canvas").append("div")
.attr("class", "tooltip_box")
.style("opacity", 1),
path2 = svg.append("path")
.attr("class", "line"),
circle = svg.append("circle")
.attr("r", 4)
.attr("cx", 0)
.attr("cy", 0)
.attr("class", "circle")
.attr("opacity", 0);
svg.append("g")
.attr("class", "x_axis")
.attr("transform", "translate(0," + height + ")");
svg.append("g")
.attr("class", "y_axis")
.attr("y", 6)
.attr("dy", ".71em");
// define value cursor
rect.on("mousemove", function (event) {
// http://jsfiddle.net/D4MRP/15/
var pathData = path2.data(),
X_px = d3.mouse(this)[0],
Y_px,
X_date = format(x.invert(X_px)),
Y_val,
X_val;
if (pathData[0]) {
pathData[0].forEach(function (element, index, array) {
if (index + 1 < array.length &&
array[index].begin_date <= X_date &&
array[index + 1].begin_date >= X_date
) {
Y_val = array[index + 1].mean;
X_val = array[index + 1].begin_date;
}
});
}
Y_px = y(Y_val);
X_px = x(format.parse(X_val));
timestring = d3.time.format("%Y %b %e &nbsp;%I:%M%p");
tooltime = timestring(format.parse(X_val));
circle
.attr("opacity", 1)
.attr("cx", X_px)
.attr("cy", Y_px);
if (Y_val !== null) {
d3.select(".tooltip_box")
.html("<b>Date:</b> " + tooltime + "<br><span style='color:#E04E39'><b>Value:</b></span> " + Number(Y_val.toFixed(3)));
} else {
d3.select(".tooltip_box")
.html("<b>Date:</b> " + tooltime + "<br><span style='color:#E04E39'><b>Value:</b></span> null");
}
});
this.update = function (json) {
data = json.results;
var line = d3.svg.line()
.x(function (d) { return x(format.parse(d.begin_date));})
.y(function (d) { return y(d.mean); });
x.domain(d3.extent(data, function (d) { return format.parse(d.begin_date); }));
y.domain(d3.extent(data, function (d) { return d.mean; }));
svg.selectAll(".x_axis")
.transition().duration(750).delay(500)
.call(xAxis);
svg.selectAll(".y_axis")
.transition().duration(750).delay(500)
.call(yAxis);
var path = svg.selectAll(".line")
.datum(data)
.transition().duration(750).delay(500)
.attr("d", line);
};
}
function Chart() {
this.update = function(args) {
var item;
// looping through argument literal and updating property when in literal
for(item in args) {
this[item] = args[item];
}
var url = create_url(this.interval, this.sensor, this.start, this.end);
d3.json(url, function(error, json) {
if (error) return console.warn(error);
d3canvas.update(json);
});
};
}
(function init() {
d3canvas = new D3Obj();
var chart = new Chart();
// populate menu
d3.json(base_url, function(error, json) {
if (error) return alert("Cannot access Ecoengine data");
// first introspect count than adjust page-size to make request
if (json.count) {
d3.json(base_url + '?page_size=' + json.count, function(error, json) {
var options = d3.select("#sensor_select").selectAll('option').data(json.results);
options.enter()
.append('option')
.attr('class', 'sensor_selector')
.attr('value', function (d) {return d.record;})
.html(function (d) {return d.station_name + ', ' + d.variable;});
// update on initial load
chart.update({
'sensor': json.results[0].record,
'interval': 'weeks',
'start': '1998-01-01',
'end': '2013-12-31'});
});
}
});
d3.select('#interval_select').on('change', function () {
chart.update({interval: d3.event.target.value});
});
d3.select('#sensor_select').on('change', function () {
chart.update({sensor: d3.event.target.value});
});
}());
}());
name: Ecoengine Sensor Line Visualization
description: This line chart has been built using data from the Berkeley Ecoinformatics Engine API.
authors:
- Kevin Koy
- Falk Schuetzenmeister
normalize_css: no
wrap: b
panel_js: 0
panel_css: 0
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<link rel="stylesheet" type="text/css" href="fiddle.css" />
</head>
<body>
<div class="selectors">
<select id="sensor_select">
</select>
<select id="interval_select">
<option class="interval_selector" value="hours">Hours</option>
<option class="interval_selector" value="days">Days</option>
<option class="interval_selector" value="weeks" selected>Weeks</option>
<option class="interval_selector" value="months">Months</option>
<option class="interval_selector" value="years">Years</option>
</select>
</div>
<div id="canvas" class="container">
</div>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.5/d3.min.js" charset="utf-8"></script>
<script src="fiddle.js"></script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment