Adding tooltip in dynamic line chart in d3.js - d3.js

Can any one help me to add tool trip in line chart in d3.js
Here is what I have so far:
My Javascript:
var n = 40;
random = d3.random.normal(0, .2),
data = [-2.0236589]; // d3.range(n).map(random);
//alert(data);
var margin = {
top: 20,
right: 20,
bottom: 20,
left: 40
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.linear()
.domain([1, n - 2])
.range([0, width]);
var y = d3.scale.linear()
.domain([-1, 1])
.range([height, 0]);
var line = d3.svg.line()
.interpolate("basis")
.x(function (d, i) {
return x(i);
})
.y(function (d, i) {
return y(d);
});
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 + ")");
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + y(0) + ")")
.call(d3.svg.axis().scale(x).orient("bottom"));
svg.append("g")
.attr("class", "y axis")
.call(d3.svg.axis().scale(y).orient("left"));
var path = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
tick();
function tick() {
// alert(data);
// random();
// console.log(data);
// push a new data point onto the back
data.push(random());
//random();
// redraw the line, and slide it to the left
path.attr("d", line)
// .attr("d", valueline(data))
.attr("transform", null)
.transition()
.duration(500)
.ease("linear")
.attr("transform", function (data) {
return data.length > 40 ? "translate(" + x(0) + ",0)" : "translate(0)"
})
.each("end", tick);
// pop the old data point off the front
//alert(data.length);
if (data.length > 40) {
//alert(data.length);
data.shift();
}
}
CSS:
svg {
font: 10px sans-serif;
}
.line {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
JSFiddle link: http://jsfiddle.net/RajuSahoo/ug6v86cc/1/

Related

Wrapping long labels with axis transition in D3.js v4

This is the original example: https://bl.ocks.org/mbostock/7555321.
I added transition to the x-axis and made two fiddles to demonstrate the fact that wrapping long labels on an axis with transition works in version 3 but doesn't work in version 4 of D3.
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.transition()
.call(xAxis)
.selectAll(".tick text")
// .call(wrap, x.rangeBand()); (v3)
.call(wrap, x.bandwidth());
https://jsfiddle.net/cfwbh0st/
https://jsfiddle.net/Ls5ux9gv/
How do I go about this? Thanks.
I was in the middle of rewriting your logic when I found out the root cause of your problem. By calling .transition(), you change the object from a selection to a transition object. But transition objects don't have a .html() method. You didn't recognise that because you use .call(). That is executed before the transition has completed, however, and thus the ticks are overwritten with the default behaviour. I'll pose two solutions below. The first is an adaptation of your code, the second is the code I was working on. Hopefully it will give you some more pointers.
As close to your code as possible
var margin = {top: 80, right: 180, bottom: 80, left: 180},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(.1)
.paddingOuter(.3);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x);
var yAxis = d3.axisLeft()
.scale(y)
.ticks(8, "%");
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 + ")");
d3.tsv("https://gist.githubusercontent.com/mbostock/7555321/raw/1b17baa35f75336a8d9f4c8292b1d1ab850f3d86/data.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
svg.append("text")
.attr("class", "title")
.attr("x", x(data[0].name))
.attr("y", -26)
.text("Why Are We Leaving Facebook?");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.transition()
.call(xAxis)
.on("end", function() {
d3.select(this)
.selectAll(".tick text")
.call(wrap, x.bandwidth())
});
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.name); })
.attr("width", x.bandwidth())
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); });
});
function wrap(textElements, width) {
textElements.each(function() {
var text = d3.select(this),
words = text.text().split(/\s+/).reverse(),
word,
line = [],
lineNumber = 0,
lineHeight = 1.1, // ems
y = text.attr("y"),
dy = parseFloat(text.attr("dy")),
tspan = text.text(null).append("tspan").attr("x", 0).attr("y", y).attr("dy", dy + "em");
while (word = words.pop()) {
line.push(word);
tspan.text(line.join(" "));
if (tspan.node().getComputedTextLength() > width) {
line.pop();
tspan.text(line.join(" "));
line = [word];
tspan = text.append("tspan").attr("x", 0).attr("y", y).attr("dy", ++lineNumber * lineHeight + dy + "em").text(word);
}
}
});
}
function type(d) {
d.value = +d.value;
return d;
}
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.title {
font: bold 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>
var margin = {
top: 80,
right: 180,
bottom: 80,
left: 180
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scaleBand()
.rangeRound([0, width])
.paddingInner(.1)
.paddingOuter(.3);
var y = d3.scaleLinear()
.range([height, 0]);
var xAxis = d3.axisBottom()
.scale(x);
var yAxis = d3.axisLeft()
.scale(y)
.ticks(8, "%");
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 + ")");
d3.tsv("https://gist.githubusercontent.com/mbostock/7555321/raw/1b17baa35f75336a8d9f4c8292b1d1ab850f3d86/data.tsv", type, function(error, data) {
x.domain(data.map(function(d) {
return d.name;
}));
y.domain([0, d3.max(data, function(d) {
return d.value;
})]);
svg.append("text")
.attr("class", "title")
.attr("x", x(data[0].name))
.attr("y", -26)
.text("Why Are We Leaving Facebook?");
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.transition()
.call(xAxis)
.on("end", function() {
d3.select(this).selectAll(".tick text")
.call(wrap, x.bandwidth())
});
svg.append("g")
.attr("class", "y axis")
.call(yAxis);
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) {
return x(d.name);
})
.attr("width", x.bandwidth())
.attr("y", function(d) {
return y(d.value);
})
.attr("height", function(d) {
return height - y(d.value);
});
});
function wrap(textElements, width) {
textElements.each(function() {
d3.select(this).html(function(text) {
var words = text.split(/\s+/),
word,
lines = [],
currentLine = '',
lineNumber = 0,
lineHeight = 1.1
while (word = words.shift()) {
if (getTextWidth(currentLine + word, "10px sans-serif") < width) {
// We're safe to add the word
currentLine += word + ' ';
} else {
// If we add the word, we exceed the line length
// Trim to remove the last space
lines.push('<tspan x="0" y="9" dy="' + (++lineNumber * lineHeight) + 'em">' + currentLine.trim() + '</tspan>')
currentLine = word + ' ';
}
}
lines.push('<tspan x="0" y="9" dy="' + (++lineNumber * lineHeight) + 'em">' + currentLine.trim() + '</tspan>')
return lines.join('');
});
});
}
/**
* Measure the width of a text were it to be rendered using a given font.
*
* #param {string} text the text to be measured
* #param {string} font a valid css font value
*
* #returns {number} the width of the rendered text in pixels.
*/
function getTextWidth(text, font) {
const element = document.createElement("canvas");
const context = element.getContext("2d");
context.font = font;
return context.measureText(text).width;
};
function type(d) {
d.value = +d.value;
return d;
}
.bar {
fill: steelblue;
}
.bar:hover {
fill: brown;
}
.title {
font: bold 14px "Helvetica Neue", Helvetica, Arial, sans-serif;
}
.axis {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.13.0/d3.min.js"></script>

Finding the mean of bars in bar chart - d3

I am new to d3 and am trying to draw mean line through all the bars in the bar chart but not quite able to achieve that. Below is my code:
<!DOCTYPE html>
<html>
<head>
<meta charset=utf-8 />
<title>JS Bin</title>
<style>
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.bar {
fill: steelblue;
}
.x.axis path {
display: none;
}
.line {
fill: none;
stroke: #444;
stroke-width: 1.5px;
}
</style>
</head>
<body>
<script src="//cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script>
var data=[
{"letter": "BU", "higher": .08,"lower": .05},
{"letter": "AU", "higher": .05,"lower": .03},
{"letter": "LA", "higher": .04,"lower": .02}
]
var margin = {top: 20, right: 20, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var formatPercent = d3.format(".0%");
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var x2 = d3.scale.ordinal()
.rangeBands([0, width], 0);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.tickFormat(formatPercent);
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.forEach(function(d) {
d.higher = +d.higher;
d.lower = +d.lower;
});
x.domain(data.map(function(d) { return d.letter; }));
x2.domain(data.map(function(d) { return d.letter; }));
y.domain([0, d3.max(data, function(d) { return d.higher; })]);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("Frequency");
svg.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter); })
.attr("width", x.rangeBand())
.attr("y", function(d) { return y(d.higher); })
.attr("height", function(d) { return height - y(d.higher-d.lower); });
var dataSum = d3.mean(data, function(d) { return (d.higher + d.lower); });
var line = d3.svg.line()
.x(function(d, i) {
return x(d.letter) + i; })
.y(function(d, i) { return y(dataSum/data.length); });
svg.append("path")
.datum(data)
.attr("class", "line")
.attr("d", line);
</script>
</body>
</html>
The proper code is here'http://jsbin.com/beyakumohi/1/edit?html' . The line should pass through center of each bars but it only happens for the 3rd bar and that too it do not cross it. Is there any mistake in my code.
d3.mean will give you the mean value of the full array. Meaning, that in this case you will get the mean of:
data[0].higher + data[0].lower + data[1].higher + data[1].lower + data[2].higher + data[2].lower
In this case I would say it is more appropiate to edit your line function as following
var line = d3.svg.line()
.x(function(d, i) {
return x(d.letter); })
.y(function(d, i) { return y((d.higher + d.lower) / 2); });

