how to add tooltip for donut chart in loop using d3js - javascript

I have json object which generate dynamically,look like
var data =
[
{"hour":"16","percentage":50,"activity":[{"activity_id":"1","cnt":"1"} {"activity_id":"2","cnt":"1"}]},
{"hour":17,"percentage":0,"activity":[]}
{"hour":"18","percentage":20,"activity":[{"activity_id":"1","cnt":"1"} {"activity_id":"2","cnt":"5"}]},
{"hour":"19","percentage":80,"activity":[{"activity_id":"1","cnt":"5"} {"activity_id":"3","cnt":"7"}]},
];
and i want to draw chart with the help of d3
var can = d3.select("#chart").append("svg").attr("height",200).attr("width",800);
var r =100;
var p = Math.PI*2;
//give color to arc
//if 0 range to yellow and 100 is red
var color = d3.scale.linear()
.domain([0,100])
.range(["#D6EBFD","#FF0000"]);
var group = can.append("g")
.attr("transform","translate(100,100)");
//draw arc with outer and inner radius
var arc = d3.svg.arc()
.innerRadius(r - 30)
.outerRadius(r)
var pie = d3.layout.pie()
.sort(null)
.value(function (d){return data.length;});
var arcs = group.selectAll(".arc")
.data(pie(data))
.enter().append("g")
.attr("class", "arc")
.attr('stroke','#fff')
.attr('stroke-width', '2')
.attr('fill',function(d){return color(d.data.percentage)})
.on("mouseover", function(d){
div.style("display", "inline")
//.text(d.data.percentage + ", " + d.data.hour)
.data(d.data.activity)
//problem is here to make tooltip when mouseover to the chart where i want data from activity array object?
.text(function(a){
a.activity_id + ", " + a.cnt
})
.text(function(d){
for(var i = 0;i>data.activity.length;i++){
return data.activity['activity_id'] + ", " + data.activity['cnt'];
}
})
.style("left", (d3.event.pageX - 34) + "px")
.style("top", (d3.event.pageY - 12) + "px");
})
.on("mouseout", mouseout);
arcs.append("path")
.attr("d", arc)
.style("fill", function (d) {
return color(d.data.percentage);
});
arcs.append("text")
.attr('transform',function(d){return "translate("+arc.centroid(d)+")";})
.attr('fill','#0000FF')
.attr('z-index',1)
.text(function(d){return d.data.hour});
var div = d3.select("#chart").append("div")
.attr("class", "tooltip")
.style("display", "none");
function mouseout() {
div.style("display", "none");
}
which draw a donut chart but i want to make tooltip when mouseover to the chart which is activity_id,cnt in loop.
(Please ignore the design)
What i need is when mouseover to
16 tooltip must be 1,1
17 tooltip must be
18 tooltip must be 1,1
2,5
19 tooltip must be 1,5
3,7
This is my first time to d3,so please can anyone help me.Thanks in advance.

Instead of doing it like this having 2 text function which is wrong:
.text(function(a){
a.activity_id + ", " + a.cnt
})
.text(function(d){
for(var i = 0;i>data.activity.length;i++){
return data.activity['activity_id'] + ", " + data.activity['cnt'];
}
})
write a single text function like this:
.text(function() {
var str = "";
d.data.activity.forEach(function(a){
//make the display string by iterating over activity.
str += a.activity_id + ", " + a.cnt + " ";
})
return str;
})
Working code here

Related

Use mouseover in pie chart and show label in d3 v3 js

