d3.js - Pie chart with interactive legend hover problems - javascript

I made d3.js pie chart and related legend with population data popu. When I hover over pie segments I achieved to enlarge related legend square parts and the pie segment itself (larger outerRadius). Now I am trying to do contrary. When I hover over square of legend I want to enlarge square itself and related pie segment as well. Something like this example here https://www.amcharts.com/demos/pie-chart-with-legend/. I will write down just part of the code related to pie chart problem that I have.
var pie = d3.pie()
.value(function(d) {return d.pop})(popu);
var seg = d3.arc()
.innerRadius(100)
.outerRadius(150)
.padAngle(.1)
.padRadius(45);
var segover = d3.arc()
.innerRadius(100)
.outerRadius(170)
.padAngle(.1)
.padRadius(45);
So this part is working great.
svg.append("g")
.attr("class", "pieChart")
.attr("transform", "translate(1250,570)")
.selectAll("path")
.data(pie)
.append("path")
.attr("class", "pie")
.attr("id", function(d){return d.data.id})
.attr("d", seg)
.on("mouseenter", function(d){
d3.select(this)
.transition(10)
.duration(100)
.attr("d", segover)
})
Then I tried to change pie chart segment when hovering on legend related segments.
var pieEl = svg.selectAll(".pie");
var piePath = pieEl.nodes();
svg.append("g")
.attr("class", "legend")
.attr("transform", "translate(-50,280)")
.selectAll(".mySquers")
.data(pie)
.enter()
.append("rect")
.attr("class", "rec")
.attr("x", 100)
.attr("y", function(d,i){ return 100 + i*25})
.attr("width", "15")
.attr("height", "15")
.attr("id", function(d,i){ return (popu[d,i].id)})
.style("fill",function(d,i){
if (this.id == piePath[i].id){
return piePath[i].getAttribute("fill")
}
})
.on("mouseenter", function(d){
for (var i=0; i<piePath.length; i++){
if (piePath[i].id == d.data.id){
piePath[i].setAttribute("d", segover);
}}
})
When I tray to setAttribute("d", segover) in DOM instead of d attribute written as string as usually (d="M144.58.....") I have a function (d="function(pie){ _e);}" and on hover pie segment dissapear. But for example if I set attribute fill to red on hover it change and segment is painted. So the notation of code is good. Is there some behavior of d path generated with d3.arc() that I am missing? Any suggestion is welcome.

I think you should be passing your data as an argument in your function. Normally, it is taken as default argument when you return the function directly.
piePath[i].setAttribute("d", segover(*data associated with segment*));
svg.append("g")
.attr("class", "pieChart")
.attr("transform", "translate(1250,570)")
.selectAll("path")...
.attr("d", seg) // this is same as : attr("d", seg(d))
.on("mouseenter", function(d){
d3.select(this)
.transition(10)
.duration(100)
.attr("d", segover) // same here
})

Related

d3.js area() under a line using gradient function- x value only covering part of the graph

I am trying to create a line graph using d3.js, which takes a csv input and converts it to a data array with the keys x1, x2, class. Using that data I create a perceptron decision boundary using weights and the gradient function which updates and transitions after each iteration.
This works nicely.
What I am struggling with is the area under the curve, as I want positive values to be blue and negative to be red. The areas move with the line correctly, but something is wrong with the x values as the width of the area doesn't cover the whole graph. It covers about half, and starts about a quarter of the way in (I can't post an image.)
Here is the code I'm using for all of these elements, but I thin I am misunderstanding the way area uses the x attribute;
lineFunction = d3.svg.line()
.x(function(d) { return widthScale(d.x1); })
.y(function(d) { return heightScale(((-d.x1 * w1) - w0)/w2); })
.interpolate("linear");
var area = d3.svg.area()
.x(function(d) { return widthScale(d.x1); })
.y0(xPos)
.y1(function(d) { return heightScale(((-d.x1 * w1) - w0)/w2); })
.interpolate("linear");
var area2 = d3.svg.area()
.x(function(d) { return widthScale(d.x1); })
.y0(heightAlter)
.y1(function(d) { return heightScale(((-d.x1 * w1) - w0)/w2); })
.interpolate("linear");
lineGraph = canvas.append("path")
.attr("d", lineFunction(lineData))
.attr("class", "autoLine")
.attr("stroke", "black")
.attr("stroke-width", 1)
.attr("fill", "none")
.attr("clip-path", "url(#clip)");
shadedArea = canvas.append("path")
.data([data])
.attr("d", area)
.attr("class", "area")
.attr("clip-path", "url(#clip)");
shadedAreaPos = canvas.append("path")
.data([data])
.attr("d", area2)
.attr("class", "area2")
.attr("clip-path", "url(#clip)");
Any help would be much appreciated.
I managed to find that the root of the problem was the way in which the data array was ordered. I do not fully understand what is happening behind the scenes, but by simply reordering the array in ascending order on the x1 values, it now works correctly. I hope this helps someone.