d3 rangeRoundBands iternal calculation

I'm trying to add some padding between my bars, but can't control,say understand, it accurately.
Here's the code snippet I'm following from here:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.bar {
fill: steelblue;
}
.axis text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
</style>
<svg class="chart"></svg>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 20, right: 30, bottom: 30, left: 40},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal()
.rangeRoundBands([0, width], .1);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var chart = d3.select(".chart")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.tsv("data.tsv", type, function(error, data) {
x.domain(data.map(function(d) { return d.name; }));
y.domain([0, d3.max(data, function(d) { return d.value; })]);
chart.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
chart.append("g")
.attr("class", "y axis")
.call(yAxis);
chart.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.name); })
.attr("y", function(d) { return y(d.value); })
.attr("height", function(d) { return height - y(d.value); })
.attr("width", x.rangeBand());
});
function type(d) {
d.value = +d.value; // coerce to number
return d;
}
</script>
Data.dsv file content:
name value
A 12
B 13
C 22
D 5
Then, we you load it and query from developer's console, you know
> x.range()
[24, 258, 492, 726]
> x.rangeBand()
211
, but I wondered how these numbers be determined, the internal calcuation.Any help would be much appreciated!

Color d3 area based on computed value

I've created a line graph with d3.nest() objects, but I need the fill color for each segment to be based on another variable. I thought I could just do it based on the gradient from the data file, but it actually needs to be computed over the distance of the segment. Right now, everything is coming back as one color when it should be
if gradient < -10
color = red
if gradient < -5
color = green
if gradient < 0
color = white
if gradient < 5
color = yellow
if gradient < 10
color = black
else
color = blue
Here's a Plunk
<!DOCTYPE html>
<meta charset="utf-8">
<style>
body {
font: 12px Arial;
}
text.shadow {
stroke: #fff;
stroke-width: 2.5px;
opacity: 0.9;
}
path {
stroke: steelblue;
stroke-width: 2;
fill: none;
}
.axis path,
.axis line {
fill: none;
stroke: grey;
stroke-width: 1;
shape-rendering: crispEdges;
}
.grid .tick {
stroke: lightgrey;
stroke-opacity: 0.7;
shape-rendering: crispEdges;
}
.grid path {
stroke-width: 0;
}
.area {
stroke-width: 0;
}
</style>
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 20, bottom: 35, left: 50},
width = 600 - margin.left - margin.right,
height = 270 - margin.top - margin.bottom;
var color = d3.scale.ordinal()
.domain([-10,-5,0,5,10])
.range(['red','green','white','yellow','black','blue']);
var x = d3.scale.linear().range([0, width]);
var y = d3.scale.linear().range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5);
var yAxis = d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5);
var area = d3.svg.area()
.x(function(d) { return x(d.distance); })
.y0(height)
.y1(function(d) { return y(d.elevation); });
var valueline = d3.svg.line()
.x(function(d) { return x(d.distance); })
.y(function(d) { return y(d.elevation); });
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 + ")");
// function for the x grid lines
function make_x_axis() {
return d3.svg.axis()
.scale(x)
.orient("bottom")
.ticks(5)
}
// function for the y grid lines
function make_y_axis() {
return d3.svg.axis()
.scale(y)
.orient("left")
.ticks(5)
}
// Get the data
d3.csv("data.csv", function(error, data) {
data.forEach(function(d) {
d.distance = +d.distance;
d.elevation = +d.elevation;
d.gradient = +d.gradient;
});
var dataGroup = d3.nest()
.key(function(d) {
return d.grade;
})
.entries(data);
dataGroup.forEach(function(group, i) {
if(i < dataGroup.length - 1) {
group.values.push(dataGroup[i+1].values[0])
}
})
// Scale the range of the data
x.domain(d3.extent(data, function(d) { return d.distance; }));
y.domain([0, d3.max(data, function(d) { return d.elevation; })]);
dataGroup.forEach(function(d, i){
svg.append("path")
.datum(d.values)
.attr("class", "area")
.attr("d", area);
});
svg.selectAll(".area")
.style("fill", function(d) { return color(d.gradient); });
// Draw the x Grid lines
svg.append("g")
.attr("class", "grid")
.attr("transform", "translate(0," + height + ")")
.call(make_x_axis()
.tickSize(-height, 0, 0)
.tickFormat("")
)
// Draw the y Grid lines
svg.append("g")
.attr("class", "grid")
.call(make_y_axis()
.tickSize(-width, 0, 0)
.tickFormat("")
)
// Add the valueline path.
svg.append("path")
.attr("d", valueline(data));
// 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);
// Add the text label for the X axis
svg.append("text")
.attr("transform",
"translate(" + (width/2) + " ," +
(height+margin.bottom) + ")")
.style("text-anchor", "middle")
.text("Distance");
// Add the text label for the Y axis
svg.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("x", margin.top - (height / 2))
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("");
// Add the title
svg.append("text")
.attr("x", (width / 2))
.attr("y", 0 - (margin.top / 2))
.attr("text-anchor", "middle")
.style("font-size", "16px")
.style("text-decoration", "underline")
.text("Elevation Graph");
});
</script>
</body>
The issue is with this call:
svg.selectAll(".area")
.style("fill", function(d) { return color(d.gradient); });
Here, "d" is an array of objects of length one or two depending on the index. I don't know which object's gradient you wish to use, but my naive solution without understanding your data would be to use d[0]. Here's the Plunk.

