How can I highlight a slice of multiple donut chart d3 - d3.js

I am beginner in d3 v3. I created multiple donut charts. When I move my mouse over a slice, I get the tooltip that appears. But I would also like the slice to grow a little bit.
I have already tried several codes but I can't get there. I wonder if the problem may be related to d3 tip
this is my js file :
//Source : http://bl.ocks.org/mbostock/1305337
var m = 15,
r = 80,
z = d3.scale.ordinal()
.range(["#50FFC5", "#54E868", "#54CCE8", "#6395FF"]);
var pie = d3.layout.pie()
.value(function (d) {
return +d.count;
})
.sort(function (a, b) {
return b.count - a.count;
});
var arc = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r);
var radius = d3.scale.linear()
.range([10, r])
var tip = d3.tip()
.attr('class', 'd3-tip')
.html(function (d) {
return d.data.genre + ": " + d.data.count;
})
.direction('s');
d3.csv("../CSV/genreHameau.csv", function (error, hameau) {
if (error) throw error;
var datas = d3.nest()
.key(function (d) {
return d.origine;
})
.entries(hameau);
datas.forEach(function (d) {
totalOrigin = d3.sum(d.values, function (d) {
return +d.count;
})
d.values.forEach(function (dd) {
dd.totalOrigin = totalOrigin
})
})
datas.sort(function (a, b) {
return d3.descending(a.values[0].totalOrigin, b.values[0].totalOrigin)
})
// définir le radius / rayon des arcs (rendre fonction de 'count')
var max = d3.max(datas, function (d) {
return d.values[0].totalOrigin
})
var min = d3.min(datas, function (d) {
return d.values[0].totalOrigin
})
radius.domain([min, max])
arc
.innerRadius(function (d) {
return radius(d.data.totalOrigin) / 2
})
.outerRadius(function (d) {
return radius(d.data.totalOrigin)
})
function size(d) {
return radius(d.values[0].totalOrigin) + m
}
var div = d3.select("body").selectAll("div")
.data(datas)
.enter().append("div") // http://code.google.com/p/chromium/issues/detail?id=98951
.style("display", "inline-block")
.style("width", function (d) {
return 2 * size(d) + "px"
})
.style("height", function (d) {
return 2 * size(d) + "px"
})
.style("min-width", "80px")
.style("min-height", "80px")
div.append("span")
.attr("class", "nomhameau")
.text(function (d) {
return d.key;
})
.append("span")
.attr("class", "nombrehameau")
.text(function (d) {
return " (" + d.values[0].totalOrigin + ")"
});
var svg = div.append("svg")
//.attr("width", (r + m) * 2)
//.attr("height", (r + m) * 2)
.attr("width", function (d) {
return 2 * size(d)
})
.attr("height", function (d) {
return 2 * size(d)
})
.append("g")
.attr("transform", function (d) {
return "translate (" + size(d) + "," + size(d) + ")"
});
svg.call(tip);
var g = svg.selectAll("g")
.data(function (d) {
return pie(d.values);
})
.enter().append("g")
.on("mouseover", tip.show)
.on("mouseout", tip.hide)
g.append("path")
.attr("d", arc)
.style("fill", function (d) {
return z(d.data.genre);
})
.append("title")
});
Thank you for your help

I would do it this way:
Just like you created the arc, create a new one with a greater radius, either inner, outer or both, something like:
var arcHighlight = d3.svg.arc()
.innerRadius(r / 2)
.outerRadius(r*1.1);
Then you add mouseover and mouseout events to the slice and modify the slice doing something like:
// Add a colored arc path, with a mouseover title showing the count.
g.append("path")
.attr("d", arc)
.style("fill", function(d) { return z(d.data.carrier); })
// new code
.on('mouseover', function(d) {
d3.select(this)
.transition()
.attr('d', arcHighlight(d));
})
.on('mouseout', function(d) {
d3.select(this)
.transition()
.attr('d', arc(d));
})
.append("title")
.text(function(d) { return d.data.carrier + ": " + d.data.count; });
The transition is optional, but it looks fancier ;)

Related

Problems about update, enter, exit data and select in d3.js