Hi I am new in d3js so I am unable to use mouseover event in given code of pie chart...i have a with id named chart so how can I create some class that mouseover event and show a label?
Here is the code that I am using to draw pie chart:
var w = 300;
var h = 300;
var dataset = [
{"year":"2017-07-01","value":"5"},
{"year":"2017-07-02","value":"10"},
{"year":"2017-07-03","value":"15"},
{"year":"2017-07-04","value":"20"},
{"year":"2017-07-05","value":"25"},
{"year":"2017-07-06","value":"30"},
{"year":"2017-07-07","value":"35"},
{"year":"2017-07-08","value":"40"},
{"year":"2017-07-09","value":"45"},
{"year":"2017-07-10","value":"50"},
{"year":"2017-07-11","value":"55"},
{"year":"2017-07-12","value":"60"},
{"year":"2017-07-13","value":"65"},
{"year":"2017-07-14","value":"70"}
];
var outerRadius = w / 2;
var innerRadius = 0;
var arc = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius);
var pie = d3.layout.pie()
.value(function(d) {
return d.value;
});
var color = d3.scale.category20();
var svg = d3.select("#chart")
.append("svg")
.attr("width", w)
.attr("height", h);
var arcs = svg.selectAll("g.arc")
.data(pie(dataset))
.enter()
.append("g")
.attr("class", "arc")
.attr("transform", "translate(" + outerRadius + "," + outerRadius + ")");
arcs.append("path")
.attr("fill", function(d, i) {
return color(i);
})
.attr("d", arc);
arcs.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("text-anchor", "middle")
.text(function(d) {
return d.value;
});
I use a tooltip:
var popup=d3.select("body").append("div").attr("class","tooltip").style("opacity",0);
Then to call the tooltip, add an event listener to the nodes (I guess it would be arcs for you, but I haven't done pie charts):
nodes.on("mouseover", fade(.1,"over")).on("mouseout",fade(.8,"out"));
Then the function to put the tooltip near the node (or pie in this case):
function fade (opacity, event){
return function (d){
if(event === "over"){
popup.transition().duration(100).style("opacity", .9).style("display", "inline-block");
popup.html("Year: " + d.year + "</br> Value: " + d.value)
.style("left", (d3.event.pageX + 20) + "px")
.style("top", (d3.event.pageY - 20) + "px");
d3.select(this).classed("node-mouseover", true);}
else if(event==="out"){
popup.transition().duration(100).style("opacity",0);
d3.select(this).classed("node-mouseover",false);
}}}
There are other ways of doing it, but this seems to be pretty popular this example is similar.
Edit: check out bl.ocks.org for more examples.

Adding variables to JSON object - D3.js live donut chart

I am making a donut chart with live cryptocurrency values I fetch via an ajax from an API. I can manage to combine the values I get from a number of calls into one json and then plot a D3 donut with those, but I have to also figure out a way to add a field into the json for each currency, which will have the amount of coins the users currently has. The point is to be able to show the user how many coins he has in value.
It would be best if the coin amounts the user has would be stored in another json, but I am so desperate anything would do really.
The difficulty for me is that D3 itself iterates over the json and plots the chart, making it hard to multiply particular values in the json with certain other numbers (the way you would multiply a circle radius by 2 to make the circles all bigger)
I will hence attach my source code. Hopefully everything is straightforward. You can find the chart here: http://codepen.io/kvyb/pen/EZGyJL?editors=0110
var a = ["https://api.cryptonator.com/api/ticker/btc-usd", "https://api.cryptonator.com/api/ticker/ltc-usd", "https://api.cryptonator.com/api/ticker/eth-usd", "https://api.cryptonator.com/api/ticker/etc-usd", "https://api.cryptonator.com/api/ticker/xmr-usd", "https://api.cryptonator.com/api/ticker/icn-usd", "https://api.cryptonator.com/api/ticker/dash-usd", "https://api.cryptonator.com/api/ticker/MAID-usd", "https://api.cryptonator.com/api/ticker/omni-usd", "https://api.cryptonator.com/api/ticker/rep-usd"];
function fetch() {
all_data = ["{\"floor\": ["];
for (index = 0; index < a.length; ++index) {
$.ajax({
url: a[index],
dataType: "json",
async: false,
error: function(data) {
console.log("--");
},
success: function(data) {
all_data += JSON.stringify(data["ticker"]) + ',';
}
})
}
};
fetch()
all_data += "]}";
var pos = all_data.lastIndexOf(',');
all_data = all_data.substring(0, pos) + "" + all_data.substring(pos + 1);
console.log(all_data)
// CHART
var div = d3.select("body").append("div")
.attr("class", "tooltip")
.style("opacity", 0);
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var color = d3.scaleOrdinal()
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56", "#d0743c", "#ff8c00"]);
var arc = d3.arc()
.outerRadius(radius - 10)
.innerRadius(radius - 150);
var pie = d3.pie()
.sort(null)
.value(function(d) {
return +d.price + 50;
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
data = JSON.parse(all_data);
console.log(data);
var g = svg.selectAll(".arc")
.data(pie(data.floor))
.enter().append("g")
.attr("class", "arc")
.on("mouseover", function(d) {
div.transition()
.duration(0)
.style("opacity", .9);
div .html("Price: <br>" + d.data.price + "<br/>Volume: " + d.data.volume)
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function(d) {
div.transition()
.duration(0)
.style("opacity", 0);
})
g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color(d.data.price);
});
g.append("text")
.attr("transform", function(d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", "0em")
.attr("dx", "0em")
.html(function(d) {
return d.data.base;
});
function type(d) {
d.price = +d.price;
return d;
}
How would you go about showing the number of certain coins a user has on hover?
Another approach, instead of adding values to json, would be to get all prices from the "all_data" variable, but if I try to iterate over the json I get "undefined" returned. I thought of maybe creating another json with only the prices from "all_data" and use that to find the portfolio number. Any help would be greatly appreciated.

How to statically position elements in D3

I have currently have a line graph that looks like this:
on jsfiddle http://jsfiddle.net/vertaire/kttndjgc/1/
I've been trying to manually position the values on the graph so they get get printed next to the legend looking something like this:
Unintentional Injuries: 1980, 388437
I tried to set the positions manually, but it seems when I try and adjust to positioning, that positioning is relative to the that of the circle on the line like this:
How can I set the coordinates so that the values appear next to the legend?
Here is the code snippet for printing the values:
var mouseCircle = causation.append("g") // for each line, add group to hold text and circle
.attr("class","mouseCircle");
mouseCircle.append("circle") // add a circle to follow along path
.attr("r", 7)
.style("stroke", function(d) { console.log(d); return color(d.key); })
.style("fill", function(d) { console.log(d); return color(d.key); })
.style("stroke-width", "1px");
mouseCircle.append("text")
.attr("transform", "translate(10,3)"); // text to hold coordinates
.on('mousemove', function() { // mouse moving over canvas
if(!frozen) {
d3.select(".mouseLine")
.attr("d", function(){
yRange = y.range(); // range of y axis
var xCoor = d3.mouse(this)[0]; // mouse position in x
var xDate = x.invert(xCoor); // date corresponding to mouse x
d3.selectAll('.mouseCircle') // for each circle group
.each(function(d,i){
var rightIdx = bisect(data[1].values, xDate); // find date in data that right off mouse
yVal = data[i].values[rightIdx-1].VALUE;
yCoor = y(yVal);
var interSect = get_line_intersection(xCoor, // get the intersection of our vertical line and the data line
yRange[0],
xCoor,
yRange[1],
x(data[i].values[rightIdx-1].YEAR),
y(data[i].values[rightIdx-1].VALUE),
x(data[i].values[rightIdx].YEAR),
y(data[i].values[rightIdx].VALUE));
d3.select(this) // move the circle to intersection
.attr('transform', 'translate(' + interSect.x + ',' + interSect.y + ')');
d3.select(this.children[1]) // write coordinates out
.text(xDate.getFullYear() + "," + yVal);
yearCurrent = xDate.getFullYear();
console.log(yearCurrent)
return yearCurrent;
});
return "M"+ xCoor +"," + yRange[0] + "L" + xCoor + "," + yRange[1]; // position vertical line
});
}
});
First thing I would do is create the legend dynamically instead of hard coding each item:
var legEnter = chart1.append("g")
.attr("class","legend")
.selectAll('.legendItem')
.data(data)
.enter();
legEnter.append("text")
.attr("class","legendItem")
.attr("x",750)
.attr("y", function(d,i){
return 6 + (20 * i);
})
.text(function(d){
return d.key;
});
legEnter.append("circle")
.attr("cx",740)
.attr("cy", function(d,i){
return 4 + (20 * i);
})
.attr("r", 7)
.attr("fill", function(d,i){
return color(d.key);
});
Even if you leave it as you have it, the key here is to assign each text a class of legendItem. Then in your mouseover, find it and update it's value:
d3.select(d3.selectAll(".legendItem")[0][i]) // find it by index
.text(function(d,i){
return d.key + ": " + xDate.getFullYear() + "," + yVal;
});
Updated fiddle.

Multiple d3 charts in the same row

The code below draws 1 pie chart and a legend on the left side of the screen. Right now, I am trying to draw another pie chart with legend right next to the one on the left (same row). I've tried using multiple divs in the html to make this work, but I want a more pure d3 solution in which the duplication happens in the d3 code rather than in the html or css.
var w = 200;
var h = 200;
var r = h / 2;
var color = d3.scale.category20c();
var vis = d3.select(divId).append("svg:svg").data([descArray]).attr("width",w).attr("height", h).append("svg:g").attr("transform", "translate(" + r + "," + r + ")");
var pie = d3.layout.pie().value(function (d, i) {
return countArray[i];
});
// declare an arc generator function
var arc = d3.svg.arc().outerRadius(r);
// select paths, use arc generator to draw
var arcs = vis.selectAll("g.slice").data(pie).enter().append("svg:g").attr("class", "slice");
arcs.append("svg:path")
.on("click", function(d) {//clicking on individual arcs
arcs.selectAll("path").style("opacity", 1);//resets all arcs' opacity to 1
d3.select(this).style("opacity", 0.5);//sets clicked arc's opacity down
alert(d.data + " " + d.value);
})
.style("fill", function(d,i) { return color(i); })
.transition().delay(function(d, i) { return i * 100; }).duration(1000)
.attrTween('d', function(d) {
var i = d3.interpolate(d.startAngle+0.7, d.endAngle);
return function(t) {
d.endAngle = i(t);
return arc(d);
}
})
.attr("fill", function (d, i) {
return color(i);
});
var legend = d3.select(divId).append("svg")
.attr("class", "legend")
.attr("width", r * 4)
.attr("height", r * 4)
.selectAll("g")
.data(color.domain().slice().reverse())
.enter().append("g")
.attr("transform", function(d, i) { return "translate(230," + i * 27 + ")"; });
legend.append("rect")
.on("click", function(d) {
alert(d.data + " " + d.value);
})
.attr("width", 18)
.attr("height", 18)
.style("fill", function (d, i) {
return color(i);
})
put them in seperate divs but in the same SVG element
Presuming vis is your svgElement:
var firstChart = vis.append(div). // then put your first chart here
var secondChart = vis.append(div). // then put your second chart here

vertical bar showing values in javascript

I have an issue and I really need your help.
I have a realtime graph with a vertical bar that moves with cursor and i want it to show the value of the graph (d.time and d.value) when the cursor points to. http://jsfiddle.net/QBDGB/54/ i have two series of data (data1s and data2s) that is generated randomly and I put the time in which the data is generated in "time" variable as you can see:
now = new Date(Date.now() - duration);
var data1 = initialise();
var data2 = initialise();
//Make stacked data
var data1s = data1;
var data2s = [];
for(var i = 0; i < data1s.length; i++){
data2s.push({
value: data1s[i].value + data2[i].value,
time: data2[i].time
}
)};
function initialise() {
var arr = [];
for (var i = 0; i < n; i++) {
var obj = {
time: Date.now(),
value: Math.floor(Math.random() * 100)
};
arr.push(obj);
}
return arr;
}
When I hover around the graph I want the tooltip show the time and value but it does not recognize it and show "undefined" since I do not know how to pass my datasets (data1s and data2s) so "mouseover function can recognize which data to show! This is how the tooltip functions are made and call from "path1" and "path2".
function mouseover() {
div.transition()
.duration(500)
.style("opacity", 1);
}
function mousemove(d) {
div
.text( d.time+ ", " + d.value)
.style("left", (d3.event.pageX ) + "px")
.style("top", (d3.event.pageY ) + "px");
}
function mouseout() {
div.transition()
.duration(500)
.style("opacity", 1e-6);
}
var path1 = svg.append("g")
.attr("clip-path", "url(#clip)")
.append("path")
.data([data1s])
.attr("class", "line1")
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
var path2 =svg.append("g")
.attr("clip-path", "url(#clip)")
.append("path")
.data([data2s])
.attr("class", "line2")
.on("mouseover", mouseover)
.on("mousemove", mousemove)
.on("mouseout", mouseout);
Do you have any idea of what to do? i think i need to add
svg.selectAll("path1")
.attr("opacity", 1)
or svg.selectAll("datas1")
.attr("opacity", 1)
Somewhere! but i do not know how..
Thank you,
Update your mouseover function as:
function mousemove(d) {
div
.text( d[0].time+ ", " + d[0].value)
.style("left", (d3.event.pageX ) + "px")
.style("top", (d3.event.pageY ) + "px");
}
Include the index to the object 'd'.
Hope that helps.

Categories

Resources