D3.js transition bar chart when switching between data sources

I am using D3.js to create a bar chart. I have several source CSV files that I switch between based on user input:
onclick="showFoo();"
onclick="showBar();"
Then:
function showFoo(){d3.csv("teams/foo.csv",type,update)}
function showBar(){d3.csv("teams/bar.csv",type,update)}
function update(error, data) {
x.domain(data.map(function(d){return d.category;}));
y.domain([0,1]);
chart.select(".x.axis").remove();
chart.append("g")
.attr("class","x axis")
.attr("transform","translate(0," + height + ")")
.call(xAxis)
.selectAll("text")
.style("text-anchor","start")
.attr("transform","rotate(-90)");
chart.selectAll(".bar").remove();
chart.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class","bar")
.attr("x",function(d){return x(d.category);})
.attr("y",function(d){return y(d.value);})
.attr("height",function(d){return height - y(d.value);})
.attr("width",x.rangeBand());
};
I am using an ordinal scale for my x axis. The data files have different numbers of categories. What I want to do is animate the switch between the two data sources. Specifically, have the first bars drop down and disappear below the x axis, then have the new bars pop up from below. I'm a D3 noob and haven't been able to find an example of this kind of behavior.
Use transition for the animation
For removing:
chart.selectAll(".bar")
.transition()
.duration(300)
.ease("exp")
.attr("width", 0).remove()
For creating
chart.selectAll(".bar")
.data(data)
.enter()
.append("rect")
.attr("class","bar")
.transition()
.duration(300)
.attr("x",function(d){return x(d.category);})
.attr("y",function(d){return y(d.value);})
.attr("height",function(d){return height - y(d.value);})
.attr("width",x.rangeBand());

Changing the symbol in d3's GeoPaths MultiPoints

When I render latitude/longitude points (my_coords) on my globe with:
svg.append("path")
.datum({type: "MultiPoint", coordinates: my_coords})
.attr("class", "points")
.attr("d", path);
I get circles. I can change the color with CSS, but how can I change the symbol from circles to triangles? I've tried:
svg.append("path")
.datum({type: "MultiPoint", coordinates: my_coords})
.attr("class", "point")
.attr("d", d3.svg.symbol().type("triangle-up"));
But this doesn't work.
You can try this:
//define triangle
var arc = d3.svg.symbol().type('triangle-up');
// put a triangle on every city
svg.selectAll(".tripath")
.data(topojson.feature(uk, uk.objects.places).features)
.enter().append("path")
.attr('d',arc)
.attr("transform", function(d) {
return "translate(" + projection(d.geometry.coordinates) + ")"; });
Here the working code: http://jsfiddle.net/6d3ansfn/
Here the SVG Shape reference from Mike's github: https://github.com/mbostock/d3/wiki/SVG-Shapes#symbol
Example code is from Mike: http://bost.ocks.org/mike/map/
If you has a .tsv file width coordinates, use this:
svg.selectAll(".tripath")
.data( my_coords)
.enter().append("path")
.attr('d',arc)
.attr("transform", function(d) {
return "translate(" + projection(d) + ")"; });
remember update proyection on drag and/or zoom.