I'm doing a visual project to show natural disaster in 1900-2018 using d3. I want add an interactive action that one can choose the first year and last year to show.
Originally I create the picture as the following:
d3.csv("output.csv", rowConventer, function (data) {
dataset = data;
var xScale = d3.scaleBand()
.domain(d3.range(dataset.length))
.range([padding, width - padding])
.paddingInner(0.05);
var yScale = d3.scaleLinear()
.domain([0,
d3.max(dataset, function (d) {
return d.AllNaturalDisasters;
})])
.range([height - padding, padding])
.nice();
stack = d3.stack().keys(["Drought", "Earthquake", "ExtremeTemperature", "ExtremeWeather", "Flood", "Impact", "Landslide", "MassMovementDry", "VolcanicActivity", "Wildfire"]);
series = stack(dataset);
gr = svg.append("g");
groups = gr.selectAll("g")
.data(series)
.enter()
.append("g")
.style("fill", function(d, i) {
return colors(i);
})
.attr("class", "groups");
rects = groups.selectAll("rect")
.data(function(d) { return d; })
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScale(i);
})
.attr("y", function(d) {
return yScale(d[1]);
})
.attr("height", function(d) {
return yScale(d[0]) - yScale(d[1]);
})
.attr("width", xScale.bandwidth())
.append("title")
.text(function (d) {
var rect = this.parentNode;// the rectangle, parent of the title
var g = rect.parentNode;// the g, parent of the rect.
return d.data.Year + ", " + d3.select(g).datum().key + "," + (d[1]-d[0]);
});
d3.select("button")
.on("click", choosePeriod);
I have simplified some code to make my question simple. At the last row, I add an event listener to achieve what I described above. And the update function is choosePeriod. Now it is as following:
function choosePeriod() {
firstYear = parseInt(document.getElementById("FirstYear").value);
lastYear = parseInt(document.getElementById("LastYear").value);
d3.csv("output.csv", rowConventer, function (newdata) {
dataset = newdata;
series=stack(dataset);
groups.data(series);
groups.selectAll("rect")
.data(function (d) {
return d;
})
.enter()
.append("rect")
.attr("x", function(d, i) {
return xScales(i);
})
.attr("y", function(d) {
return yScales(d[1]);
})
.attr("height", function(d) {
return yScales(d[0]) - yScales(d[1]);
})
.attr("width", xScales.bandwidth())
.append("title")
.text(function (d) {
var rect = this.parentNode;// the rectangle, parent of the title
var g = rect.parentNode;// the g, parent of the rect.
return d.data.Year + ", " + d3.select(g).datum().key + "," + (d[1]-d[0]);
});
groups.selectAll("rect")
.data(function (d) {
return d;
})
.exit()
.remove();
})
}
The change of dataset is achieved by rowConventer, which is not important in this question. Now the functionchoosePeriod is not running as envisioned! Theenter and the exit and update are all not work well! The whole picture is a mess! What I want is, for instance, if I input the firstYear=1900 and the lastYear=2000, then the picture should be updated with the period 1900-2000 to show. How can I achieve it?
I am unfamiliar the arrangement of the entire structure, I mean, at some place using d3.select() by class or id instead of label is better, right?
It looks like you've dealt with the enter and the exit selections. The only bit you're missing is the update selection, which will deal with the rectangles that already exist and don't need adding or removing. To do this copy your update pattern but just remove the enter().append() bit, e.g.:
groups.selectAll("rect")
.data(function (d) {
return d;
})
.attr("x", function(d, i) {
return xScales(i);
})
.attr("y", function(d) {
return yScales(d[1]);
})
.attr("height", function(d) {
return yScales(d[0]) - yScales(d[1]);
})
.attr("width", xScales.bandwidth())
.append("title")
.text(function (d) {
var rect = this.parentNode;// the rectangle, parent of the title
var g = rect.parentNode;// the g, parent of the rect.
return d.data.Year + ", " + d3.select(g).datum().key + "," + (d[1]-d[0]);
})

How to Drag line by its end points using rectangle as end points error?

