Skip to content

Instantly share code, notes, and snippets.

@chrisbrich
Created April 4, 2012 21:33
Show Gist options
  • Save chrisbrich/2305872 to your computer and use it in GitHub Desktop.
Save chrisbrich/2305872 to your computer and use it in GitHub Desktop.
Data inheritance problem
<!DOCTYPE html>
<html>
<head>
<title>Line Chart</title>
<link type="text/css" rel="stylesheet" href="test.css"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.js?2.0.0"></script>
<script type="text/javascript" src="http://mbostock.github.com/d3/d3.time.js?2.0.0"/></script>
</head>
<body>
<script type="text/javascript" src="test.js"></script>
</body>
</html>
body {
font: 10px sans-serif;
}
.gridlines line {
shape-rendering: crispEdges;
stroke-width: 1.5px;
fill: none;
}
.gridlines line.line {
stroke: #eee;
}
line.edge {
stroke: gray;
stroke-linecap: square;
shape-rendering: crispEdges;
stroke-width: 1.5px;
fill: none;
}
.line{
stroke-width: 1.5px;
fill: none;
}
circle.datapoint {
fill: #fff;
}
rect.uiholder{
stroke: #000;
fill: steelblue;
}
rect.plotarea {
stroke: #000;
stroke-linejoin: miter;
fill: white;
fill-opacity: 0.0;
}
rect.background.legend{
stroke: #000;
stroke-linejoin: miter;
fill: white;
fill-opacity: 0.0;
}
div.plot{
float: left;
}
div.testplot{
float: left;
}
.axis {
shape-rendering: crispEdges;
}
.axis line, .axis path {
fill: none;
stroke: lightgrey;
}
var blarg = [[[{"title":"1","data":[{"x":1,"y":1},{"x":2,"y":2},{"x":3,"y":3},{"x":4,"y":4}]},
{"title":"2","data":[{"x":1,"y":1},{"x":2,"y":4},{"x":3,"y":9},{"x":4,"y":16}]}],
[{"title":"3","data":[{"x":1,"y":1},{"x":2,"y":8},{"x":3,"y":27},{"x":4,"y":64}]}]],
[[{"title":"4","data":[{"x":1,"y":2},{"x":2,"y":20},{"x":3,"y":40},{"x":4,"y":65}]}]]];
//For testing date purposes
var currenttime = new Date().getTime();
var blargtime = [[[{"title":"1","data":[{"x":new Date(currenttime - (60 * 1000 * 1)),"y":1},{"x":new Date(currenttime - (60 * 1000 * 2)),"y":2},
{"x":new Date(currenttime - (60 * 1000 * 3)),"y":3},{"x":new Date(currenttime - (60 * 1000 * 4)),"y":4}]},
{"title":"2","data":[{"x":new Date(currenttime - (60 * 1000 * 1)),"y":1},{"x":new Date(currenttime - (60 * 1000 * 2)),"y":4},
{"x":new Date(currenttime - (60 * 1000 * 3)),"y":9},{"x":new Date(currenttime - (60 * 1000 * 4)),"y":16}]}],
[{"title":"3","data":[{"x":new Date(currenttime - (60 * 1000 * 1)),"y":1},{"x":new Date(currenttime - (60 * 1000 * 2)),"y":8},
{"x":new Date(currenttime - (60 * 1000 * 3)),"y":27},{"x":new Date(currenttime - (60 * 1000 * 4)),"y":64}]}]],
[[{"title":"4","data":[{"x":new Date(currenttime - (60 * 1000 * 1)),"y":2},{"x":new Date(currenttime - (60 * 1000 * 2)),"y":20},
{"x":new Date(currenttime - (60 * 1000 * 3)),"y":40},{"x":new Date(currenttime - (60 * 1000 * 4)),"y":65}]}]]];
//holder for plots
d3.select("body").append("div")
.attr("class","plots")
.attr("style","float:right;width:600px;"); //div to hold plots
//clickable link for updating data
d3.select("body").append("a")
.attr("onclick","javascript:dateredraw(blargtime[Math.round(Math.random())]);")
.html("click me");
function dateredraw(d){
var blarg = timeSeriesChart();
d3.select("body div.plots").datum(d).call(blarg);}
//The reusable chart function
function timeSeriesChart() {
////////////
//Defaults//
////////////
var margin = {top: 25, right: 25, bottom: 25, left: 25},
width = 350,
height = 200,
legendWidth = 150,
legendColorSide = 15,
title = function(d){ return d.title;},
xValue = function(d) { return d.x; },
yValue = function(d) { return d.y; },
xScale = d3.time.scale(),
yScale = d3.scale.linear(),
xDomain = function(data){return d3.extent([].concat.apply([],data.map(function(e){return [].concat.apply([],e.map(function(d){return d3.extent(d.data,xValue)}))})))},
yDomain = function(data){return d3.extent([].concat.apply([],data.map(function(e){return [].concat.apply([],e.map(function(d){return d3.extent(d.data,yValue)}))})))},
xMin = function(data){return xDomain(data)[0]}, //assumes data is an array of objects
xMax = function(data){return new Date()}, //i.e. [{"title": "blarg" "data":[{}...]},{...}]
yMin = function(data){return yDomain(data)[0]},
yMax = function(data){return yDomain(data)[1]},
xAxis = d3.svg.axis().scale(xScale).orient("bottom").tickSize(-height),
yAxis = d3.svg.axis().scale(yScale).orient("left").tickSize(-width),
colors = d3.scale.category10();
// check that ymin and ymaxs are different
if (yMin == yMax){
var yeps = Math.pow(10,Math.floor(Math.log(yMin) / Math.LN10));
yMin = yMin - yeps;
yMax = yMax + yeps;};
///////////////
//Plot/Replot//
///////////////
function chart(selection) {
selection.each(function(data) {
// Update the x-scale.
xScale
.domain([xMin(data),xMax(data)])
.range([0, width]);
// Update the y-scale.
yScale
.domain([yMin(data),yMax(data)])
.range([height, 0]).nice();
xAxis.scale(xScale);
yAxis.scale(yScale);
//////////
//Enters//
//////////
// Select the svg element, if it exists.
var svg = d3.select(this).selectAll("svg").data(data); //.data([data])
// Otherwise, create the skeletal chart.
var gEnter = svg.enter().append("svg")
.append("g");
// Axis
gEnter.append("g").attr("class", "x axis");
gEnter.append("g").attr("class", "y axis");
// Cursor indicator
var tind = gEnter.append("svg:text")
.attr("class","position-indicator")
.attr("text-anchor","middle");
// Everything in plotarea will have its position displayed when mouse is over it
var plotarea = gEnter.append("svg:g").attr("class","plotarea");
// Display x/y coordinates on hover for everything in plotarea
plotarea.on("mouseover",function(d,i){
plotarea.on("mousemove",function(){
var b = d3.svg.mouse(this);
if (tind[0][i]) {
d3.select(tind[0][i])
.text(dateToString(xScale.invert(b[0])) + " , " + yScale.invert(b[1]).toPrecision(4));}});})
.on("mouseout",function(){
plotarea.on("mousemove",function(){
return false;})})
.append("svg:rect")
.attr("class", "plotarea");
///Legend
gEnter.append("svg:g")
.attr("class","legend")
.append("svg:rect")
.attr("class","legend background");
//NOT UPDATING PROPERLY//
var lgd = d3.selectAll("g.legend");
var lgddatasets = lgd.selectAll("g.dataset_legend")
.data(function(d){console.log(d);return d},function(d){return d.title});
var s = lgddatasets.enter().append("svg:g")
.attr("class",function(d){return "dataset_legend " + title(d)})
.attr("transform",function(d,i){return "translate(0," + i*legendColorSide + ")"});
s.append("svg:rect")
.attr("width",legendColorSide)
.attr("height",legendColorSide)
.attr("fill",function(d,i){console.log(i);return colors(i%10)});
s.append("svg:text")
.attr("x",legendColorSide)
.attr("y",legendColorSide/2)
.attr("dy",".35em");
// Datasets
//NOT UPDATING PROPERLY//
var test = d3.selectAll("g.plotarea");
var ds = test.selectAll("g.dataset")
.data(function(d){return d},title);
var set = ds.enter().append("svg:g")
.attr("class", function(d){return "dataset " + title(d)})
.attr("stroke", function(d,i){return colors(i%10)});
var myline = d3.svg.line()
.x(function(b) { return xScale(xValue(b)); })
.y(function(b) { return yScale(yValue(b)); });
set.append("svg:path")
.attr("class", function(d){return "line " + title(d)});
var datapoint = set.selectAll("circle.datapoint")
.data(function(d){return d.data;},function(d){return +d.x});
datapoint.enter().append("svg:circle")
.attr("class", "datapoint")
.attr("r", 1.5);
/////////////
// Updates //
/////////////
// Update the outer dimensions.
svg.attr("width", width + margin.left + margin.right + legendWidth)
.attr("height", height + margin.top + margin.bottom);
// Update the inner dimensions.
var g = svg.select("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
// Update the axes.
g.select(".x.axis")
.attr("transform", "translate(0," + yScale.range()[0] + ")")
.call(xAxis);
g.select(".y.axis")
.attr("transform", "translate(0," + xScale.range()[0] + ")")
.call(yAxis);
// Update cursor indicator
g.select("text.position-indicator")
.attr("x",width/2)
.attr("y",0)
.attr("dy","-.71em");
// Update legend
var legend = g.select("g.legend")
.attr("transform", "translate(" + (margin.right + width - 2) + ",0)");
legend.select("rect.background")
.attr("width",legendWidth)
.attr("height",height);
svg.selectAll("g.dataset_legend text")
.text(title);
// Update plotarea
var pa = g.select("g.plotarea");
pa.select("rect.plotarea")
.attr("width",width)
.attr("height",height);
// Update datasets
var dataset = svg.selectAll("g.dataset");
dataset.selectAll("path")
.attr("d", function(d){return myline(d.data);});
dataset.selectAll("circle.datapoint")
.attr("cx", function(d) { return xScale(xValue(d)); })
.attr("cy", function(d) { return yScale(yValue(d)); });
////////////////
///TRANSITIONS//
////////////////
//TODO//
//////////
///EXITS//
//////////
svg.exit().remove();
lgddatasets.exit().remove();
ds.exit().remove();
datapoint.exit().remove();
});
}
/////////////
//Accessors//
/////////////
chart.margin = function(_) {
if (!arguments.length) return margin;
margin = _;
return chart;
};
chart.width = function(_) {
if (!arguments.length) return width;
width = _;
return chart;
};
chart.height = function(_) {
if (!arguments.length) return height;
height = _;
return chart;
};
chart.legendWidth = function(_) {
if (!arguments.length) return legendWidth;
legendWidth = _;
return chart;
};
chart.xMin = function(_) {
if (!arguments.length) return xMin;
xMin = _;
return chart;
};
chart.xMax = function(_) {
if (!arguments.length) return xMax;
xMax = _;
return chart;
};
chart.yMin = function(_) {
if (!arguments.length) return yMin;
yMin = _;
return chart;
};
chart.yMax = function(_) {
if (!arguments.length) return yMax;
yMax = _;
return chart;
};
chart.colors = function(_) {
if (!arguments.length) return colors;
colors = _;
return chart;
};
chart.x = function(_) {
if (!arguments.length) return xValue;
xValue = _;
return chart;
};
chart.y = function(_) {
if (!arguments.length) return yValue;
yValue = _;
return chart;
};
return chart;
}
//Helper functions
function dateToString(d){
function pad(n){return n<10 ? '0'+n : n}
var theday = ["Sun","Mon","Tues","Wed","Thurs","Fri","Sat"]
return theday[d.getDay()]+" "
+ d.getFullYear()+'-'
+ pad(d.getMonth()+1)+'-'
+ pad(d.getDate())+' '
+ pad(d.getHours())+':'
+ pad(d.getMinutes())+':'
+ pad(d.getSeconds())}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment