Drill down pie chart disappears - d3.js

I make a drill down pie chart and it works well.
You can see it here.
But there is an issue:
1) click a node then it shows its children (level 1) or level 0 nodes.
2) Move mouse to another position and move back to the new node which contains its original position, the node disappears.
I think there is problem in this code (gradientPie.js)
var paths = gPie.selectAll("path").data(pieChart(currData), function(d) {return d.data.cat;});
var texts = gPie.selectAll("text").data(pieChart(currData), function(d) {return d.data.cat;});
var lines = gPie.selectAll("line").data(pieChart(currData), function(d) {return d.data.cat;});
var arcs = paths.enter().append("g").attr('class', 'slice');
arcs.append("path").attr("fill", function(d, i) { return "url(#gradient" + d.data.cat + ")"; })
.transition().duration(1000).attrTween("d", tweenIn).each("end", function(){
this._listenToEvents = true;
gradPie.transitioning = false;
})
.attr("id", function(d, i){return 'p' + i;})
.each(function(d) { this._current = d; });
arcs.append("text").attr("transform", function(d) {
var c = d3.svg.arc().outerRadius(radius * 1.4).innerRadius(radius).centroid(d);
return "translate(" + (0 + c[0]) + "," + (0 + c[1]) + ")";
})
.attr("dy", ".35em")
.attr("class", "text-main")
.style("text-anchor", "middle")
.style("fill", "#3f5763")
.style("font", "bold 14px Helvetica")
.text(function(d) {
$("#" + d.data.domID + " p").html(d.data.percent + "%");
return d.data.percent + "%";
});
arcs.append("line").attr("transform", function (d, i) {
var rAngle = ((d.startAngle + d.endAngle) / 2 - (Math.PI / 2)) * 180 / Math.PI;
return "rotate(" + rAngle + ")translate(" + radius * 1.1 + ")";
})
.attr("class", "line-ticks")
.attr('stroke-width', '1')
.attr("x2", -0.5 * radius)
.style("stroke", "#3f5763")
.style("fill", "none");
// Mouse interaction handling
paths.on("click", function(d, i){
if(this.childNodes[0]._listenToEvents && !gradPie.transitioning){
// Reset inmediatelly
d3.select(this).attr("transform", "translate(0,0)")
// Change level on click if no transition has started
paths.each(function(){
this.childNodes[0]._listenToEvents = false;
});
updateGraph(d.data.subfractions? d.data.cat : undefined);
}
})
.on("mouseover", function(d, i){
// Mouseover effect if no transition has started
if(this.childNodes[0]._listenToEvents && !gradPie.transitioning) {
// Calculate angle bisector
var ang = (d.endAngle + d.startAngle)/2;
// Transformate to SVG space
ang = (ang - (Math.PI / 2) ) * -1;
// Calculate a 10% radius displacement
var x = Math.cos(ang) * radius * 0.1;
var y = Math.sin(ang) * radius * -0.1;
d3.select(this).transition()
.duration(250).attr("transform", function() {
return "translate(" + x + ", " + y + ")";
})
}
})
.on("mouseout", function(d){
// Mouseout effect if no transition has started
if(this.childNodes[0]._listenToEvents && !gradPie.transitioning){
d3.select(this).transition()
.duration(150).attr("transform", function() {
return "translate(0,0)";
});
}
});
// Collapse sectors for the exit selection
paths.exit().transition()
.duration(1000)
.attrTween("d", tweenOut).remove();
texts.exit().transition()
.duration(100).remove();
lines.exit().transition()
.duration(100).remove();
Any help?

Related

D3.js link two rectangle which size fits the text

