d3 using zoom and brush on a bar chart - d3.js

Hi I am trying to adapt Matthew Izanuk's Unit Bar Chart with Brush and Zoom to a randomly generated bar chart I have already created.
I get an error with the zoom function, on line 261
line 261 is
x.domain(t.rescaleX(x2).domain());
Here is the code and jsFiddle
<!DOCTYPE html>
<body>
<style>
div {
display: inline-block;
vertical-align: top;
}
#bar_chart {
border: 2px solid lightgray;
border-radius: 15px;
}
#json {
max-height: 600px;
width: 200px;
overflow: scroll;
border: 2px solid gray;
border-radius: 15px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
<div id="bar_chart">
</div>
<div id="json"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//************* generate data ************
var data = [];
var space = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var spaceLength = space.length;
makedata();
function makedata() {
var obj = {};
for (var i = 0; i < spaceLength; i++) {
obj = {};
value = Math.floor(Math.random() * 500);
rand = Math.floor(Math.random() * space.length)
name = space.charAt(rand);
obj["name"] = name;
obj["val"] = value;
data.push(obj);
space = space.slice(0, rand) + space.slice(rand + 1, space.length)
}
}
// To display json in html page
document.getElementById("json").innerHTML = "<pre>" + JSON.stringify(data, null, 4) + "</pre>";
var margin = {
top: 50,
right: 20,
bottom: 90,
left: 50
},
margin2 = {
top: 530,
right: 20,
bottom: 30,
left: 50
},
width = 1000 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom,
height2 = 600 - margin2.top - margin2.bottom;
var x = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var x2 = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height, 0]);
var y2 = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height2, 0]);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("brush", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var svg = d3.select("#bar_chart")
// .data(data)
.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 + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var g0 = focus.append("g")
.attr("class", "focus")
.attr("transform", "translate(0,0)");
var xAxis = d3.axisBottom(x);
var xAxis2 = d3.axisBottom(x2);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
focus.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(7));
var tooltip = d3.select("#info")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
var focus_group = focus.append("g");
focus_group.attr("clip-path", "url(#clip)");
var rects = focus_group.selectAll('rect')
.data(data);
//********* Bar Chart 1 ****************
var newRects1 = rects.enter();
newRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x(d.name);
})
.attr('y', function(d, i) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.attr("transform", "translate(" + 4 + ",0)")
.style('fill', 'lightblue')
.style('stroke', 'gray');
var focus_group = context.append("g");
focus_group.attr("clip-path", "url(#clip)");
var brushRects = focus_group.selectAll('rect')
.data(data);
//********* Brush Bar Chart ****************
var brushRects1 = brushRects.enter();
brushRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x2(d.name);
})
.attr('y', function(d, i) {
return y2(d.val);
})
.attr('height', function(d, i) {
return height2 - y2(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.attr("transform", "translate(" + 4 + ",0)")
.style('fill', 'lightblue')
.style('stroke', 'gray');
//append brush xAxis2
context.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
//create brush function redraw scatterplot with selection
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.selectAll(".mainBars")
.attr("x", function(d) {
return x(d.name);
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x-axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.selectAll(".mainBars")
.attr("x", function(d) {
return x(d.name);
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x-axis").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
</script>
Any help would be greatly appreciated,
thanks

As mentioned in the comments, invert only exists for continuous ranges. Your scaleBand is a ordinal scale composed of discreet values. One way I've worked around this is to simply figure which values in my domain fall in the selection:
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return;
// ignore brush-by-zoom
// get bounds of selection
var s = d3.event.selection,
nD = [];
// for each "tick" in our domain is it in selection
x2.domain().forEach((d)=>{
var pos = x2(d) + x2.bandwidth()/2;
if (pos > s[0] && pos < s[1]){
nD.push(d);
}
});
// set new domain
x.domain(nD);
// redraw
focus.selectAll(".mainBars")
// hide bars not in domain
.style("opacity", function(d){
return x.domain().indexOf(d.name) === -1 ? 0 : 100;
})
.attr("x", function(d) {
return x(d.name)+ x.bandwidth()/2 - 5;
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x.axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
Running code:
<!DOCTYPE html>
<head>
<style>
div {
display: inline-block;
vertical-align: top;
}
#bar_chart {
border: 2px solid lightgray;
border-radius: 15px;
}
#json {
max-height: 600px;
width: 200px;
overflow: scroll;
border: 2px solid gray;
border-radius: 15px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
</style>
</head>
<body>
<div id="bar_chart">
</div>
<div id="json"></div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
//************* generate data ************
var data = [];
var space = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz";
var spaceLength = space.length;
makedata();
function makedata() {
var obj = {};
for (var i = 0; i < spaceLength; i++) {
obj = {};
value = Math.floor(Math.random() * 500);
rand = Math.floor(Math.random() * space.length)
name = space.charAt(rand);
obj["name"] = name;
obj["val"] = value;
data.push(obj);
space = space.slice(0, rand) + space.slice(rand + 1, space.length)
}
}
// To display json in html page
document.getElementById("json").innerHTML = "<pre>" + JSON.stringify(data, null, 4) + "</pre>";
var margin = {
top: 50,
right: 20,
bottom: 90,
left: 50
},
margin2 = {
top: 530,
right: 20,
bottom: 30,
left: 50
},
width = 1000 - margin.left - margin.right,
height = 600 - margin.top - margin.bottom,
height2 = 600 - margin2.top - margin2.bottom;
var x = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var x2 = d3.scaleBand()
.domain(data.map(function(d) {
return d.name
}))
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height, 0]);
var y2 = d3.scaleLinear()
.domain([0, d3.max(data, function(d) {
return d.val
})])
.range([height2, 0]);
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("brush", brushed);
var zoom = d3.zoom()
.scaleExtent([1, Infinity])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var svg = d3.select("#bar_chart")
// .data(data)
.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 + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
var xAxis = d3.axisBottom(x);
var xAxis2 = d3.axisBottom(x2);
focus.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
focus.append("g")
.attr("class", "axis")
.call(d3.axisLeft(y)
.ticks(7));
var tooltip = d3.select("#info")
.append("div")
.style("position", "absolute")
.style("z-index", "10")
.style("visibility", "hidden");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
var focus_group = focus.append("g");
focus_group.attr("clip-path", "url(#clip)");
var rects = focus_group.selectAll('rect')
.data(data);
//********* Bar Chart 1 ****************
var newRects1 = rects.enter();
newRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x(d.name) + x.bandwidth()/2;
})
.attr('y', function(d, i) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.style('fill', 'lightblue')
.style('stroke', 'gray');
var focus_group = context.append("g");
focus_group.attr("clip-path", "url(#clip)");
var brushRects = focus_group.selectAll('rect')
.data(data);
//********* Brush Bar Chart ****************
var brushRects1 = brushRects.enter();
brushRects1.append('rect')
.attr('class', 'bar mainBars')
.attr('x', function(d, i) {
return x2(d.name);
})
.attr('y', function(d, i) {
return y2(d.val);
})
.attr('height', function(d, i) {
return height2 - y2(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10)
.attr("transform", "translate(" + 4 + ",0)")
.style('fill', 'lightblue')
.style('stroke', 'gray');
//append brush xAxis2
context.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
//create brush function redraw scatterplot with selection
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
// get bounds of selection
var s = d3.event.selection,
nD = [];
x2.domain().forEach((d)=>{
var pos = x2(d) + x2.bandwidth()/2;
if (pos > s[0] && pos < s[1]){
nD.push(d);
}
});
x.domain(nD);
focus.selectAll(".mainBars")
.style("opacity", function(d){
return x.domain().indexOf(d.name) === -1 ? 0 : 100;
})
.attr("x", function(d) {
return x(d.name)+ x.bandwidth()/2 - 5;
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x.axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
}
function zoomed() {
/*
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.selectAll(".mainBars")
.attr("x", function(d) {
return x(d.name) + x.bandwidth()/2;
})
.attr("y", function(d) {
return y(d.val);
})
.attr('height', function(d, i) {
return height - y(d.val)
})
.attr('opacity', 0.85)
.attr('width', 10);
focus.select(".x-axis").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
*/
}
</script>

Related

how to convert scatter chart into line chart in d3 version 3

I have implemented one scatter chart using d3.js. I want to convert this chart to line chart, but i am not able to do so. I have tried to follow ( http://embed.plnkr.co/wJDcZmkEzXaLVhuLZmcQ/ ) but it didn't helped me.
This is the code for scatter chart.
var data = [{"buildName":"otfa_R5-10_a1","build":"Build 1","value":"19628"},{"buildName":"otfa_R5-91_a1","build":"Build 2","value":"19628"},{"buildName":"otfa_R5-9_a1","build":"Build 3","value":"19628"}]
var yValues = [], responseData = [];
data.map(function(key) {
var test = [];
test[0] = key.build;
test[1] = key.value;
responseData.push(test);
yValues = key.value;
})
var margin = {
top: 20,
right: 15,
bottom: 60,
left: 60
},
width = 300 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(responseData.map(function(d) {
return d[0];
}))
.rangePoints([0, width], 0.5)
var y = d3.scale.linear()
.domain([5000,20000])
.range([height, 0]);
var chart = d3.select(divId)
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var colors = d3.scale.linear()
.domain([5, 20])
.range(['#4577bc', '#4577bc'])
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)" );
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var g = main.append("svg:g");
g.selectAll("scatter-dots")
.data(responseData)
.enter().append("svg:circle")
.attr("cx", function(d, i) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 6)
.style('stroke', function(d, i) {
return colors(i);
})
.style('fill', function(d, i) {
return colors(i);
})
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", "#fff8ee");
div.transition()
.duration(200)
.style("opacity", 2.9);
div .html((d[1]))
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 18) + "px");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "#4577bc");
div.transition()
.duration(500)
.style("opacity", 0);
});
How we can add a line connecting these points ?
Please help me !!
To add a line to your existing chart, just add it using path generators.
Line generator:
var line = d3.svg.line()
.x(function (d) { return x(d[0]); })
.y(function (d) { return y(d[1]); });
Append the line to the svg:
g.append('path').classed('line', true)
.style( { fill: 'none', 'stroke': 'steelblue'} )
.attr('d', line(responseData));
Snippet with the above code included and a few CSS styles to make it look better:
var data = [{"buildName":"otfa_R5-10_a1","build":"Build 1","value":"19628"},{"buildName":"otfa_R5-91_a1","build":"Build 2","value":"10628"},{"buildName":"otfa_R5-9_a1","build":"Build 3","value":"17628"}]
var yValues = [], responseData = [];
data.map(function(key) {
var test = [];
test[0] = key.build;
test[1] = key.value;
responseData.push(test);
yValues = key.value;
})
var margin = {
top: 20,
right: 15,
bottom: 60,
left: 60
},
width = 300 - margin.left - margin.right,
height = 200 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.domain(responseData.map(function(d) {
return d[0];
}))
.rangePoints([0, width], 0.5)
var y = d3.scale.linear()
.domain([5000,20000])
.range([height, 0]);
var chart = d3.select('body')
.append('svg:svg')
.attr('width', width + margin.right + margin.left)
.attr('height', height + margin.top + margin.bottom)
.attr('class', 'chart')
var colors = d3.scale.linear()
.domain([5, 20])
.range(['#4577bc', '#4577bc'])
var main = chart.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')')
.attr('width', width)
.attr('height', height)
.attr('class', 'main')
// draw the x axis
var xAxis = d3.svg.axis()
.scale(x)
.orient('bottom');
main.append('g')
.attr('transform', 'translate(0,' + height + ')')
.attr('class', 'main axis date')
.call(xAxis)
.selectAll("text")
.style("text-anchor", "end")
.attr("dx", "-.8em")
.attr("dy", ".15em")
.attr("transform", "rotate(-45)" );
// draw the y axis
var yAxis = d3.svg.axis()
.scale(y)
.orient('left');
main.append('g')
.attr('transform', 'translate(0,0)')
.attr('class', 'main axis date')
.call(yAxis);
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var g = main.append("svg:g");
g.selectAll("scatter-dots")
.data(responseData)
.enter().append("svg:circle")
.attr("cx", function(d, i) {
return x(d[0]);
})
.attr("cy", function(d) {
return y(d[1]);
})
.attr("r", 6)
.style('stroke', function(d, i) {
return colors(i);
})
.style('fill', function(d, i) {
return colors(i);
})
.on("mouseover", function(d) {
d3.select(this).attr("r", 10).style("fill", "#fff8ee");
div.transition()
.duration(200)
.style("opacity", 2.9);
div .html((d[1]))
.style("left", (d3.event.pageX+4) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
d3.select(this).attr("r", 5.5).style("fill", "#4577bc");
div.transition()
.duration(500)
.style("opacity", 0);
});
var line = d3.svg.line()
.x(function (d) { return x(d[0]); })
.y(function (d) { return y(d[1]); });
g.append('path').classed('line', true)
.style( { fill: 'none', 'stroke': 'steelblue'} )
.attr('d', line(responseData));
path.domain {
fill: none;
stroke: #000;
}
.axis text {
font-size: 12px;
}
div.tooltip {
position: absolute;
background: #FFF;
padding: 5px;
border: 1px solid #DDD;
pointer-events: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.0/d3.min.js"></script>

Smooth scrolling brush with scaleband like string labels [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 3 years ago.
Improve this question
The problem: Using scaleband does not pan smoothly using brush.
https://shanegibney.github.io/D3-v4-Bar-chart-Scaleband-with-Brush/
Here is an example of smooth panning with dates on x-axis.
https://shanegibney.github.io/d3-Bar-Chart-Pan-And-Zoom/
But this only works when the x-domain is continuous.
Using a linearscale, I would like to replace the values on the x-axis with the string in data d.name. The issue is that when zooming new ticks are created in between the value. For example the values 1 and 2 when zoomed will produce values and tciks for 1, 1.5, 2 and 2.5 etc. This means that the names do not match up anymore.
This could be solved with two things;
Prevent new ticks appearing when zooming in. I don't know how to do this.
Replace each label on the x-axis ticks with d.name from the data
These are my attempts to do this,
var xAxis = d3.axisBottom(x)
.tickFormat(function(d, i) {
label = data[i].name;
local.set(this, data[i].name);
return "test";
return data[i].name;
return label;
return i;
return d;
});
None of which work.
The Plunker
https://plnkr.co/edit/zbWu6AV8MNO9N8m0byre
Inside the tickFormat function, check if the numeric tick corresponds to any value in the data array. If it does, return the name, otherwise return nothing:
var xAxis = d3.axisBottom(x)
.tickFormat(function(d) {
var found = data.find(function(e){
return e.num === d
});
return found ? found.name : null;
})
Here is the code with that change:
<!DOCTYPE html>
<meta charset="utf-8">
<style type="text/css">
body {
font-family: avenir next, sans-serif;
font-size: 12px;
}
.zoom {
cursor: move;
fill: none;
pointer-events: all;
}
.axis {
stroke-width: 0.5px;
stroke: #888;
font: 10px avenir next, sans-serif;
}
.axis>path {
stroke: #888;
}
</style>
<body>
<div id="totalDistance">
</div>
</body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
/* Adapted from: https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172 */
var data = [{
"name": "A",
"date": "2016-11-09 11:15",
"num": "1",
"distance": "1900"
},
{
"name": "B",
"date": "2016-11-10 10:40",
"num": "2",
"distance": "1500"
},
{
"name": "C",
"date": "2016-11-11 16:45",
"num": "3",
"distance": "2500"
},
{
"name": "D",
"date": "2016-11-12 12:48",
"num": "4",
"distance": "2300"
},
{
"name": "E",
"date": "2016-11-15 20:00",
"num": "5",
"distance": "2000"
}
];
var margin = {
top: 20,
right: 20,
bottom: 90,
left: 50
},
margin2 = {
top: 230,
right: 20,
bottom: 30,
left: 50
},
width = 960 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom,
height2 = 300 - margin2.top - margin2.bottom;
var parseTime = d3.timeParse("%Y-%m-%d %H:%M");
var local = d3.local();
var x = d3.scaleLinear().range([0, width]),
x2 = d3.scaleLinear().range([0, width]),
y = d3.scaleLinear().range([height, 0]),
y2 = d3.scaleLinear().range([height2, 0]);
// dur = d3.scaleLinear().range([0, 12]);
var tickLabels = ['a', 'b', 'c', 'd'];
var newData = [];
data.forEach(function(e) {
newData.push({
"name": e.name
});
});
//var xAxis = d3.axisBottom(x).tickSize(0);
var xAxis2 = d3.axisBottom(x2).tickSize(0)
.tickFormat(function(d) {
var found = data.find(function(e) {
return e.num === d
});
return found ? found.name : null;
})
yAxis = d3.axisLeft(y).tickSize(0);
var xAxis = d3.axisBottom(x)
.tickFormat(function(d) {
var found = data.find(function(e) {
return e.num === d
});
if (!found) {
d3.select(this.parentNode).select("line").remove()
}
return found ? found.name : null;
})
// var xAxis = d3.axisBottom(x)
// .tickFormat(function(d, i) {
// label = data[i].name;
// local.set(this, data[i].name);
// return "test";
// return data[i].name;
// return label;
// return i;
// return d;
// });
var brush = d3.brushX()
.extent([
[0, 0],
[width, height2]
])
.on("start brush end", brushed);
var zoom = d3.zoom()
.scaleExtent([1, 10])
.translateExtent([
[0, 0],
[width, height]
])
.extent([
[0, 0],
[width, height]
])
.on("zoom", zoomed);
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom);
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append("g")
.attr("class", "focus")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var context = svg.append("g")
.attr("class", "context")
.attr("transform", "translate(" + margin2.left + "," + margin2.top + ")");
// d3.json("data.json", function(error, data) {
// if (error) throw error;
var parseTime = d3.timeParse("%Y-%m-%d %H:%M");
var mouseoverTime = d3.timeFormat("%a %e %b %Y %H:%M");
var minTime = d3.timeFormat("%b%e, %Y");
var parseDate = d3.timeParse("%b %Y");
data.forEach(function(d) {
d.distance = +d.distance;
d.num = +d.num;
return d;
},
function(error, data) {
if (error) throw error;
});
var total = 0;
data.forEach(function(d) {
total = d.distance + total;
});
var minDate = d3.min(data, function(d) {
return d.num;
});
var xMin = d3.min(data, function(d) {
return d.num;
});
var yMax = Math.max(20, d3.max(data, function(d) {
return d.distance;
}));
x.domain([xMin, d3.max(data, function(d) {
return d.num;
})]);
y.domain([0, yMax]);
x2.domain(x.domain());
y2.domain(y.domain());
var rects = focus.append("g");
rects.attr("clip-path", "url(#clip)");
rects.selectAll("rects")
.data(data)
.enter().append("rect")
.style("fill", function(d) {
return "#8DA5ED";
})
.attr("class", "rects")
.attr("x", function(d) {
return x(d.num);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", function(d) {
return 10;
})
.attr("height", function(d) {
return height - y(d.distance);
});
focus.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
focus.append("g")
.attr("class", "axis axis--y")
.call(yAxis);
// Summary Stats
focus.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 0 - margin.left)
.attr("x", 0 - (height / 2))
.attr("dy", "1em")
.style("text-anchor", "middle")
.text("Distance in meters");
svg.append("text")
.attr("transform",
"translate(" + ((width + margin.right + margin.left) / 2) + " ," +
(height + margin.top + margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Date");
svg.append("rect")
.attr("class", "zoom")
.attr("width", width)
.attr("height", height)
.attr("transform", "translate(" + margin.left + "," + margin.top + ")")
.call(zoom);
var rects = context.append("g");
rects.attr("clip-path", "url(#clip)");
rects.selectAll("rects")
.data(data)
.enter().append("rect")
.style("fill", function(d) {
return "#8DA5ED";
})
.attr("class", "rects")
.attr("x", function(d) {
return x2(d.num);
})
.attr("y", function(d) {
return y2(d.distance);
})
.attr("width", function(d) {
return 10;
})
.attr("height", function(d) {
return height2 - y2(d.distance);
});
context.append("g")
.attr("class", "axis x-axis")
.attr("transform", "translate(0," + height2 + ")")
.call(xAxis2);
context.append("g")
.attr("class", "brush")
.call(brush)
.call(brush.move, x.range());
// });
function brushed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "zoom") return; // ignore brush-by-zoom
var s = d3.event.selection || x2.range();
x.domain(s.map(x2.invert, x2));
focus.selectAll(".rects")
.attr("x", function(d) {
return x(d.num);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", function(d) {
return 10;
})
.attr("height", function(d) {
return height - y(d.distance);
});
focus.select(".x-axis").call(xAxis);
svg.select(".zoom").call(zoom.transform, d3.zoomIdentity
.scale(width / (s[1] - s[0]))
.translate(-s[0], 0));
var e = d3.event.selection;
var selectedrects = focus.selectAll('.rects').filter(function() {
var xValue = this.getAttribute('x');
return e[0] <= xValue && xValue <= e[1];
});
}
function zoomed() {
if (d3.event.sourceEvent && d3.event.sourceEvent.type === "brush") return; // ignore zoom-by-brush
var t = d3.event.transform;
x.domain(t.rescaleX(x2).domain());
focus.selectAll(".rects")
.attr("x", function(d) {
return x(d.num);
})
.attr("y", function(d) {
return y(d.distance);
})
.attr("width", function(d) {
return 10;
})
.attr("height", function(d) {
return height - y(d.distance);
});
focus.select(".x-axis").call(xAxis);
context.select(".brush").call(brush.move, x.range().map(t.invertX, t));
}
</script>
PS: this is clearly a XY Problem: disguising a linear scale as an categorical scale, as you're doing here, may not be the correct solution. Instead of that, you should find out how to brush the band scale itself.

How to assign id to the bar chart

How can I assign id base on its name?
the link of the screenshot of the console is down below.
Thanks
serie.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("class","bar")
.attr("x", function(d) { return x(d.data.Company); })
.attr("y", function(d) { return y(d[1]); })
.attr("height", function(d) { return y(d[0]) - y(d[1]); })
.attr("width", x.bandwidth())
.attr("id",function(d,i){
return "id"+ d.name;
})
I discourage assigning ids to every bar as ids need to be unique. Its better to have a class instead. But as requested, I have posted a fiddle below. Hover on the bars to get the id. And you also said you need the id for animating so I've also added some animations when the chart loads (If that's what you meant by animation).
// Setup svg using Bostock's margin convention
var margin = {
top: 20,
right: 160,
bottom: 35,
left: 30
};
var width = 700 - margin.left - margin.right,
height = 300 - margin.top - margin.bottom;
var svg = d3.select("body")
.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 + ")");
/* Data in strings like it would be if imported from a csv */
var data = [{
mystate: "Intuit",
name: "Asian",
y0: 53,
y1: 1824,
value: "1771"
}, {
mystate: "Intuit",
name: "Latino",
y0: 2707,
y1: 1231,
value: "1771"
}, {
mystate: "Intuit",
name: "Black_or_African_American",
y0: 2060,
y1: 1824,
value: "1771"
}, {
mystate: "Intuit",
name: "Caucasian",
y0: 355,
y1: 1024,
value: "1771"
}];
// Transpose the data into layers
var dataset = d3.layout.stack()(['y0', 'y1'].map(function(values) {
return data.map(function(d) {
return {
x: d.name,
y: +d[values],
};
});
}));
// Set x, y and colors
var x = d3.scale.ordinal()
.domain(dataset[0].map(function(d) {
return d.x;
}))
.rangeRoundBands([10, width - 10], 0.02);
var y = d3.scale.linear()
.domain([0, d3.max(dataset, function(d) {
return d3.max(d, function(d) {
return d.y0 + d.y;
});
})])
.range([height, 0]);
var colors = ["b33040", "#d25c4d", "#f2b447", "#d9d574"];
// Define and draw axes
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
.tickSize(-width, 0, 0)
.tickFormat(function(d) {
return d
});
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Create groups for each series, rects for each segment
var groups = svg.selectAll("g.cost")
.data(dataset)
.enter().append("g")
.attr("class", "cost")
.style("fill", function(d, i) {
return colors[i];
});
var rect = groups.selectAll("rect")
.data(function(d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d) {
return x(d.x);
})
.attr("y", function(d) {
return height;
})
.attr("height", function(d) {
return 0;
})
.attr("width", x.rangeBand())
.attr('id', function(d) {
return 'id' + d.x;
})
.on("mouseover", function() {
console.log(d3.select(this).attr('id'));
tooltip.style("display", null);
})
.on("mouseout", function() {
tooltip.style("display", "none");
})
.on("mousemove", function(d) {
var xPosition = d3.mouse(this)[0] - 15;
var yPosition = d3.mouse(this)[1] - 25;
tooltip.attr("transform", "translate(" + xPosition + "," + yPosition + ")");
tooltip.select("text").text(d.y);
}).transition().duration(1000)
.delay(function(d, i) {
return i * 300;
})
.attr("height", function(d) {
return y(d.y0) - y(d.y0 + d.y);
}).attr("y", function(d) {
return y(d.y0 + d.y);
});
// Prep the tooltip bits, initial display is hidden
var tooltip = svg.append("g")
.attr("class", "tooltip")
.style("display", "none");
tooltip.append("rect")
.attr("width", 30)
.attr("height", 20)
.attr("fill", "white")
.style("opacity", 0.5);
tooltip.append("text")
.attr("x", 15)
.attr("dy", "1.2em")
.style("text-anchor", "middle")
.attr("font-size", "12px")
.attr("font-weight", "bold");
svg {
font: 10px sans-serif;
shape-rendering: crispEdges;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
}
path.domain {
stroke: none;
}
.y .tick line {
stroke: #ddd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.3.13/d3.min.js"></script>
Now coming back to your code, when you transform the data by using d3.layout.stack() there is no property called name in your new data (I suppose) and hence your ids are idundefined

D3 Chart version 4 Normalized Stacked Bar Chart from vertical to horizontal

This question is pretty such similar to this D3JS question but I am using latest D3 version(//d3js.org/d3.v4.min.js).
I am trying make this Normalized Stacked Bar Chart
chart horizontal. Is there any optimized way in latest version to achieve this?
I have swapped the x axis and y axis as below
var svg = d3.select("svg"),
margin = {top: 20, right: 60, bottom: 30, left: 40},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
g = svg.append("g").attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var y = d3.scaleBand()
.rangeRound([0, width])
.padding(0.1)
.align(0.1);
var x = d3.scaleLinear()
.rangeRound([height, 0]);
var z = d3.scaleOrdinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var stack = d3.stack()
.offset(d3.stackOffsetExpand);
d3.csv("data.csv", type, function(error, data) {
if (error) throw error;
data.sort(function(a, b) { return b[data.columns[1]] / b.total - a[data.columns[1]] / a.total; });
y.domain(data.map(function(d) { return d.State; }));
z.domain(data.columns.slice(1));
var serie = g.selectAll(".serie")
.data(stack.keys(data.columns.slice(1))(data))
.enter().append("g")
.attr("class", "serie")
.attr("fill", function(d) { return z(d.key); });
serie.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return y(d.data.State); })
.attr("x", function(d) { return x(d[1]); })
.attr("height", function(d) { return x(d[0]) - x(d[1]); })
.attr("width", y.bandwidth());
g.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(y));
g.append("g")
.attr("class", "axis axis--x")
.call(d3.axisLeft(x).ticks(10, "%"));
var legend = serie.append("g")
.attr("class", "legend")
.attr("transform", function(d) { var d = d[d.length - 1]; return "translate(" + (y(d.data.State) + y.bandwidth()) + "," + ((x(d[0]) + x(d[1])) / 2) + ")"; });
legend.append("line")
.attr("x1", -6)
.attr("x2", 6)
.attr("stroke", "#000");
legend.append("text")
.attr("y", 9)
.attr("dy", "0.35em")
.attr("fill", "#000")
.style("font", "10px sans-serif")
.text(function(d) { return d.key; });
});
function type(d, i, columns) {
for (i = 1, t = 0; i < columns.length; ++i) t += d[columns[i]] = +d[columns[i]];
d.total = t;
return d;
}
Referring the example
You will need to reverse the domains:
var y = d3.scaleBand()
.rangeRound([0, width])
.padding(0.1)
.align(0.1);
var x = d3.scaleLinear()
.rangeRound([height, 0]);
Swap the x to y as the domains are reverse so x will become y and y will become x, when you create the rectangles.
serie.selectAll("rect")
.data(function(d) {
return d;
})
.enter().append("rect")
.attr("y", function(d) {
return y(d.data.State);
})
.attr("x", function(d) {
return x(d[1]);
})
.attr("width", function(d) {
return x(d[0]) - x(d[1]);
})
.attr("height", y.bandwidth());
Change the legend position accordingly to position it on the top bar.
var legend = serie.append("g")
.attr("class", "legend")
.attr("transform", function(d) {
var d = d[0];//get the top data for placing legends on that.
return "translate(" + ((x(d[0]) + x(d[1])) / 2) + ", " +(y(d.data.State) - y.bandwidth())+ ")";
});
Finally position the legend lines:
legend.append("line")
.attr("y1", 5)
.attr("x1", 15)
.attr("x2", 15)
.attr("y2", 12)
.attr("stroke", "#000");
working code here
Below example also will help you
var initStackedBarChart = {
draw: function(config) {
me = this,
domEle = config.element,
stackKey = config.key,
data = config.data,
margin = {top: 20, right: 20, bottom: 30, left: 50},
parseDate = d3.timeParse("%m/%Y"),
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom,
xScale = d3.scaleLinear().rangeRound([0, width]),
yScale = d3.scaleBand().rangeRound([height, 0]).padding(0.1),
color = d3.scaleOrdinal(d3.schemeCategory20),
xAxis = d3.axisBottom(xScale),
yAxis = d3.axisLeft(yScale).tickFormat(d3.timeFormat("%b")),
svg = d3.select("#"+domEle).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 + ")");
var stack = d3.stack()
.keys(stackKey)
/*.order(d3.stackOrder)*/
.offset(d3.stackOffsetNone);
var layers= stack(data);
data.sort(function(a, b) { return b.total - a.total; });
yScale.domain(data.map(function(d) { return parseDate(d.date); }));
xScale.domain([0, d3.max(layers[layers.length - 1], function(d) { return d[0] + d[1]; }) ]).nice();
var layer = svg.selectAll(".layer")
.data(layers)
.enter().append("g")
.attr("class", "layer")
.style("fill", function(d, i) { return color(i); });
layer.selectAll("rect")
.data(function(d) { return d; })
.enter().append("rect")
.attr("y", function(d) { return yScale(parseDate(d.data.date)); })
.attr("x", function(d) { return xScale(d[0]); })
.attr("height", yScale.bandwidth())
.attr("width", function(d) { return xScale(d[1]) - xScale(d[0]) });
svg.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + (height+5) + ")")
.call(xAxis);
svg.append("g")
.attr("class", "axis axis--y")
.attr("transform", "translate(0,0)")
.call(yAxis);
}
}
var data = [{"date":"4/1854","total":8571,"disease":1,"wounds":0,"other":5},{"date":"5/1854","total":23333,"disease":12,"wounds":0,"other":9},{"date":"6/1854","total":28333,"disease":11,"wounds":0,"other":6},{"date":"7/1854","total":28772,"disease":359,"wounds":0,"other":23},{"date":"8/1854","total":30246,"disease":828,"wounds":1,"other":30},{"date":"9/1854","total":30290,"disease":788,"wounds":81,"other":70},{"date":"10/1854","total":30643,"disease":503,"wounds":132,"other":128},{"date":"11/1854","total":29736,"disease":844,"wounds":287,"other":106},{"date":"12/1854","total":32779,"disease":1725,"wounds":114,"other":131},{"date":"1/1855","total":32393,"disease":2761,"wounds":83,"other":324},{"date":"2/1855","total":30919,"disease":2120,"wounds":42,"other":361},{"date":"3/1855","total":30107,"disease":1205,"wounds":32,"other":172},{"date":"4/1855","total":32252,"disease":477,"wounds":48,"other":57},{"date":"5/1855","total":35473,"disease":508,"wounds":49,"other":37},{"date":"6/1855","total":38863,"disease":802,"wounds":209,"other":31},{"date":"7/1855","total":42647,"disease":382,"wounds":134,"other":33},{"date":"8/1855","total":44614,"disease":483,"wounds":164,"other":25},{"date":"9/1855","total":47751,"disease":189,"wounds":276,"other":20},{"date":"10/1855","total":46852,"disease":128,"wounds":53,"other":18},{"date":"11/1855","total":37853,"disease":178,"wounds":33,"other":32},{"date":"12/1855","total":43217,"disease":91,"wounds":18,"other":28},{"date":"1/1856","total":44212,"disease":42,"wounds":2,"other":48},{"date":"2/1856","total":43485,"disease":24,"wounds":0,"other":19},{"date":"3/1856","total":46140,"disease":15,"wounds":0,"other":35}];
var key = ["wounds", "other", "disease"];
initStackedBarChart.draw({
data: data,
key: key,
element: 'stacked-bar'
});
.axis text {
font: 10px sans-serif;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.path-line {
fill: none;
stroke: yellow;
stroke-width: 1.5px;
}
svg {
background: #f0f0f0;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
<div id='stacked-bar'></div>

d3 brushing and mouse move coexist

im trying to update http://bl.ocks.org/d3noob/6eb506b129f585ce5c8a and add brushing into it(brushing displayed under the line graph) to make it look like https://www.google.com.hk/#q=s%26p+500
added coded to the first link:
var brush = d3.svg.brush()
.x(x)
.on("brush", brushmove)
.on("brushend", brushend);
svg.append("g")
.attr("class", "brush")
.call(brush)
.selectAll('rect')
.attr('height', height);
function brushmove() {
var extent = brush.extent();
}
function brushend() {
x.domain(brush.extent())
console.log(brush.extent());
}
The problem is that once i add brushing into it, there's a background formed behind the graph and i can't perform mouse events(mousemove) anymore.
Is there a way to fix it to make it look like google?
1) brushing and mouse event coexist
2) brushing area under the curve
var csv = date,close1, close2
26-Mar-12,606.98,58.13
27-Mar-12,614.48,53.98
28-Mar-12,617.62,67.00
29-Mar-12,609.86,89.70
30-Mar-12,599.55,99.00
2-Apr-12,618.63,130.28
3-Apr-12,629.32,166.70
4-Apr-12,624.31,234.98
5-Apr-12,633.68,345.44
9-Apr-12,636.23,443.34
10-Apr-12,628.44,543.70
11-Apr-12,626.20,580.13
12-Apr-12,622.77,605.23
13-Apr-12,605.23,626.20
16-Apr-12,580.13,628.44
17-Apr-12,543.70,636.23
18-Apr-12,443.34,633.68
19-Apr-12,345.44,624.31
20-Apr-12,234.98,629.32
23-Apr-12,166.70,618.63
24-Apr-12,130.28,599.55
25-Apr-12,99.00,609.86
26-Apr-12,89.70,617.62
27-Apr-12,67.00,614.48
30-Apr-12,53.98,606.98
1-May-12,58.13,503.15
Both the example you link to and the brush add a rect on top of the plot to capture mouse events. The key to making them coexist is to add the brush (and allow it to create its rect) and then use that rect to add the tooltip events. This way you only end up with one point-events rect:
// add a g for the brush
var context = svg.append("g");
// add the brush
context.call(brush);
// grab the brush's rect and add the tooltip events
context.select(".background")
.on("mouseover", function() {
focus.style("display", null);
})
.on("mouseout", function() {
focus.style("display", "none");
})
.on("mousemove", mousemove);
Full code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
/* set the CSS */
body {
font: 12px Arial;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.extent {
stroke: #fff;
fill-opacity: .125;
shape-rendering: crispEdges;
}
</style>
<body>
<!-- load the d3.js library -->
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
// Set the dimensions of the canvas / graph
var margin = {
top: 30,
right: 20,
bottom: 30,
left: 50
},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
// Parse the date / time
var parseDate = d3.time.format("%d-%b-%y").parse,
formatDate = d3.time.format("%d-%b"),
bisectDate = d3.bisector(function(d) {
return d.date;
}).left;
// Set the ranges
var x = d3.time.scale().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
// Define the axes
var xAxis = d3.svg.axis().scale(x)
.orient("bottom").ticks(5);
var yAxis = d3.svg.axis().scale(y)
.orient("left").ticks(5);
// Define the line
var valueline = d3.svg.line()
.x(function(d) {
return x(d.date);
})
.y(function(d) {
return y(d.close);
});
var area = d3.svg.area()
.x(function(d) {
return x(d.date);
})
.y0(height)
.y1(function(d) {
return y(d.close);
});
// Adds the svg canvas
var svg = d3.select("body")
.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 + ")");
var defs = svg.append("defs");
var areaClip = defs.append("clipPath")
.attr("id", "areaClip")
.append("rect")
.attr("x", width)
.attr("y", 0)
.attr("width", width)
.attr("height", height);
var lineSvg = svg.append("g");
var focus = svg.append("g")
.style("display", "none");
var brush = d3.svg.brush()
.x(x)
.on("brush", function() {
var s = brush.extent(),
x1 = x(s[0]),
x2 = x(s[1]);
areaClip.attr('x', x1);
areaClip.attr('width', x2 - x1);
})
var csv = `date,close
26-Mar-12,606.98
27-Mar-12,614.48
28-Mar-12,617.62
29-Mar-12,609.86
30-Mar-12,599.55
2-Apr-12,618.63
3-Apr-12,629.32
4-Apr-12,624.31
5-Apr-12,633.68
9-Apr-12,636.23
10-Apr-12,628.44
11-Apr-12,626.20
12-Apr-12,622.77
13-Apr-12,605.23
16-Apr-12,580.13
17-Apr-12,543.70
18-Apr-12,443.34
19-Apr-12,345.44
20-Apr-12,234.98
23-Apr-12,166.70
24-Apr-12,130.28
25-Apr-12,99.00
26-Apr-12,89.70
27-Apr-12,67.00
30-Apr-12,53.98
1-May-12,58.13`;
var data = d3.csv.parse(csv);
data.forEach(function(d) {
d.date = parseDate(d.date);
d.close = +d.close;
});
// Scale the range of the data
x.domain(d3.extent(data, function(d) {
return d.date;
}));
y.domain([0, d3.max(data, function(d) {
return d.close+20;
})]);
// Add the valueline path.
lineSvg.append("path")
.attr("class", "line")
.attr("d", valueline(data));
lineSvg.append("path")
.attr("d", area(data))
.style("fill", "steelblue")
.style("stroke", "none")
.style("opacity", "0.5")
.attr("clip-path", "url(#areaClip)")
// Add the X Axis
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// Add the Y Axis
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
// append the x line
focus.append("line")
.attr("class", "x")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("y1", 0)
.attr("y2", height);
// append the y line
focus.append("line")
.attr("class", "y")
.style("stroke", "blue")
.style("stroke-dasharray", "3,3")
.style("opacity", 0.5)
.attr("x1", width)
.attr("x2", width);
// append the circle at the intersection
focus.append("circle")
.attr("class", "y")
.style("fill", "none")
.style("stroke", "blue")
.attr("r", 4);
// place the value at the intersection
focus.append("text")
.attr("class", "y1")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "-.3em");
focus.append("text")
.attr("class", "y2")
.attr("dx", 8)
.attr("dy", "-.3em");
// place the date at the intersection
focus.append("text")
.attr("class", "y3")
.style("stroke", "white")
.style("stroke-width", "3.5px")
.style("opacity", 0.8)
.attr("dx", 8)
.attr("dy", "1em");
focus.append("text")
.attr("class", "y4")
.attr("dx", 8)
.attr("dy", "1em");
// append the rectangle to capture mouse
var context = svg.append("g");
context.call(brush);
context.selectAll(".resize").append("path")
.attr("d", "M0,2V" + (height - 2))
.style("stroke", "black")
context.select(".extent")
.attr("height", height - 2)
.attr("fill", "none");
context.select(".background")
.attr("height", height)
.on("mouseover.tooltip", function() {
focus.style("display", null);
})
.on("mouseout.tooltip", function() {
focus.style("display", "none");
})
.on("mousemove.tooltip", mousemove);
function mousemove() {
var x0 = x.invert(d3.mouse(this)[0]),
i = bisectDate(data, x0, 1),
d0 = data[i - 1],
d1 = data[i],
d = x0 - d0.date > d1.date - x0 ? d1 : d0;
focus.select("circle.y")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")");
focus.select("text.y1")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.close);
focus.select("text.y2")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(d.close);
focus.select("text.y3")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(formatDate(d.date));
focus.select("text.y4")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.text(formatDate(d.date));
focus.select(".x")
.attr("transform",
"translate(" + x(d.date) + "," +
y(d.close) + ")")
.attr("y2", height - y(d.close));
focus.select(".y")
.attr("transform",
"translate(" + width * -1 + "," +
y(d.close) + ")")
.attr("x2", width + width);
}
</script>
</body>

Resources