d3.js brush with multiline chart -
i'm making multi-line chart , using brush select time periods. it's broadly based on mike bostock's example @ http://bl.ocks.org/mbostock/1667367
my chart @ http://lowercasen.com/dev/d3/general/piezobrush.html
my problem in selecting multiple lines in 'focus' area apply brush to. i've nested data based on key, data within function. because function calls brush outside function, can't access data , i'm getting typeerror: undefined not object (evaluating 'data.length')
here's code nests data:
datanest.foreach(function(d, i) { focus.append("path") .attr("class", "line") .attr("id", d.key.replace(/\s+/g, '')) //the replace stuff getting rid of spaces .attr("d", levelfocus(d.values)); context.append("path") .attr("class", "line") .attr("id", d.key.replace(/\s+/g, '')) //the replace stuff getting rid of spaces .attr("d", levelcontext(d.values));
and @ bottom have function brush:
function brushed() { xfocus.domain(brush.empty() ? xcontext.domain() : brush.extent()); focus.selectall(".line").attr("d", levelfocus(d.values)); focus.select(".x.axis").call(xaxisfocus); }
it works fine x axis (if comment out line i'm trying select lines) don't know how select lines correctly.
apologies garbled syntax or confusing language, coding skills basic @ best.
any appreciated, i've searched hours solution.
here's full code requested lars
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <title>multiline brush</title> <script src="http://d3js.org/d3.v3.js"></script> <script src="d3/tooltip.js"></script> <link href="styles/evidentlysocharts.css" rel="stylesheet"> <meta name="viewport" content="initial-scale=1"> <style> svg { font: 10px sans-serif; } path { stroke-width: 1; fill: none; } #stream1, #nebo1d { stroke: #009390; } #stream1legend, #nebo1dlegend { fill: #009390; } #stream2, #nebo2d { stroke: #8dc63f; } #stream2legend, #nebo2dlegend { fill: #8dc63f; } #stream3, #nebo1s { stroke: #132d46; } #stream3legend, #nebo1slegend { fill: #132d46; } #stream4, #nebo2s { stroke: #aaa813; } #stream4legend, #nebo2slegend { fill: #aaa813; } #stream5, #nebo3 { stroke: #619dd4; } #stream5legend, #nebo3legend { fill: #619dd4; } .pn1d, .pn2d { fill: none; clip-path: url(#clip); } .pn1d { stroke: #009390; } .pn2d { stroke: #1b4164; } .axis path, .axis line { fill: none; stroke: #000; stroke-width: 1px; shape-rendering: crispedges; } .brush .extent { stroke: #fff; fill-opacity: .125; shape-rendering: crispedges; } </style> </head> <body> <script> var marginfocus = {top: 10, right: 10, bottom: 250, left: 40}, margincontext = {top: 430, right: 10, bottom: 170, left: 40}, width = 960 - marginfocus.left - marginfocus.right, heightfocus = 650 - marginfocus.top - marginfocus.bottom, heightcontext = 650 - margincontext.top - margincontext.bottom; legendoffset = 550; var parsedate = d3.time.format("%d/%m/%y %h:%m").parse; var xfocus = d3.time.scale().range([0, width]), xcontext = d3.time.scale().range([0, width]), yfocus = d3.scale.linear().range([heightfocus, 0]), ycontext = d3.scale.linear().range([heightcontext, 0]); var xaxisfocus = d3.svg.axis().scale(xfocus).orient("bottom"), xaxiscontext = d3.svg.axis().scale(xcontext).orient("bottom"), yaxisfocus = d3.svg.axis().scale(yfocus).orient("left"); var levelfocus = d3.svg.line() .interpolate("linear") .x(function(d) { return xfocus(d.date); }) .y(function(d) { return yfocus(d.level); }); var levelcontext = d3.svg.line() .interpolate("linear") .x(function(d) { return xcontext(d.date); }) .y(function(d) { return ycontext(d.level); }); var svg = d3.select("body").append("svg") .attr("width", width + marginfocus.left + marginfocus.right) .attr("height", heightfocus + marginfocus.top + marginfocus.bottom); svg.append("defs").append("clippath") .attr("id", "clip") .append("rect") .attr("width", width) .attr("height", heightfocus); var focus = svg.append("g") .attr("class", "focus") .attr("transform", "translate(" + marginfocus.left + "," + marginfocus.top + ")"); var context = svg.append("g") .attr("class", "context") .attr("transform", "translate(" + margincontext.left + "," + margincontext.top + ")"); d3.csv("data/piezonebonestsimple.csv", function(error, data) { data.foreach(function(d) { d.date = parsedate(d.date); d.level = +d.level; }); xfocus.domain(d3.extent(data.map(function(d) { return d.date; }))); yfocus.domain([d3.min(data.map(function(d) { return d.level; })) -2,0]); xcontext.domain(xfocus.domain()); ycontext.domain(yfocus.domain()); // nest entries piezo var datanest = d3.nest() .key(function(d) {return d.piezo;}) .entries(data); legendspace = width/datanest.length; // spacing legend // ****** var brush = d3.svg.brush() .x(xcontext) .on("brush", brushed); focus.selectall("g").data(datanest) .enter() .append("g") .attr("class", "line") .attr("id", function(d) { return d.key.replace(/\s+/g, '') }) //the replace stuff getting rid of spaces .append("path") .attr("d", function(d) { return levelfocus(d.values); }); context.selectall("g").data(datanest) .enter() .append("g") .attr("class", "line") .attr("id", function(d) { return d.key.replace(/\s+/g, '') }) //the replace stuff getting rid of spaces .append("path") .attr("d", function(d) { return levelcontext(d.values); }); focus.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + heightfocus + ")") .call(xaxisfocus); focus.append("g") .attr("class", "y axis") .call(yaxisfocus); context.append("g") .attr("class", "x axis") .attr("transform", "translate(0," + heightcontext + ")") .call(xaxiscontext); context.append("g") .attr("class", "x brush") .call(brush) .selectall("rect") .attr("y", -6) .attr("height", heightcontext + 7); function brushed() { xfocus.domain(brush.empty() ? xcontext.domain() : brush.extent()); focus.selectall(".line").attr("d", levelfocus(datanest.values)); focus.select(".x.axis").call(xaxisfocus); } }); </script> </body> </html>
it boils down 2 things far can see. first, elements you're selecting updated g
, not path
elements , second, need reference data bound elements in order set d
. both fixed , brushed
function looks this.
function brushed() { xfocus.domain(brush.empty() ? xcontext.domain() : brush.extent()); focus.selectall("path").attr("d", function(d) { return levelfocus(d.values); }); focus.select(".x.axis").call(xaxisfocus); }
complete demo here. note bits still missing, in particular clip path restrict lines chart area. can copied , pasted directly example you've referenced though.
Comments
Post a Comment