How can I add a "%" to all values in d3.js? - javascript

If I simply add a % to the value it won't work. How can I add % to all the values?
var chartData = {
"barCircleMobile":[
{"index":0.3, "value":100, "fill":"#37b54e", "label":"WebMd Health"},
{"index":0.4, "value":50, "fill":"#69dad9", "label":"Livestrong.com"},
{"index":0.5, "value":31.25, "fill":"#00a8dd", "label":"Everyday
Health"},
{"index":0.6, "value":18.75, "fill":"#157996", "label":"About.com"},
{"index":0.7, "value":6.5, "fill":"#0068ff", "label":"Drugs.com"},
],
"barCircleWeb":[
{"index":0.3, "value":100, "fill":"#20d974", "label":"WebMD Health"},
{"index":0.4, "value":40, "fill":"#3bc1c4", "label":"Everyday Health"},
{"index":0.5, "value":30, "fill":"#23889d", "label":"Livestrong.com"},
{"index":0.6, "value":20, "fill":"#0071a3", "label":"About.com Health
Section"},
{"index":0.7, "value":10, "fill":"#0042a3", "label":"Healthline"},
]
};
function drawBarCircleChart(data,target,values,labels){
var w = 362,
h = 362,
size = data[0].value * 1,
radius = 200,
sectorWidth = .1,
radScale = 25,
sectorScale = 1.75,
target = d3.select(target),
valueText = d3.select(values),
labelText = d3.select(labels);
var arc = d3.svg.arc()
.innerRadius(function(d,i){return (d.index/sectorScale) * radius +
radScale; })
.outerRadius(function(d,i){return ((d.index/sectorScale) +
(sectorWidth/sectorScale)) * radius + radScale; })
.startAngle(Math.PI)
.endAngle(function(d) { return Math.PI + (d.value / size) * 2 * Math.PI;
});
var path = target.selectAll("path")
.data(data);
//TODO: seperate color and index from data object, make it a pain to update
object order
path.enter().append("svg:path")
.attr("fill",function(d,i){return d.fill})
.transition()
.ease("elastic")
.duration(1000)
.delay(function(d,i){return i*100})
.attrTween("d", arcTween);
valueText.selectAll("tspan").data(data).enter()
.append("tspan")
.attr({
x:50,
y:function(d,i){return i*14},
"text-anchor":"end"
})
.text(function(d,i){return data[i].value});
labelText.selectAll("tspan").data(data).enter()
.append("tspan")
.attr({
x:0,
y:function(d,i){return i*14}
})
.text(function(d,i){return data[i].label});
function arcTween(b) {
var i = d3.interpolate({value: 0}, b);
return function(t) {
return arc(i(t));
};
}
}
// Animation Queue
setTimeout(function(){drawBarCircleChart(chartData.barCircleWeb,"#circleBar-
web-chart","#circleBar-web-values","#circleBar-web-labels")},500);
setTimeout(function()
{drawBarCircleChart(chartData.barCircleMobile,"#circleBar-mobile-
chart","#circleBar-mobile-values","#circleBar-mobile-labels")},800);
d3.select("#circleBar-web-icon")
.transition()
.delay(500)
.duration(500)
.attr("opacity","1");
d3.select("#circleBar-web-text")
.transition()
.delay(750)
.duration(500)
.attr("opacity","1");
d3.select("#circleBar-web-clipLabels")
.transition()
.delay(600)
.duration(1250)
.attr("height","150");
d3.select("#circleBar-mobile-icon")
.transition()
.delay(800)
.duration(500)
.attr("opacity","1");
d3.select("#circleBar-mobile-text")
.transition()
.delay(1050)
.duration(500)
.attr("opacity","1");
d3.select("#circleBar-mobile-clipLabels")
.transition()
.delay(900)
.duration(1250)
.attr("height","150");

You can add '%' in your text return,
valueText.selectAll("tspan").data(data).enter()
.append("tspan")
.attr({
x:50,
y:function(d,i){return i*14},
"text-anchor":"end"
})
.text(function(d,i){return data[i].value +'%' });
I hope it helps you

Related

animate arc start angle with d3.js

I am trying to animate the start angle of the arc using D3.js
Any help or link for reference would do.
I have tried the below:
http://jsfiddle.net/87e3d4tj/
d3.select('#my-path').datum({
startAngle: endAngle,
endAngle: ( 90 * (Math.PI/180) )
})
.transition()
.duration(duration)
.attrTween('d', d => {
var interpolate = d3.interpolate(d.startAngle, d.endAngle);
return function(t) {
d.endAngle = endAngle;
d.startAngle = interpolate(t);
return arc(d);
};
});
i have tried the below hope you are looking for the same.
https://jsfiddle.net/debasish007/eu7xo4mL/
var width = 400,
height = 400,
τ = (Math.PI/180);
var arc = d3.arc()
.innerRadius(130)
.outerRadius(150);
var svg = d3.select("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")")
var foreground = svg.append("path")
.datum({
endAngle: 90 * τ,
startAngle: -90 * τ
})
.style("fill", "blue")
.attr("d", arc);
setTimeout(function () {
foreground.transition()
.duration(750)
.call(arcTween, -30 * τ, 90 * τ);
}, 1500);
function arcTween(transition, newStartAngle, newFinishAngle) {
transition.attrTween("d", function (d) {
var interpolateStart = d3.interpolate(d.startAngle, newStartAngle);
return function (t) {
d.endAngle = newFinishAngle;
d.startAngle = interpolateStart(t);
return arc(d);
};
});
}

D3: Rotating labels in Bi-Level Partition

I'm using D3 and javascript to create a BiLevel Partition following this example. The labels in the left side of the diagram are upside down, and I was trying to rotate them, but I've not been successful yet.
I found numerous cases of people with the same problem, but using Sunburst. Also tried to implement those solutions, but I'm still unable to solve this problem.
<body>
<script src="http://d3js.org/d3.v3.min.js"></script>
<script>
/*var margin = {top: 350, right: 480, bottom: 350, left: 480},
radius = Math.min(margin.top, margin.right, margin.bottom, margin.left) - 10;*/
var width = 1200,
height = 1200,
radius = Math.min(width, height) / 2;
function filter_min_arc_size_text(d, i) {return (d.dx*d.depth*radius/3)>14};
var hue = d3.scale.category10();
var luminance = d3.scale.sqrt()
.domain([0, 1e6])
.clamp(true)
.range([90, 20]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ")");
var partition = d3.layout.partition()
.sort(function(a, b) { return d3.ascending(a.name, b.name); })
.size([2 * Math.PI, radius]);
var arc = d3.svg.arc()
.startAngle(function(d) { return d.x; })
.endAngle(function(d) { return d.x + d.dx - .01 / (d.depth + .5); })
.innerRadius(function(d) { return radius / 3 * d.depth; })
.outerRadius(function(d) { return radius / 3 * (d.depth + 1) - 1; });
//Tooltip description
var tooltip = d3.select("body")
.append("div")
.attr("id", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("opacity", 0);
function format_number(x) {
return x.toString().replace(/\B(?=(\d{3})+(?!\d))/g, ",");
}
function format_description(d) {
var description = d.description;
return /* '<b>' + d.name + '</b></br>'+*/ d.description + '<br> (' + format_number(d.value) + ')';
}
function computeTextRotation(d) {
var ang = ((d.x + d.dx / 2) - Math.PI / 2) / Math.PI * 180;
return (ang > 90) ? 180 + ang : ang;
}
function mouseOverArc(d) {
d3.select(this).attr("stroke","black")
tooltip.html(format_description(d));
return tooltip.transition()
.duration(50)
.style("opacity", 0.9);
}
function mouseOutArc(){
d3.select(this).attr("stroke","")
return tooltip.style("opacity", 0);
}
function mouseMoveArc (d) {
return tooltip
.style("top", (d3.event.pageY-10)+"px")
.style("left", (d3.event.pageX+10)+"px");
}
var root_ = null;
d3.json("flare.json", function(error, root) {
if (error) return console.warn(error);
// Compute the initial layout on the entire tree to sum sizes.
// Also compute the full name and fill color for each node,
// and stash the children so they can be restored as we descend.
partition
.value(function(d) { return d.size; })
.nodes(root)
.forEach(function(d) {
d._children = d.children;
d.sum = d.value;
d.key = key(d);
d.fill = fill(d);
});
// Now redefine the value function to use the previously-computed sum.
partition
.children(function(d, depth) { return depth < 2 ? d._children : null; })
.value(function(d) { return d.sum; });
var center = svg.append("circle")
.attr("r", radius / 3)
.on("click", zoomOut);
center.append("title")
.text("zoom out");
var partitioned_data=partition.nodes(root).slice(1)
var path = svg.selectAll("path")
.data(partitioned_data)
.enter().append("path")
.attr("d", arc)
.style("fill", function(d) { return d.fill; })
.each(function(d) { this._current = updateArc(d); })
.on("click", zoomIn)
.on("mouseover", mouseOverArc)
.on("mousemove", mouseMoveArc)
.on("mouseout", mouseOutArc);
var texts = svg.selectAll("text")
.data(partitioned_data)
.enter().append("text")
.filter(filter_min_arc_size_text)
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) { return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d,i) {return d.name})
function zoomIn(p) {
if (p.depth > 1) p = p.parent;
if (!p.children) return;
zoom(p, p);
}
function zoomOut(p) {
if (!p.parent) return;
zoom(p.parent, p);
}
// Zoom to the specified new root.
function zoom(root, p) {
if (document.documentElement.__transition__) return;
// Rescale outside angles to match the new layout.
var enterArc,
exitArc,
outsideAngle = d3.scale.linear().domain([0, 2 * Math.PI]);
function insideArc(d) {
return p.key > d.key
? {depth: d.depth - 1, x: 0, dx: 0} : p.key < d.key
? {depth: d.depth - 1, x: 2 * Math.PI, dx: 0}
: {depth: 0, x: 0, dx: 2 * Math.PI};
}
function outsideArc(d) {
return {depth: d.depth + 1, x: outsideAngle(d.x), dx: outsideAngle(d.x + d.dx) - outsideAngle(d.x)};
}
center.datum(root);
// When zooming in, arcs enter from the outside and exit to the inside.
// Entering outside arcs start from the old layout.
if (root === p) enterArc = outsideArc, exitArc = insideArc, outsideAngle.range([p.x, p.x + p.dx]);
var new_data=partition.nodes(root).slice(1)
path = path.data(new_data, function(d) { return d.key; });
// When zooming out, arcs enter from the inside and exit to the outside.
// Exiting outside arcs transition to the new layout.
if (root !== p) enterArc = insideArc, exitArc = outsideArc, outsideAngle.range([p.x, p.x + p.dx]);
d3.transition().duration(d3.event.altKey ? 7500 : 750).each(function() {
path.exit().transition()
.style("fill-opacity", function(d) { return d.depth === 1 + (root === p) ? 1 : 0; })
.attrTween("d", function(d) { return arcTween.call(this, exitArc(d)); })
.remove();
path.enter().append("path")
.style("fill-opacity", function(d) { return d.depth === 2 - (root === p) ? 1 : 0; })
.style("fill", function(d) { return d.fill; })
.on("click", zoomIn)
.on("mouseover", mouseOverArc)
.on("mousemove", mouseMoveArc)
.on("mouseout", mouseOutArc)
.each(function(d) { this._current = enterArc(d); });
path.transition()
.style("fill-opacity", 1)
.attrTween("d", function(d) { return arcTween.call(this, updateArc(d)); });
});
texts = texts.data(new_data, function(d) { return d.key; })
texts.exit()
.remove()
texts.enter()
.append("text")
texts.style("opacity", 0)
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) { return radius / 3 * d.depth; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.filter(filter_min_arc_size_text)
.text(function(d,i) {return d.name})
.transition().delay(750).style("opacity", 1)
}
});
function key(d) {
var k = [], p = d;
while (p.depth) k.push(p.name), p = p.parent;
return k.reverse().join(".");
}
function fill(d) {
var p = d;
while (p.depth > 1) p = p.parent;
var c = d3.lab(hue(p.name));
c.l = luminance(d.sum);
return c;
}
function arcTween(b) {
var i = d3.interpolate(this._current, b);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
function updateArc(d) {
return {depth: d.depth, x: d.x, dx: d.dx};
}
d3.select(self.frameElement).style("height", margin.top + margin.bottom + "px");
</script>
The problem I have is that it is only showing the right half of the Partition.

Why the attrTween doesn't work

I trying to mimic an example from Mike Bostock Extending Arcs. My code is very similar to Mike's, but mines doesn't work like his.
Here below is the JavaScript code:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Example</title>
<script src="static/d3.min.js"></script>
</head>
<body>
<script>
var Width = 500, Height = 400, innerRadius = 45, outerRadius = 100;
var colors = d3.scale.category10();
var svg = d3.select('body')
.append('svg')
.attr({ width: Width, height: Height });
var data = [40, 32, 35, 64, 83],
pieData = d3.layout.pie()(data)
var ArcGen = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
.padAngle(0.02)
.startAngle(function(d){
return d.startAngle;
})
.endAngle(function(d){
return d.endAngle;
});
var group = svg.append('g')
.attr('transform', 'translate(' + [Width / 2.0, Height / 2.0] + ')');
var segment = group.selectAll('g')
.data(pieData)
.enter()
.append('g');
segment
.append('path')
.attr('d', ArcGen)
.attr('fill', function(d,i){ return colors(i); })
.on('mouseover', arcTween(outerRadius * 1.2, 0))
.on('mouseout', arcTween(outerRadius, 150));
function arcTween(oRadius, delay){
// closure function
return function(){
d3.select(this)
.transition()
.delay(delay)
.attrTween('d', function(d){
var i = d3.interpolate(d.outerRadius, oRadius);
return function(t){
d.outerRadius = i(t);
return ArcGen(d);
}
})
};
};
</script>
</body>
</html>
This routine is simple, no error no action either.
I have two questions:
If it's necessary to chain startAngle and endAngle attributes at every arc generator? I read some pros' code, such as Mike Bostock's, don't add this two attributes at the initial stage and their codes work fine when constructing path elements.
Where am I wrong? is anybody can give me more attrTween examples.
Thanks, everyone!
Your code has 2 problems, that you can easily find comparing it with Bostock's code.
First, this line:
var i = d3.interpolate(d.outerRadius, oRadius);
Uses the property outerRadius in the element's datum. But it has none. You can fix this with:
.each(function(d) { d.outerRadius = outerRadius; })
Second, your arc generator is setting the outer radius:
var ArcGen = d3.svg.arc()
.innerRadius(innerRadius)
.outerRadius(outerRadius)
.padAngle(0.02)
.startAngle(function(d) {
return d.startAngle;
})
.endAngle(function(d) {
return d.endAngle;
});
Remove it:
var ArcGen = d3.svg.arc()
.innerRadius(innerRadius)
.padAngle(0.02)
.startAngle(function(d) {
return d.startAngle;
})
.endAngle(function(d) {
return d.endAngle;
});
Here is your working code with the 2 changes:
var Width = 500,
Height = 400,
innerRadius = 45,
outerRadius = 100;
var colors = d3.scale.category10();
var svg = d3.select('body')
.append('svg')
.attr({
width: Width,
height: Height
});
var data = [40, 32, 35, 64, 83],
pieData = d3.layout.pie()(data)
var ArcGen = d3.svg.arc()
.padAngle(0.02)
.innerRadius(innerRadius)
.startAngle(function(d) {
return d.startAngle;
})
.endAngle(function(d) {
return d.endAngle;
});
var group = svg.append('g')
.attr('transform', 'translate(' + [Width / 2.0, Height / 2.0] + ')');
var segment = group.selectAll('g')
.data(pieData)
.enter()
.append('g');
segment
.append('path')
.each(function(d) {
d.outerRadius = outerRadius;
})
.attr('d', ArcGen)
.attr('fill', function(d, i) {
return colors(i);
})
.on('mouseover', arcTween(outerRadius * 1.2, 0))
.on('mouseout', arcTween(outerRadius, 150));
function arcTween(oRadius, delay) {
// closure function
return function() {
d3.select(this)
.transition()
.delay(delay)
.attrTween('d', function(d) {
var i = d3.interpolate(d.outerRadius, oRadius);
return function(t) {
d.outerRadius = i(t);
return ArcGen(d);
}
})
};
};
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.js"></script>

Dynamically update D3 Sunburst if the source json is updated (item added or deleted)

I am new to D3 and trying to dynamically update the chart if the source json is modified. But I am not able to achieve this.
Please check this plunkr
Js:
var width = 500,
height = 500,
radius = Math.min(width, height) / 2;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.sqrt()
.range([0, radius]);
var color = d3.scale.category10();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ") rotate(-90 0 0)");
var partition = d3.layout.partition()
.value(function(d) {
return d.size;
});
var arc = d3.svg.arc()
.startAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function(d) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function(d) {
return Math.max(0, y(d.y));
})
.outerRadius(function(d) {
return Math.max(0, y(d.y + d.dy));
});
//d3.json("/d/4063550/flare.json", function(error, root) {
var root = initItems;
var g = svg.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var path = g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.on("click", click)
.each(function(d) {
this.x0 = d.x;
this.dx0 = d.dx;
});
//.append("text")
var text = g.append("text")
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.text(function(d) {
return d.name;
})
.style("fill", "white");
function computeTextRotation(d) {
var angle = x(d.x + d.dx / 2) - Math.PI / 2;
return angle / Math.PI * 180;
}
function click(d) {
console.log(d)
// fade out all text elements
if (d.size !== undefined) {
d.size += 100;
};
text.transition().attr("opacity", 0);
path.transition()
.duration(750)
.attrTween("d", arcTween(d))
.each("end", function(e, i) {
// check if the animated element's data e lies within the visible angle span given in d
if (e.x >= d.x && e.x < (d.x + d.dx)) {
// get a selection of the associated text element
var arcText = d3.select(this.parentNode).select("text");
// fade in the text element and recalculate positions
arcText.transition().duration(750)
.attr("opacity", 1)
.attr("transform", function() {
return "rotate(" + computeTextRotation(e) + ")"
})
.attr("x", function(d) {
return y(d.y);
});
}
});
} //});
// Word wrap!
var insertLinebreaks = function(t, d, width) {
alert(0)
var el = d3.select(t);
var p = d3.select(t.parentNode);
p.append("g")
.attr("x", function(d) {
return y(d.y);
})
// .attr("dx", "6") // margin
//.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
//p
.append("foreignObject")
.attr('x', -width / 2)
.attr("width", width)
.attr("height", 200)
.append("xhtml:p")
.attr('style', 'word-wrap: break-word; text-align:center;')
.html(d.name);
alert(1)
el.remove();
alert(2)
};
//g.selectAll("text")
// .each(function(d,i){ insertLinebreaks(this, d, 50 ); });
d3.select(self.frameElement).style("height", height + "px");
// Interpolate the scales!
function arcTween(d) {
var xd = d3.interpolate(x.domain(), [d.x, d.x + d.dx]),
yd = d3.interpolate(y.domain(), [d.y, 1]),
yr = d3.interpolate(y.range(), [d.y ? 20 : 0, radius]);
return function(d, i) {
return i ? function(t) {
return arc(d);
} : function(t) {
x.domain(xd(t));
y.domain(yd(t)).range(yr(t));
return arc(d);
};
};
}
function arcTweenUpdate(a) {
console.log(path);
var _self = this;
var i = d3.interpolate({ x: this.x0, dx: this.dx0 }, a);
return function(t) {
var b = i(t);
console.log(window);
_self.x0 = b.x;
_self.dx0 = b.dx;
return arc(b);
};
}
setTimeout(function() {
path.data(partition.nodes(newItems))
.transition()
.duration(750)
.attrTween("d", arcTweenUpdate)
}, 2000);
In addition to what #Cyril has suggested about removing the following line:
d3.select(self.frameElement).style("height", height + "px");
I made further modifications in your fiddle: working fiddle
The idea used here is to add a function updateChart which takes the items and then generate the chart:
var updateChart = function (items) {
// code to update the chart with new items
}
updateChart(initItems);
setTimeout(function () { updateChart(newItems); }, 2000);
This doesn't use the arcTweenUpdate function you have created but I will try to explain the underlying concept:
First, you will need to JOIN the new data with your existing data:
// DATA JOIN - Join new data with old elements, if any.
var gs = svg.selectAll("g").data(partition.nodes(root));
then, ENTER to create new elements if required:
// ENTER
var g = gs.enter().append("g").on("click", click);
But, we also need to UPDATE the existing/new path and text nodes with new data:
// UPDATE
var path = g.append("path");
gs.select('path')
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
//.on("click", click)
.each(function(d) {
this.x0 = d.x;
this.dx0 = d.dx;
})
.transition().duration(500)
.attr("d", arc);
var text = g.append("text");
gs.select('text')
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.text(function(d) {
return d.name;
})
.style("fill", "white");
and, after everything is created/updated remove the g nodes which are not being used i.e. EXIT:
// EXIT - Remove old elements as needed.
gs.exit().transition().duration(500).style("fill-opacity", 1e-6).remove();
This whole pattern of JOIN + ENTER + UPDATE + EXIT is demonstrated in following articles by Mike Bostock:
General Update Pattern - I
General Update Pattern - II
General Update Pattern - III
In side the fiddle the setTimeout is not running because:
d3.select(self.frameElement).style("height", height + "px");
You will get Uncaught SecurityError: Failed to read the 'frame' property from 'Window': Blocked a frame with origin "https://fiddle.jshell.net" from accessing a frame with origin and the setTimeout never gets called.
So you can remove this line d3.select(self.frameElement).style("height", height + "px"); just for the fiddle.
Apart from that:
Your timeout function should look like this:
setTimeout(function() {
//remove the old graph
svg.selectAll("*").remove();
root = newItems;
g = svg.selectAll("g")
.data(partition.nodes(newItems))
.enter().append("g");
/make path
path = g.append("path")
.attr("d", arc)
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.on("click", click)
.each(function(d) {
this.x0 = d.x;
this.dx0 = d.dx;
});
//make text
text = g.append("text")
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.text(function(d) {
return d.name;
})
.style("fill", "white");
}
working fiddle here
for the enter() and transitions to work you need to give d3 a way to identify each item in your data. the .data() function has a second parameter that lets you return something to use as an id. enter() will use the id to decide whether the object is new.
try changing
path.data(partition.nodes(newItems))
.data(partition.nodes(root));
to
path.data(partition.nodes(newItems), function(d){return d.name});
.data(partition.nodes(root), function(d){return d.name});

D3 Pie Chart Update, IV in while loop not working well - why?

I work for the first time with the D3 library and basically also with javascript and i rly need some help.
I want to create this beautiful pie chart in a while loop when the mysql-query is true. It works. It makes three pie charts and the associated forms with the radio inputs. But after that, only the last pie chart works when I want to switch the radio buttons value. The other two pie charts dont switch their value when I activate the unselected radio button.
What should i do?
<?php
$gem_stud_data = $db->prepare("SELECT * FROM ergebnisse WHERE studiengang_1 <> 0 AND studiengang_2 <> 0 AND semester_id=1");
$gem_stud_data->execute();
if($_GET["id"] == 1){
while($gem_stud_datas = $gem_stud_data->fetch()){
$stud1_data = $db->prepare("SELECT * FROM studiengaenge WHERE id=:eins");
$stud1_data->bindParam(":eins", $gem_stud_datas["studiengang_1"]);
$stud1_data->execute();
$stud1_datas = $stud1_data->fetch();
$stud2_data = $db->prepare("SELECT * FROM studiengaenge WHERE id=:eins");
$stud2_data->bindParam(":eins", $gem_stud_datas["studiengang_2"]);
$stud2_data->execute();
$stud2_datas = $stud2_data->fetch();
?>
<form>
<label><input type="radio" name="dataset" value="stud_1" checked> <?php echo $stud1_datas["kuerzel"]; ?></label>
<label><input type="radio" name="dataset" value="stud_2"> <?php echo $stud2_datas["kuerzel"]; ?></label>
</form>
<script>
var dataset = {
stud_1: [53245, 28479, 19697, 24037, 40245],
stud_2: [200, 200, 200, 200]
};
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var enterClockwise = {
startAngle: 0,
endAngle: 0
};
var enterAntiClockwise = {
startAngle: Math.PI * 2,
endAngle: Math.PI * 2
};
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 20);
var svg = d3.select("main").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// set the start and end angles to 0 so we can transition
// clockwise to the actual values later
var path = svg.selectAll("path")
.data(pie(dataset.stud_1))
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc(enterClockwise))
.each(function(d) {
this._current = {
data: d.data,
value: d.value,
startAngle: enterClockwise.startAngle,
endAngle: enterClockwise.endAngle
}
}); // store the initial values
path.transition() // update
.duration(750)
.attrTween("d", arcTween);
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"stud_2\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
path = path.data(pie(dataset[this.value])); // update the data
// set the start and end angles to Math.PI * 2 so we can transition
// anticlockwise to the actual values later
path.enter().append("path")
.attr("fill", function (d, i) {
return color(i);
})
.attr("d", arc(enterAntiClockwise))
.each(function (d) {
this._current = {
data: d.data,
value: d.value,
startAngle: enterAntiClockwise.startAngle,
endAngle: enterAntiClockwise.endAngle
};
}); // store the initial values
path.exit()
.transition()
.duration(750)
.attrTween('d', arcTweenOut)
.remove() // now remove the exiting arcs
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}
// Store the displayed angles in _current.
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
function arcTweenOut(a) {
var i = d3.interpolate(this._current, {startAngle: Math.PI * 2, endAngle: Math.PI * 2, value: 0});
this._current = i(0);
return function (t) {
return arc(i(t));
};
}
</script>
<?php
}
} ?>
I really hope you can help me! I am very frustrated :(
I thank you in advance!
Please see below, this works for me and changes the data on the timeout you created.
<form>
<label><input type="radio" name="dataset" value="stud_1" checked> Option 1 </label>
<label><input type="radio" name="dataset" value="stud_2"> Option 2 </label>
</form>
<script>
var dataset = {
stud_1: [53245, 28479, 19697, 24037, 40245],
stud_2: [200, 200, 200, 200]
};
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var enterClockwise = {
startAngle: 0,
endAngle: 0
};
var enterAntiClockwise = {
startAngle: Math.PI * 2,
endAngle: Math.PI * 2
};
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 20);
var svg = d3.select("#main").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// set the start and end angles to 0 so we can transition
// clockwise to the actual values later
var path = svg.selectAll("path")
.data(pie(dataset.stud_1))
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc(enterClockwise))
.each(function(d) {
this._current = {
data: d.data,
value: d.value,
startAngle: enterClockwise.startAngle,
endAngle: enterClockwise.endAngle
}
}); // store the initial values
path.transition() // update
.duration(750)
.attrTween("d", arcTween);
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"stud_2\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
path = path.data(pie(dataset[this.value])); // update the data
// set the start and end angles to Math.PI * 2 so we can transition
// anticlockwise to the actual values later
path.enter().append("path")
.attr("fill", function (d, i) {
return color(i);
})
.attr("d", arc(enterAntiClockwise))
.each(function (d) {
this._current = {
data: d.data,
value: d.value,
startAngle: enterAntiClockwise.startAngle,
endAngle: enterAntiClockwise.endAngle
};
}); // store the initial values
path.exit()
.transition()
.duration(750)
.attrTween('d', arcTweenOut)
.remove() // now remove the exiting arcs
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}
// Store the displayed angles in _current.
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
function arcTweenOut(a) {
var i = d3.interpolate(this._current, {startAngle: Math.PI * 2, endAngle: Math.PI * 2, value: 0});
this._current = i(0);
return function (t) {
return arc(i(t));
};
}
</script>
See revised solution ... there you must grab all svgs and then select the paths and then append data ... see change in the change function.
<form>
<label><input type="radio" name="dataset" value="stud_1" checked> Option 1 </label>
<label><input type="radio" name="dataset" value="stud_2"> Option 2 </label>
</form>
<script>
var dataset = {
stud_1: [53245, 28479, 19697, 24037, 40245],
stud_2: [200, 200, 200, 200]
};
var width = 960,
height = 500,
radius = Math.min(width, height) / 2;
var enterClockwise = {
startAngle: 0,
endAngle: 0
};
var enterAntiClockwise = {
startAngle: Math.PI * 2,
endAngle: Math.PI * 2
};
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null);
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 20);
var svg = d3.select("#main").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 3 + "," + height / 3 + ")");
var svg2 = d3.select("#main2").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + (width / 3 * 2) + "," + (height / 3 * 2) + ")");
var svg3 = d3.select("#main3").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + (width / 3 * 2) + "," + (height / 3 * 2)+ ")");
// set the start and end angles to 0 so we can transition
// clockwise to the actual values later
var path = d3.selectAll('svg').selectAll("path")
.data(pie(dataset.stud_1))
.enter().append("path")
.attr("fill", function(d, i) { return color(i); })
.attr("d", arc(enterClockwise))
.each(function(d) {
this._current = {
data: d.data,
value: d.value,
startAngle: enterClockwise.startAngle,
endAngle: enterClockwise.endAngle
}
}); // store the initial values
path.transition() // update
.duration(750)
.attrTween("d", arcTween);
d3.selectAll("input").on("change", change);
var timeout = setTimeout(function() {
d3.select("input[value=\"stud_2\"]").property("checked", true).each(change);
}, 2000);
function change() {
clearTimeout(timeout);
path = d3.selectAll('svg').selectAll('path').data(pie(dataset[this.value])); // update the data
// set the start and end angles to Math.PI * 2 so we can transition
// anticlockwise to the actual values later
path.enter().append("path")
.attr("fill", function (d, i) {
return color(i);
})
.attr("d", arc(enterAntiClockwise))
.each(function (d) {
this._current = {
data: d.data,
value: d.value,
startAngle: enterAntiClockwise.startAngle,
endAngle: enterAntiClockwise.endAngle
};
}); // store the initial values
path.exit()
.transition()
.duration(750)
.attrTween('d', arcTweenOut)
.remove() // now remove the exiting arcs
path.transition().duration(750).attrTween("d", arcTween); // redraw the arcs
}
// Store the displayed angles in _current.
// Then, interpolate from _current to the new angles.
// During the transition, _current is updated in-place by d3.interpolate.
function arcTween(a) {
var i = d3.interpolate(this._current, a);
this._current = i(0);
return function(t) {
return arc(i(t));
};
}
function arcTweenOut(a) {
var i = d3.interpolate(this._current, {startAngle: Math.PI * 2, endAngle: Math.PI * 2, value: 0});
this._current = i(0);
return function (t) {
return arc(i(t));
};
}
</script>

Categories