Tooltip for Line Chart with Clip Path in D3

I have put together a D3 line chart and added threshold encoding using clip path / clipping. The only problem I am facing is I am not able to add tooltips to this chart. I want a tooltip when I hover anywhere in the chart and the corresponding y axis value on the chart shows up in the tooltip.
I have added threshold encoding using this example by Mike Bostock.
var line = d3.svg.line()
.interpolate("basis")
.x(function(d) { return _config.xScale(d.vtc); })
.y(function(d) { return _config.yScale(d.values); });
svg.append("clipPath")
.attr("id", "clip-above")
.append("rect")
.attr("width", _config.width)
.attr("height", _config.yScale(55));
svg.append("clipPath")
.attr("id", "clip-below")
.append("rect")
.attr("y", _config.yScale(55))
.attr("width", _config.width)
.attr("height", _config.height - _config.yScale(55));
svg.selectAll(".line")
.data(["above", "below"])
.enter().append("path")
.attr("class", function(d) { return "line " + d; })
.attr("clip-path", function(d) { return "url(#clip-" + d + ")"; })
.datum(data)
.attr("d", line);
I didn't know how to go about adding a tooltip for this particular chart as there is clip rectangle over the path and the path is broken down into above and below segment to give the colour effects.
Do we have a unified way to add a tooltip to normal path and this one? If yes I would like to know some sources/links I can look at.
Something like this, but not that complicated (without any indicator on the line, just the tooltip)
My CODEPEN LINK
You can add mouseOver handler for the line and translate back the mouse y position to yAxis value using the .invert function of d3 linear scale. Now, you can dynamically add a tooltip text element and set the position, value to it
Here is the updated Codepen link
NOTE: You still need to increase the capture area of the line. This can be done by adding a transparent stroke to the line.
svg.selectAll(".line")
.data(["above", "below"])
.enter().append("path")
.attr("class", function(d) { return "line " + d; })
.attr("clip-path", function(d) { return "url(#clip-" + d + ")"; })
.datum(data)
.attr("d", line)
.on("mouseover", function() {
var mousePos = d3.mouse(this);
var yAxisValue = _config.yScale.invert(mousePos[1]);
svg.selectAll(".tooltip").data([mousePos])
.enter().append("text")
.classed("tooltip", true)
.attr("x", function(d) { return d[0]})
.attr("y", function(d) { return d[1]})
.text(yAxisValue);
})
.on("mouseout", function() {
svg.selectAll(".tooltip").data([]).exit().remove();
});

Transition for a line chart in D3

I have built a multi series line chart using the D3 library. I have been trying to work on animating the line chart so I can give out one line at a time and not all 5 of them together. Can anybody give me some ideas as to how I can go about with this? I have tried using transition() but I seem to be using it wrong. This is what I have done so far -
var city = svg.selectAll(".city")
.data(years)
.enter().append("g")
.attr("class", "city");
city.append("path")
.attr("class", "line");`
d3.transition().selectAll(".line")
.duration(500)
.ease("linear")
.attr("d", function(d){return line(d.values); })
.style("stroke", function(d) {return color(d.name); })
.attrTween("d",pathTween);
function pathTween() {
var interpolate = d3.scale.quantile()
.domain([0,1])
.range(d3.range(1,years.length+1));
return function(t){
return line(data.slice(0,interpolate(t)));
};
D3 transitions have a .delay() method that allows you to set a delay before each transition starts. In your case (having each line start separately) it would look like this:
d3.transition().selectAll(".line")
.duration(500)
.delay(function(d, i) { return i * 500; })
.ease("linear")
.attr("d", function(d){return line(d.values); })
.style("stroke", function(d) {return color(d.name); });
This takes the index of each line (i) and offsets the start of the transition depending on that.

Categories