D3
OG
Old school D3 from simpler times
All examples
By author
By category
About
mayblue9
Full window
Github gist
A visualization to explore timeseries of MIT wifi network data.
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>MIT Wifi - Timeseries</title> <link rel="stylesheet" href="styles.css"> <style> #banner { position: fixed; top: 0; left: 0; width: 100%; height: 320px; background-color: white; } #instructions { width: 170px; position: fixed; left: 0; top: 0; } #single-graph .access-point .line { fill: none; stroke-width: 1.5px; opacity: 1.0; } #single-graph { width: 1000px; height: 310px; padding-top:10px; margin: auto; } #single-graph-ui { float: right; padding-right: 10px; text-decoration: none; } #small-multiples { width: 1000px; margin: 330px auto; -webkit-column-width: 100px; -webkit-column-gap: 0px; -webkit-column-rule: none; } #small-multiples svg:hover, #small-multiples .selected { background-color: #EEEEEE; } #small-multiples .access-point .line { fill: none; stroke-width: 1.5px; opacity: 1.0; } #small-multiples ul { list-style-type: none; padding: 0; } #small-multiples ul li { float: right; padding: 0; } #small-multiples ul li .ap-id-text{ opacity: 0.25; font-family: sans-serif; font-size: 1.25em; } #small-multiples ul li .ap-id-text:hover{ opacity: 0.75; } body { font: 10px sans-serif; } .axis path { display: none; } .axis line { shape-rendering: crispEdges; stroke: #333; } .axis .minor line { stroke: #CCC; stroke-dasharray: 2,2; } </style> </head> <body> <div id="banner"> <div id="instructions"> <ol><label>Instructions:</label> <li>Scroll through timerseries of unique devices connected to access points around campus.</li> <li>Select/de-select a time series by clicking on it.</li> <li>Access points for the same building are shown in the same color.</li> </ol> </div> <div id="single-graph"> <div id="single-graph-ui"> <a href="#" onclick="UnselectAll()">clear</a> </div> </div> </div> <div id="small-multiples"> <ul id="small-multiples-list"></ul> </div> <script src="https://d3js.org/d3.v3.min.js" charset="utf-8"></script> <script> function SingleGraph() { this.margin = {top: 30, right: 20, bottom: 20, left: 20}; this.width = 1000 - this.margin.right - this.margin.left; this.height = 300 - this.margin.top - this.margin.bottom; this.x = d3.time.scale(). range([0, this.width]). domain([new Date('9/16/2014'), new Date('9/20/2014')]); this.y = d3.scale.linear(). range([this.height, 0]). domain([0, 20]); this.xAxis = d3.svg.axis() .scale(this.x) .ticks(d3.time.hours,6) .tickSize(-this.height) .orient("bottom"); this.yAxis = d3.svg.axis() .scale(this.y) .tickSize(this.width) .orient("right"); }; SingleGraph.prototype.Initialize = function() { var svg = d3.select("#single-graph").append("svg"). attr("width", this.width + this.margin.left + this.margin.right). attr("height", this.height + this.margin.top + this.margin.bottom) .append("g") .attr("class", "graph") .attr("transform", "translate(" + this.margin.left + "," + this.margin.top + ")"); var gx = svg.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + this.height + ")") .call(this.xAxis) gx.selectAll("g").filter(function(d) { return d; }) .classed("minor", true); var gy = svg.append("g") .attr("class", "y axis") .call(this.yAxis); gy.selectAll("g").filter(function(d) { return d; }) .classed("minor", true); gy.selectAll("text") .attr("x", 4) .attr("dy", -4); gy.append("text") .attr("transform", "rotate(-90)") .attr("y", -10) .attr("dy", ".71em") .style("text-anchor", "end") .text("Unique Devices"); }; SingleGraph.prototype.Clear = function() { d3.select("#single-graph svg .graph"). selectAll(".access-point"). data([]).exit().remove() }; SingleGraph.prototype.Update = function(selected_data) { this.Clear(); selected_data.series = d3.selectAll('.selected')[0].map(function(d) { return d.__data__;}); if (selected_data.series.length <= 0) {return true;}; var y = this.y.domain([0, d3.max(selected_data.series.map(function(d) { return d3.max(d.timeseries);}) )]); var x = this.x; // Update axis var yaxis = d3.select('#single-graph svg').select('.y.axis') yaxis.call(this.yAxis); yaxis.selectAll("g").filter(function(d) { return d; }) .classed("minor", true); yaxis.selectAll(".tick text") .attr("x", 4) .attr("dy", -4); var dateArray = d3.time.scale() .domain([new Date('9/16/2014'), new Date('9/20/2014')]) .ticks(d3.time.minutes, 15) var line = d3.svg.line(). interpolate("basis"). x(function(d, i) {return x(dateArray[i]);}). y(function(d) {return y(d);}); var access_points = d3.select("#single-graph svg .graph"). selectAll(".access-point"). data(selected_data.series). enter(). append("g"). attr("class", "access-point"); // Plot the timeseries access_points.append("path"). attr("class", "line"). attr("d", function(d, i) {return line(d.timeseries, i);}). attr("stroke", function(d) {return building_colors[d.building].toString();}); // Add a label. access_points.append("text"). attr("class", "ap-label"). attr("x", function(d) {return 900;}). attr("y", function(d,i) {return i*12;}). attr("text-anchor", "start"). attr("fill", function(d) {return building_colors[d.building].toString();}). text(function(d) { return d.ap_id.toUpperCase(); }); }; SingleGraph.prototype.UnselectAll = function() { d3.selectAll(".selected").classed("selected",false); this.Clear(); selected_data = {'series': []}; }; var raw_data; var timeseries_len; var selected_data = {}; var building_colors = {}; var selected_idx = d3.range(4).map(function() {return d3.round(Math.random()*100);}); var singleGraph = new SingleGraph(); singleGraph.Initialize(); d3.json("https://dl.dropboxusercontent.com/u/4035638/clean_timeseries_4day.json", function(error, json) { if (error) return console.warn(error); raw_data = json; timeseries_len = raw_data.series[0].timeseries.length; populate_building_colors(raw_data); small_multiples(raw_data); selected_data.series = raw_data.series.filter( function(d,i) {return selected_idx.indexOf(i) >= 0;}); singleGraph.Update(selected_data); }); function small_multiples(raw_data) { var margin = {top: 10, right: 10, bottom: 10, left: 10}; var width = 100 - margin.right - margin.left; var height = 50 - margin.top - margin.bottom; // Generate random timeseries data. var timeseries_len = 24*4; var x = d3.time.scale(). range([0, width]). domain([0, raw_data.series[0].timeseries.length]); var y = d3.scale.linear(). range([height, 0]). domain([0, 100]); var line = d3.svg.line(). interpolate("basis"). x(function(d, i) {return x(i);}). y(function(d) {return y(d);}); // Sort series by building number. raw_data.series.sort(function(a, b) { if (a.ap_id < b.ap_id) return -1; else return 1; }) // Plot timeseries. var access_point = d3.select("#small-multiples ul").selectAll("svg"). data(raw_data.series.slice(0,1000)). enter().append("li").append("svg"). attr("width", width + margin.left + margin.right). attr("height", height + margin.top + margin.bottom). on("click", function(d) { var clicked_data = this.__data__; if (!d3.select(this).classed("selected")) { d3.select(this).classed("selected", true); } else { selected_data.series = selected_data.series. filter(function(d) { return d.ap_id != clicked_data.ap_id; }); d3.select(this).classed("selected", false); } singleGraph.Update(selected_data); }). append("g"). attr("class", "access-point"). attr("transform", "translate(" + margin.left + "," + margin.top + ")"); // Add the spark line path. access_point.append("path") .attr("class", "line") .attr("d", function(d, i) { y.domain([0, d3.max(d.timeseries)]); return line(d.timeseries, i); }). attr("stroke", function(d) {return building_colors[d.building].toString();}); // Add some text access_point.append("text"). attr("class","ap-id-text"). attr("x", width/2). attr("y", (height + margin.top)/2). attr("text-anchor", "middle"). text(function(d) { return (d.building+"-"+d.room).toUpperCase(); }); }; function generate_random_data(ntimeseries, len) { /* * Generates random timeseries data. */ var data = d3.range(ntimeseries).map(function(d, i) { return { 'idx': i, 'building': Math.random() * 10, 'room': Math.random() * 300, 'ap_id': 'lorem-123', 'timeseries': generate_timeseries(len) } }); return data; }; function generate_timeseries(len) { /* * Generates a single random timeseries. */ return d3.range(len). map(function() {return Math.floor(Math.random()*100);}); }; function populate_building_colors(raw_data) { /* * Assigns a unique, random color to each building */ var buildings = d3.set(raw_data.series.map(function(d) { return d.building; })).values(); for(var i=0, building; building=buildings[i]; i++){ building_colors[building] = d3.rgb(d3.round(Math.random()*255), d3.round(Math.random()*255), d3.round(Math.random()*255)) } }; function UnselectAll() { console.log(singleGraph); singleGraph.UnselectAll(); }; </script> </body> </html>
Modified
http://d3js.org/d3.v3.min.js
to a secure url
https://d3js.org/d3.v3.min.js