As a follow up question of D3.js change width of container after it is drawn I create the rectangles that fits the text length, I want to link the rectangles from bottom. But I'm stuck in getting the width of rectangle when I draw the link.
This is the js code:
var rectW = 140, rectH = 40;
// Declare the nodes.
var node = draw.selectAll('g.node')
.data(nodes, function(d) { return d.id; });
// Enter the nodes.
var nodeLabel = node.enter().append('g')
.attr('transform', function(d) { return 'translate(' + source.x0 + ',' + source.y0 + ')'; });
var nodeRect = nodeLabel.append('rect')
.attr('width', rectW)
.attr('height', rectH);
var nodeText = nodeLabel.append('text')
.attr('x', rectW / 2)
.attr('y', rectH / 2)
.text(function (d) { return d.name; });
// This arranges the width of the rectangles
nodeRect.attr("width", function() {
return this.nextSibling.getComputedTextLength() + 20;
})
// This repositions texts to be at the center of the rectangle
nodeText.attr('x', function() {
return (this.getComputedTextLength() + 20) /2;
})
Next,I'd like to link the nodeRects. Linking the top left corner is ugly, so I adjust a bit:
link.attr('d', function (d) {
var sourceX = d.source.x + 0.5*d.source.getComputedTextlength() + 10,
sourceY = (d.source.y > d.target.y)? d.source.y: (d.source.y + rectH),
targetX = d.target.x + 0.5*d.target.getComputedTextlength() +10,
targetY = (d.source.y >= d.target.y)? (d.target.y + rectH) : d.target.y;
It returns error. Is there a way that I can get access to the target rect and source rect's textlength or width?
I find an answer by myself. d.source.width doesn't work because it is not defined.
Change
nodeRect.attr("width", function() {
return this.nextSibling.getComputedTextLength() + 20;
})
to
nodeRect.attr("width", function(d) {
d.width = this.nextSibling.getComputedTextLength() + 20;
return d.width;
})
Then use d.source.width works well.

Highlight path in d3 tree is not working in IE

I created tree layout as in JSFiddle example http://jsfiddle.net/oo66o0q0/15/.
Requirement is path should highlighted in red and with extra width when user click on node's right click menu "Highlight Route" option.
This is working in chrome correctly but in IE highlighted route color becomes black.
If I remove markers then it works in IE as well.
How to resolve this issue in IE but not removing markers?
function treeInitialize(graphData){
diagramLayout = d3.select("#diagramLayout")
.attr("id", "diagramLayout")//set id
.attr("width", width)//set width
.attr("height", height)//set height
.append("g")
.attr("transform", "translate(" + 20 + "," + 20 + ")")
markerRefx = 40;
var data2 = graphData.links.filter(function(l){
if(l.target == undefined && l.source == undefined){
return false;
}else{
return true;
}
});
data2.push(JSON.parse('{"target":"glossforArrow","source":""}'))
var treeData = d3.stratify().id(function(d){ return d.target; }).parentId(function(d) {
return d.source;
})(data2)
nodes = d3.hierarchy(treeData, function(d) {return d.children;});
var levelWidth = [1];
var childCount = function(level, n) {
if(n.children && n.children.length > 0) {
if(levelWidth.length <= level + 1) levelWidth.push(0);
levelWidth[level+1] += n.children.length;
n.children.forEach(function(d) {
childCount(level + 1, d);
});
}
};
childCount(0, nodes);
newHeight = d3.max(levelWidth) * 100;
var tree = d3.tree().size([height, width])
tree.size([newHeight,height/2]);
tree.separation(function (a, b) {
return a.parent == b.parent ? 50 : 100;
});
nodes = tree(nodes);
treeLayout(nodes);
function treeLayout(nodes){
var node = diagramLayout.selectAll(".node");
node = node.data(nodes.descendants());
var link = diagramLayout.selectAll(".link")
.data(nodes.descendants().slice(1))
.enter().append("path")
.attr("class", "link")
.attr("fill", "none")
.attr("stroke", "#000")
.attr("stroke-width", "1px")
.attr("stroke-opacity", "0.3")
.attr("d",connector)
nodes.descendants().slice(1).forEach(function(d) {
var mark = diagramLayout.append("svg:defs").selectAll("marker")//
.data(["start"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", -markerRefx)
.attr("refY", 0)
.attr("markerWidth", 5)
.attr("markerHeight", 5)
.attr("orient", "auto")
.attr("stroke", "#000")
.attr("fill", "#000")
.append("svg:path")
.attr("d", "M0,-5L10,0L0,5")
.style("stroke-width", "0.3px")
.attr("transform","rotate(180,5, 0)");
});
link.attr("marker-start", "url(#start)")
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("height", nodeHeight)
.attr("width", nodeWidth)
nodeEnter.attr("transform", function(d) {
return "translate(" + project(d.x, d.y) + ")";
}).on('contextmenu', menuCall);
var nodeIcon = nodeEnter.append("rect")
.attr("class", "rect")
.attr("x", -20)
.attr("y", -20)
.attr("rx", 10)
.attr("width", 40)
.attr("height", 40)
.attr("stroke-width", function(d) { return Math.sqrt(2); })
.attr("stroke-opacity", "0.3")
.attr("stroke", "#000" )
.attr("fill", "blue" )
//wrap(nodeText, 8)
}
}
function connector(d) {
return "M" + project(d.x, d.y)
+ "C" + project(d.x, (d.y + d.parent.y) / 2)
+ " " + project(d.parent.x, (d.y + d.parent.y) / 2)
+ " " + project(d.parent.x, d.parent.y);
}
function project(x, y) {
return [x,y];
}
function menuCall(di,i) {
var nodeClicked = d3.select(this);
var menuitems = ["Highlight route"];
d3.selectAll('.context-menu').data([1])
.enter()
.append('div')
.attr('class', 'context-menu');
// close menu
d3.select('body').on('click.context-menu', function() {
d3.select('.context-menu').style('display', 'none');
});
// this gets executed when a contextmenu event occurs
d3.selectAll('.context-menu')
.html('')
.append('ul')
.selectAll('li')
.data(menuitems).enter()
.append('li')
.on('click' , function(d) {
// d3.select('.context-menu').style('display', 'none');
if(d=="Highlight route"){
var id = nodeClicked.datum().data.id;
var node = diagramLayout.selectAll(".node");
var link = diagramLayout.selectAll(".link");
link.style("stroke","black").style("stroke-width", "1.5px")
var linkSelected = link.filter(function (d, i) {
console.log(d.data.id)
console.log(id)
return d.data.id === id;});
linkSelected.style("stroke", "red").style("stroke-width", "5px");
}
d3.select('.context-menu').style('display', 'none');
}).text(function(di) { return di; });
d3.select('.context-menu').style('display', 'none');
// show the context menu
d3.select('.context-menu')
.style('left', (d3.event.pageX - 2) + 'px')
.style('top', (d3.event.pageY - 2) + 'px')
.style('display', 'block');
d3.event.preventDefault();
}
Looks like a bug in IE. If you inspect the DOM after you apply the highlight, IE reports that the inline style has overridden the sheet style (as it should), but the path does not update:
The only fix I can think us is to remove the sheet stroke style on class link
.link {
stroke-opacity: .6;
}
And just apply all the styles in-line.
Updated fiddle.

How to separate different "gravitational fields" in D3?

I have a force enabled SVG visualisation where I want smaller circles to be attracted to bigger circles. This attraction works by calculating the elements' centre point and change it in iterations for every "tick" in the visualisation, to keep the items from going over the centre of the nodes I use a function to change the charge of the items depending on their size.
I used Mike's code here as a basis: http://mbostock.github.io/d3/talk/20110921/#14
My problem comes here - it seems like the bigger circles are affecting each others "gravitational fields" - is there a way I can separate them from eachother?
Force layout setup:
var w = 1280,
h = 800,
color = d3.scale.category10();
var force = d3.layout.force()
.gravity(0.0)
.charge(function(d){
return -10 * d.r;
})
.size([w, h]);
Element drawing:
var g = svg.selectAll("g.node")
.data(nodes)
.enter().append("svg:g")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
})
;
g.append("svg:circle")
.attr("r", 40)
.attr("transform", function(d) { return "translate(" + 0 + ","+ 0 + ")"; })
.style("fill", fill)
.call(force.drag);
g.append("svg:text")
.attr("x", 0)
.attr("dy", ".31em")
.attr("text-anchor", "middle")
.text(function(d) {
return d.label;
});
Animation loop:
force.on("tick", function(e) {
var k = e.alpha * 0.5;
nodes.forEach(function(node) {
var center = nodes[node.type];
dx = center.x - node.x;
dy = center.y - node.y;
node.x += dx * k;
node.y += dy * k;
});
svg.selectAll(".circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
});
Adding smaller circles:
svg.on("mousemove", function() {
var p1 = d3.svg.mouse(this),
node = {
type: Math.random() * 3 | 0,
x: p1[0],
y: p1[1],
r: 1.5,
px: (p0 || (p0 = p1))[0],
py: p0[1]
};
p0 = p1;
svg.append("svg:circle")
.attr("class", "circle")
.data([node])
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", 4.5)
.style("fill", fill);
nodes.push(node);
force.start();
});

cannot show tooltip on rotating cluster layout

I have not been able to show the tooltip for this graph:
http://mbostock.github.io/d3/talk/20111018/cluster.html
I have tried the simplest way without success:
node.append("svg:title").text(function(d) { return d.key; });
And also tried this without success:
var div = d3.select("body").append("div").attr("class", "tooltip")
.style("opacity", 0);
node.append("svg:circle")
.attr("r", 3)
.on("mouseover", function (d) {
div.transition()
.duration(0)
div.style("opacity", 1)
div.html(d.key)
.style("width", d.key.length + "mm")
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function (d) {
div.transition()
.duration(0)
.style("opacity", 0);
});
Here is the complete code:
var w = 1280,
h = 800,
rx = w / 2,
ry = h / 2,
m0,
rotate = 0;
var cluster = d3.layout.cluster()
.size([360, ry - 120])
.sort(null);
var diagonal = d3.svg.diagonal.radial()
.projection(function (d) {
return [d.y, d.x / 180 * Math.PI];
});
var svg = d3.select("body").append("div")
.style("width", w + "px")
.style("height", w + "px");
var vis = svg.append("svg:svg")
.attr("width", w)
.attr("height", w)
.append("svg:g")
.attr("transform", "translate(" + rx + "," + ry + ")");
vis.append("svg:path")
.attr("class", "arc")
.attr("d", d3.svg.arc().innerRadius(ry - 120).outerRadius(ry).startAngle(0).endAngle(2 * Math.PI))
.on("mousedown", mousedown);
var div = d3.select("body").append("div").attr("class", "tooltip")
.style("opacity", 0);
function drawNodes(graph) {
var nodes = cluster.nodes(graph);
var link = vis.selectAll("path.link")
.data(cluster.links(nodes))
.enter().append("svg:path")
.attr("class", "link")
.attr("d", diagonal);
var node = vis.selectAll("g.node")
.data(nodes)
.enter().append("svg:g")
.attr("class", "node")
.attr("transform", function (d) {
return "rotate(" + (d.x - 90) + ")translate(" + d.y + ")";
})
node.append("svg:circle")
.attr("r", 3)
.on("mouseover", function (d) {
div.transition()
.duration(0)
div.style("opacity", 1)
div.html(/*"info:" + "<br/>" + */d.key)
.style("width", d.key.length + "mm")
//.style("height", "10mm") ???
.style("left", (d3.event.pageX) + "px")
.style("top", (d3.event.pageY - 28) + "px");
})
.on("mouseout", function (d) {
div.transition()
.duration(0)
.style("opacity", 0);
});
node.append("svg:text")
.attr("dx", function (d) {
return d.x < 180 ? 8 : -8;
})
.attr("dy", ".31em")
.attr("text-anchor", function (d) {
return d.x < 180 ? "start" : "end";
})
.attr("transform", function (d) {
return d.x < 180 ? null : "rotate(180)";
})
.text(function (d) {
return d.name;
});
// TODO: does not work...not sure why...mouseover being captured already?
node.append("svg:title")
.text(function(d) { return d.key; });
}
//});
d3.select(window)
.on("mousemove", mousemove)
.on("mouseup", mouseup);
function mouse(e) {
return [e.pageX - rx, e.pageY - ry];
}
function mousedown() {
m0 = mouse(d3.event);
d3.event.preventDefault();
}
function mousemove() {
if (m0) {
var m1 = mouse(d3.event),
dm = Math.atan2(cross(m0, m1), dot(m0, m1)) * 180 / Math.PI,
tx = "translate3d(0," + (ry - rx) + "px,0)rotate3d(0,0,0," + dm + "deg)translate3d(0," + (rx - ry) + "px,0)";
svg.style("-moz-transform", tx)
.style("-ms-transform", tx)
.style("-webkit-transform", tx);
}
}
function mouseup() {
if (m0) {
var m1 = mouse(d3.event),
dm = Math.atan2(cross(m0, m1), dot(m0, m1)) * 180 / Math.PI,
tx = "rotate3d(0,0,0,0deg)";
rotate += dm;
if (rotate > 360) rotate -= 360;
else if (rotate < 0) rotate += 360;
m0 = null;
svg.style("-moz-transform", tx)
.style("-ms-transform", tx)
.style("-webkit-transform", tx);
vis.attr("transform", "translate(" + rx + "," + ry + ")rotate(" + rotate + ")")
.selectAll("g.node text")
.attr("dx", function (d) {
return (d.x + rotate) % 360 < 180 ? 8 : -8;
})
.attr("text-anchor", function (d) {
return (d.x + rotate) % 360 < 180 ? "start" : "end";
})
.attr("transform", function (d) {
return (d.x + rotate) % 360 < 180 ? null : "rotate(180)";
});
}
}
function cross(a, b) {
return a[0] * b[1] - a[1] * b[0];
}
function dot(a, b) {
return a[0] * b[0] + a[1] * b[1];
}
The CSS of the example is preventing the nodes from capturing the pointer and thus from showing the tooltips. If you remove the line
pointer-events: none;
from the CSS for .node, it works fine.

Rotating text paths in d3.js chord diagram without usual svg:text

I am trying to adapt this Chord diagram by Mike Bostock:
I want to have the text labels rotating outwards like this chord diagram:
http://bost.ocks.org/mike/uberdata/
There is an example here
http://bl.ocks.org/mbostock/910126
However, the transformation is done using the svg:text :
g.append("svg:text")
.each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("dy", ".35em")
.attr("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + (r0 + 26) + ")"
+ (d.angle > Math.PI ? "rotate(180)" : "");
})
.text(function(d) { return nameByIndex[d.index]; });
The one I am trying to adapt uses "text" and "textPath" and I don't seem to be able to simply add a transform/rotate attribute. Adding this line
.attr("transform",function(d,i){return "rotate(90)";})
to the code below does nothing:
// Add a text label.
var groupText = group.append("text")
.attr("x", 6)
.attr("dy", 15);
groupText.append("textPath")
.attr("xlink:href", function(d, i) { return "#group" + i; })
.text(function(d, i) { return cities[i].country; });
Any ideas how I can rotate the text outwards so smaller chord groups text labels can be displayed without being bunched up or (as is the orginal solution) turned off altogether?
I think you are looking for this example by Mike Bostock
As far as modifying the initial chord code, the following change should do what you want:
// Add a text label.
// var groupText = group.append("text")
// .attr("x", 6)
// .attr("dy", 15);
//groupText.append("textPath")
// .attr("xlink:href", function(d, i) { return "#group" + i; })
// .text(function(d, i) { return cities[i].name; });
// Remove the labels that don't fit. :(
//groupText.filter(function(d, i) { return groupPath[0][i].getTotalLength() / 2 - 16 < this.getComputedTextLength(); })
// .remove();
group.append("text")
.each(function(d) { d.angle = (d.startAngle + d.endAngle) / 2; })
.attr("dy", ".35em")
.attr("transform", function(d) {
return "rotate(" + (d.angle * 180 / Math.PI - 90) + ")"
+ "translate(" + (innerRadius + 26) + ")"
+ (d.angle > Math.PI ? "rotate(180)" : "");
})
.style("text-anchor", function(d) { return d.angle > Math.PI ? "end" : null; })
.text(function(d, i) { return cities[i].name; });

Resources