Difference chart in D3 js doesn't show the graph properly

Difference chart in D3 js does not show the graph properly.
I have the below data in the tsv file.
for column C there is a value 50 but however the graph stops at approx 30+ on Y axis.
May I know how can I get the graph to be plotted properly.
date A C
20140108 1
20140110 2
20140116 1 3
20140117 1 1
20140120 20 38
20140127 0 1
20140128 0 50
20140130 1 2
20140203 25 2
20140210 3 5
20140211 40 0
20140212 1
20140218 4
Code:
body {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.x.axis path {
display: none;
}
.area.above {
fill: rgb(252,141,89); //for red
}
.area.below {
fill: rgb(145,207,96); //for green
}
.line {
fill: none;
stroke: #000;
stroke-width: 1.5px;
}
</style>
<body>
<!script type="text/javascript" src="d3/d3.v3.min.js"><!/script>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
var margin = {top: 30, right: 30, bottom: 50, left: 50},
width = 960 - margin.left - margin.right,
height = 800 - margin.top - margin.bottom;
var parseDate = d3.time.format("%Y%m%d").parse;
var x = d3.time.scale()
.range([0, width]);
var y = d3.scale.linear()
.range([height, 0]);
var xAxis = d3.svg.axis()
.scale(x)
.orient("bottom");
var yAxis = d3.svg.axis()
.scale(y)
.orient("left");
var line = d3.svg.area()
.interpolate("basis")
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d["A"]); });
var area = d3.svg.area()
.interpolate("basis")
.x(function(d) { return x(d.date); })
.y1(function(d) { return y(d["A"]); });
var svg = d3.select("body").append("svg") //main svg object which does everything
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
d3.tsv("data.tsv", function(error, data) {
data.forEach(function(d) {
d.date = parseDate(d.date);
d["A"]= +d["A"]; //data here should be in sync with columns in the input file
d["C"] = +d["C"];
});
x.domain(d3.extent(data, function(d) { return d.date; }));
y.domain([
d3.min(data, function(d) { return Math.min(d["A"], d["C"]); }),
d3.max(data, function(d) { return Math.max(d["A"], d["C"]); })
]);
//svg block starts from here
svg.datum(data); //get or set data for individual elements, without computing a join
svg.append("clipPath")
.attr("id", "clip-below")
.append("path")
.attr("d", area.y0(height));
svg.append("clipPath")
.attr("id", "clip-above")
.append("path")
.attr("d", area.y0(0));
svg.append("path")
.attr("class", "area above")
.attr("clip-path", "url(#clip-above)")
.attr("d", area.y0(function(d) { return y(d["C"]); }));
svg.append("path")
.attr("class", "area below")
.attr("clip-path", "url(#clip-below)")
.attr("d", area);
svg.append("path")
.attr("class", "line")
.attr("d", line);
svg.append("g")
.attr("class", "x axis")
.attr("transform", "translate(0," + height + ")")
.call(xAxis);
// .append ("text")
// .text("New York");
svg.append("g")
.attr("class", "y axis")
.call(yAxis)
.append("text")
.attr("transform", "rotate(-90)")
.attr("y", 6)
.attr("dy", ".71em")
.style("text-anchor", "end")
.text("count");
});
</script>

Resources