I am new to d3.js and please forgive me if this sounds like a naive question. I have plotted a line (d3 v4) which can be draggable by its end points. The end points are rectangle.
The current output looks as below :
This is how it looks
The challenge that i am facing is - when i start dragging the point, the line seems to take its origin from the top left corner. When i drag the second point of the same line, the line drags / moves as expected.
The sample data looks as below :
The sample data
Requesting your suggestions / inputs on how to fix the above issue.
Below is the attached code that i am using :
var margin = { top: 0, right: 0, bottom: 0, left: 0 },
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom;
// Creating the colour Category
var color = d3.scaleOrdinal(d3.schemeCategory10);
var y = d3.scaleLinear().range([390, 0]);
// Scale the range of the data
y.domain([0, d3.max(data, function (d) { return Math.max(d.nonpromotedprice, d.promotedprice)*1.2; })]).nice();
// Line for the 1st Block
var lines = svg.selectAll("line")
.data(data)
.enter()
.append('line')// attach a line
.style("stroke", "#E6EAEE")
.style("stroke-width", 8) // colour the line
.attr("x1", function (d) { return d.x_value; }) // x position of the first end of the line
.attr("y1", function (d) { return y(d.nonpromotedprice); }) // y position of the first end of the line
.attr("x2", function (d) { return d.x_value; }) // x position of the second end of the line
.attr("y2", function (d) { return y(d.promotedprice); });
// Add the Y Axis
svg.append("g")
.attr("class", "grid")
.attr("fill", "lightgrey")
.attr("stroke-width", 0.7)
.attr("stroke-opacity", 0.2)
.call(d3.axisLeft(y)
.tickSize(-400)
.tickFormat(""));
var topEndPoints = data.map(function (line, i) {
return {
'x': line.x_value,
'y': line.nonpromotedprice,
'marker': 'marker-start',
'lineIndex': i
};
});
var bottomEndPoints = data.map(function (line, i) {
return {
'x': line.x_value,
'y': line.promotedprice,
'marker': 'marker-end',
'lineIndex': i
};
});
var MiddleEndPoints = data.map(function (line, i) {
return {
'x': line.x_value,
'y': line.avgprice,
'marker': 'marker-middle',
'lineIndex': i
};
});
var endPointsData = topEndPoints.concat(bottomEndPoints, MiddleEndPoints);
// Pointer to d3 rectangles
var endPoints = svg
.selectAll('rect')
.data(endPointsData)
.enter()
.append('rect')
.attr("width", 12)
.attr("height", 8)
.attr("x", function (d) { return d.x - 6; })
.attr("y", function (d) { return y(d.y); })
//.attr("cx", function (d) { return d.x; })
//.attr("cy", function (d) { return d.y; })
//.attr('r',7)
.attr("fill", function (d) { return color(d.x); })
.call(d3.drag()
//.origin(function(d) { return y(d.y); })
.subject(function() {
var t = d3.select(this);
return {x: t.attr("x"), y: t.attr("y")};
})
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// draw the logo
svg.selectAll("image")
.data(data)
.enter()
.append("svg:image")
.attr("xlink:href", function (d) { return d.logo; })
//.append("rect")
.attr("x", function (d) { return d.x_value - 13; })
.attr("y", function (d) { return y(d.nonpromotedprice + 35); })
.attr("height", 25)
.attr("width", 25);
function dragstarted() {
d3.select(this).classed("active", true).attr('y', d.y = y(d3.event.y));
}
function dragged(d, i) {
var marker = d3.select(this);
// Update the marker properties
marker
//.attr('cx', d.x = d3.event.x)
.attr('y', d.y = d3.event.y);
// Update the line properties
lines
.filter(function (lineData, lineIndex) {
return lineIndex === d.lineIndex;
})
.attr('x1', function (lineData) {
return d.marker === 'marker-start' ? lineData.x1 = d.x : lineData.x1;
})
.attr('y1', function (lineData) {
return d.marker === 'marker-start' ? lineData.y1 = d.y : lineData.y1;
})
.attr('x2', function (lineData) {
return d.marker === 'marker-end' ? lineData.x2 = d.x : lineData.x2;
})
.attr('y2', function (lineData) {
return d.marker === 'marker-end' ? lineData.y2 = d.y : lineData.y2;
});
}
function dragended() {
d3.select(this).classed("active", false);
Shiny.setInputValue("pricechanged",
{price: (d3.max(data, function (d) { return Math.max(d.nonpromotedprice, d.promotedprice); }) -(d3.event.y / 390)* d3.max(data, function (d) { return Math.max(d.nonpromotedprice, d.promotedprice); }))*1.19},
{priority: "event"}
);
}

Double-click on a node to fade out all but its immediate neighbours. Double-click to bring them back again

I am using the below code for the iamges and mouseover text. And now i have added the code for double click on a node to fade out all but it is not working and also link color is not changing. Can anyone help on this
var width = 960,
height = 500
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var force = d3.layout.force()
.gravity(0.1)
.charge(-120)
.linkDistance(30)
.size([width, height]);
var voronoi = d3.geom.voronoi()
.x(function(d) { return d.x; })
.y(function(d) { return d.y; })
.clipExtent([[0, 0], [width, height]]);
d3.json("miserables1.json", function(error, json) {
if (error) throw error;
force
.nodes(json.nodes)
.links(json.links)
.start();
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link", "fill:red; stroke:red;");
var node = svg.selectAll(".node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.call(force.drag);
node.append("svg:image")
.attr("xlink:href", function(d) { return d.imagen })
.on('dblclick', connectedNodes);
var circle = node.append("circle")
.attr("r", 4.5);
var label = node.append("text")
.attr("dy", ".35em")
.text(function(d) { return d.name; });
var cell = node.append("path")
.attr("class", "cell");
force.on("tick", function() {
cell
.data(voronoi(json.nodes))
.attr("d", function(d) { return d.length ? "M" + d.join("L") : null; });
link
.attr("x1", function(d) { return d.source.x; })
.attr("y1", function(d) { return d.source.y; })
.attr("x2", function(d) { return d.target.x; })
.attr("y2", function(d) { return d.target.y; });
circle
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
label
.attr("x", function(d) { return d.x + 8; })
.attr("y", function(d) { return d.y; });
});
});
var toggle = 0;
var linkedByIndex = {};
for (i = 0; i < graph.nodes.length; i++) {
linkedByIndex[i + "," + i] = 1;
};
graph.links.forEach(function (d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function neighboring(a, b) {
return linkedByIndex[a.index + "," + b.index];
}
function connectedNodes() {
if (toggle == 0) {
//Reduce the opacity of all but the neighbouring nodes
d = d3.select(this).node().__data__;
node.style("opacity", function (o) {
return neighboring(d, o) | neighboring(o, d) ? 1 : 0.1;
});
link.style("opacity", function (o) {
return d.index==o.source.index | d.index==o.target.index ? 1 : 0.1;
});
toggle = 1;
} else {
node.style("opacity", 1);
link.style("opacity", 1);
toggle = 0;
}
}

Simple Colission Example With g groups?

Simple collision example, using g groups?
Hi all, i test this examples:
1- http://bl.ocks.org/mbostock/3231298 - this example use a invisible circle for generate the force.
2- http://bl.ocks.org/mbostock/1747543 - this example use a central circle for force layout
This examples always use a circles.
My idea is use pie charts with collision force but i can't find a simple example of this.
I use this construction:
chart_vars_object.svg = d3.select(".canvas_svg").append("svg")
.attr({
"width": general_vars_object.width,
"height": general_vars_object.height
})
var nodes = chart_vars_object.svg.selectAll(".pie")
.data(chart_vars_object.json_init_data)
.enter()
.append("g")
.attr("class", function(d) {
return "pie pie_id_" + d.hash_chart_id
})
.attr("width", chart_vars_object.general_radius * 2)
.attr("height", chart_vars_object.general_radius * 2)
.attr("hash_link", function(d) {
return d.hash_link
})
.call(chart_vars_object.force.drag);
chart_vars_object.force.on("tick", function(e) {
chart_vars_object.svg.selectAll(".pie")
.attr("transform", function(d, i) {
return "translate(" + Math.round(d.x) + "," + Math.round(d.y) + ")";
});
});
//CREATE ARC GROUP's
var g = nodes.selectAll(".arc")
.data(function(d) {
return chart_vars_object.pie(d.hash_taggeds);
})
.enter().append("g")
.attr("class", "arc_group")
.attr("hash_type", function(d, i) {
if (i == 0 ) {
return "article";
}
if (i == 1 ) {
return "image";
}
if (i == 2 ) {
return "video";
}
})
.on("click", function() {
d3.select(this).select('.arc')
alert('Abro el Has Tag');
})
.on("mouseover", function() {
d3.select(this).select('.arc')
.classed("active", true )
})
.on("mouseout", function() {
d3.select(this).select('.arc')
.classed("active", false)
});
//CREATE PATHS WITH ARC
chart_vars_object.path = g.append("path")
.attr("class","arc")
.style({'cursor': 'pointer', 'opacity': '0.9'})
chart_vars_object.path.transition()
.duration(20)
.attr("fill", function(d) {
return chart_vars_object.color(d.data.hash_taggeds_name);
})
.attr("d", chart_vars_object.arc)
.each(function(d) {
this._current = d;
});
Any suggest?

How to update data in stack bar chart in D3

I am able to populate a stacked bar chart first time, but my requirement is to update the stacked bar chart with new data on button click. On button click, i m making call to backend and getting the data, Could you please guide me as how to update the stacked bar char chart. My problem is passing the new data to bar chart.
d3.json("http://myhost/ITLabourEffortChart/effort/effort",function(error, data){
color.domain(d3.keys(data.effort[0]).filter(function(key) {
return key !== "qName"; }));
data.effort.forEach(function(d) {
var y0 = 0;
d.effortHr = color.domain().map(function(name) {
return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.effortHr[d.effortHr.length - 1].y1;
});
x.domain(data.effort.map(function(d) { return d.qName; }));
y.domain([0, d3.max(data.effort, function(d) {
return d.total; })]);
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("FTE");
var state = svg.selectAll(".layer")
.data(data.effort)
.enter().append("g")
.attr("class", "g")
.attr("transform", function(d) { return "translate(" + x(d.qName) + ",0)"; });
rect = state.selectAll("rect")
.attr("id", "barchart")
.data(function(d) {
return d.effortHr; })
.enter().append("rect")
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); });
On Update i am calling below method
function redraw() {
d3.json("http://localhost:8080/ITLabourEffortChart/effort/effort/YrReports",function(error, data){
color.domain(d3.keys(data.effort[0]).filter(function(key) {
return key !== "qName"; }));
data.effort.forEach(function(d) {
var y0 = 0;
d.ages = color.domain().map(function(name) {
return {name: name, y0: y0, y1: y0 += +d[name]}; });
d.total = d.ages[d.ages.length - 1].y1;
});
var updatebar = svg.selectAll("#barchart");
// Update…
updatebar
.transition()
.duration(500)
.delay(function(d, i) { return i * 10; })
.transition()
.attr("width", x.rangeBand())
.attr("y", function(d) {
return y(d.y1); })
.attr("height", function(d) { return y(d.y0) - y(d.y1); })
.style("fill", function(d) { return color(d.name); }
.attr("x", function(d) {
return x(d.x); })
);
});
.attr("x", function(d) {
return x(d.x); })
);
});
To update your data you will just need to select the svg elements again and rebind the data. In your example you are already selecting the #barchart, now you just need to rebind the data. And you can do that in the same way you did it when you first created the svg Elements. So something like this should do the trick:
var updatebar = svg.selectAll("#barchart");
.data(newdata)
.transition()
.duration(500)
... (etc.)
Here you can find a more detailed explaination:
http://chimera.labs.oreilly.com/books/1230000000345/ch09.html#_updating_data
Update:
Ok, unfortunately I cannot use Fiddle so I just post my working code here. As far as I could see you have a problem with your selectAll, because there is no element called .effort. Here is the updated code for your redraw-function:
function redraw() {
var effort = [];
var obj = {
pfte: "20",
efte: "50",
qName: "Q1"
};
var obj2 = {
pfte: "10",
efte: "13",
qName: "Q2"
};
effort[0] = obj;
effort[1] = obj2;
var newDataSet = new Object();
newDataSet.effort = effort;
color.domain(d3.keys(newDataSet.effort[0]).filter(function (key) {
return key !== "qName";
}));
effortDataSet = newDataSet.effort;
effortDataSet.forEach(function (d) {
var y0 = 0;
d.effortHr = color.domain().map(function (name) {
return { name: name, y0: y0, y1: y0 += +d[name] };
});
d.total = d.effortHr[d.effortHr.length - 1].y1;
});
state = svg.selectAll(".g")
.data(effortDataSet)
.attr("class", "g")
.attr("transform", function (d) { return "translate(" + x(d.qName) + ",0)"; });
state = state.selectAll("rect")
.data(function (d) {
return d.effortHr;
})
.attr("width", x.rangeBand())
.attr("y", function (d) {
return y(d.y1);
})
.attr("height", function (d) {
//console.log(y(d.y0) - y(d.y1));
return y(d.y0) - y(d.y1);
})
.style("fill", function (d) { return color(d.name); });
}

Resources