Is it possible to create a sunburst that is shaped as a semicircle? I was able to give it the shape that I want, but I have problems with the nodes. Not all the colors appear on the new shape and the text is displayed wrong.
var partition = d3.layout.partition()
.sort(null)
.value(function(d) { return d.size; });
var arc = d3.svg.arc()
.startAngle(function(d) { return Math.max(0, Math.min(Math.PI/360, x(d.x))); })
.endAngle(function(d) { return Math.max(0, Math.min(Math.PI, x(d.x + d.dx))); })
.innerRadius(function(d) { return Math.max(0, d.y ? y(d.y) : d.y); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
var nodes = partition.nodes(json);
var path = vis.selectAll("path").data(nodes);
path.enter().append("path")
.attr("id", function(d, i) { return "path-" + i; })
.attr("d", arc)
.attr("fill-rule", "evenodd")
.attr("fill", function(d) { return color((d.children ? d : d.parent).name); })
.on("click", function(d,i) {
//This is a mimic of the selection in the Tree List Box - picking all the parents of the selected cell
_this.Data.SearchColumn(0, "*" + d.name + "*",false);
});
var text = vis.selectAll("text").data(nodes);
var textEnter = text.enter().append("text")
.style("opacity", 1)
.style("fill","#333")
.attr("text-anchor", function(d) {
return x(d.x + d.dx / 2) > Math.PI ? "end" : "start";
})
.attr("dy", ".2em")
.attr("transform", function(d) {
var multiline = (d.name || "").split(" ").length > 1,
angle = x(d.x + d.dx / 2) * 180 / Math.PI -90,
rotate = angle + (multiline ? -.5 : 0);
return "rotate(" + rotate + ")translate(" + (y(d.y) + p) + ")rotate(" + (angle > 90 ? -180 : 0) + ")";
})
.on("click", function(d,i) {
_this.Data.SearchColumn(0, "*" + d.name + "*",false);
});
textEnter.append("tspan")
.attr("x", 0)
.text(function(d) { return d.depth ? d.name.split(" ")[0] : ""; });
textEnter.append("tspan")
.attr("x", 0)
.attr("dy", "1em")
.text(function(d) { return d.depth ? d.name.split(" ")[1] || "" : ""; });
textEnter.append("tspan")
.text(" ");
textEnter.append("tspan")
.text(function(d) { return d.depth ? d.name.split(" ")[2] || "" : ""; } );
textEnter.append("tspan")
.text(" ");
textEnter.append("tspan")
.text(function(d) { return d.depth ? d.name.split(" ")[3] || "" : ""; } );
I've solved the problem. This is the code I used:
var w = Math.min(heightc,widthc),
h = w/2,
r = w / 2-20,
x = d3.scale.linear().range([0, 1* Math.PI]),
y = d3.scale.pow().exponent(1.3).domain([0, 1]).range([0, r]),
p = 4,
color = d3.scale.category20c(),
duration = 1000;
var div = d3.select("#"+myDivId);
div.select("img").remove();
var vis = div.append("svg")
.attr("width", w + p * 2)
.attr("height", h + p * 2)
.append("g")
.attr("transform", "translate(" + (p) + "," + (r + p) + ")");
var partition = d3.layout.partition()
.sort(null)
.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, d.y ? y(d.y) : d.y); })
.outerRadius(function(d) { return Math.max(0, y(d.y + d.dy)); });
And the rest of the code remained the same.
Related
I have made a map which shows some circles and on click, the lines are shown. The map is overlayed on a leaflet basemap.
The map works fine. Here is how I projected the map:
d3.csv("going.csv", function(data) {
going = data;
d3.json("lor_migration.geojson", function(json) {
//Projection
transform = d3.geo.transform({
point: projectPoint
});
path = d3.geo.path().projection(transform);
function projectPoint(x, y) {
var point = map.latLngToLayerPoint(new L.LatLng(y, x));
this.stream.point(point.x, point.y);
}
//Bind data and create one path per GeoJSON feature
var feature = g.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("id", function(d) {
return d.properties.state;
})
.attr("d", path)
.attr("stroke-width", 0.5)
.attr("fill-opacity", "0.5")
.style("stroke", "#666")
.style("fill", "#fff");
map.on("viewreset", reset);
reset();
//Reset function
function reset() {
var bounds = path.bounds(json);
topLeft = bounds[0],
bottomRight = bounds[1];
svg.attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");
g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
feature.attr("d", path);
}
Here is how the circles are drawn on the map:
//Create circles for net migration
var circles = g.selectAll("circle")
.data(json.features)
.enter().append("circle")
.attr("cx", function(d) {
d.centroid = path.centroid(d);
return d.centroid[0];
})
.attr("cy", function(d) {
return d.centroid[1];
})
.attr("r", function(d) { //circle size
console.log("Value: " + value);
var diff = d.properties.total_imm - d.properties.total_emm;
return circleSize((Math.abs(diff)/Math.PI));
})
.attr("class", "circ")
.attr("id", function(d) {
return d.abbrev;
})
.attr("fill", function(d) { //circle color
var diff = d.properties.total_imm - d.properties.total_emm;
if (diff > 0) {
return "#65a89d";
}
else {
return "#a96a46";
}
})
.attr("fill-opacity", "0.5")
.attr("stroke", "#fff")
.attr("stroke-weight", "0.5")
.on("mouseover", function(d) {
return toolOver(d, this);
})
.on("mousemove", function(d) {
var m = d3.mouse(this);
mx = m[0];
my = m[1];
return toolMove(mx, my, d);
})
.on("mouseout", function(d) {
return toolOut(d, this);
})
.on("click", function(d) {
clicked(d)
});
Here is the onclick function for the lines:
function clicked(selected) {
var selname = selected.id;
var homex = selected.centroid[0];
var homey = selected.centroid[1];
g.selectAll(".goingline")
.attr("stroke-dasharray", 0)
.remove()
g.selectAll(".goingline")
.data(going)
.enter().append("path")
.attr("class", "goingline")
.attr("d", function(d, i) {
var finalval = coming[i][selname] - going[i][selname];
come = coming[i][selname];
go = going[i][selname];
try {
var theState = d3.select("#" + d.abbrev).datum();
if (!isNaN(finalval)) {
var startx = theState.centroid[0];
var starty = theState.centroid[1];
if (finalval > 0)
return "M" + startx + "," + starty + " Q" + (startx + homex) / 2 + " " + (starty + homey) / 1.5 + " " + homex + " " + homey;
else
return "M" + homex + "," + homey + " Q" + (startx + homex) / 2 + " " + (starty + homey) / 5 + " " + startx + " " + starty;
}
}
catch (err) {
console.log(err)
console.log('No datum found for ' + d.abbrev)
}
})
.call(transition)
.attr("stroke-width", function(d, i) {
var finalval = coming[i][selname] - going[i][selname];
return 0.75//(Math.abs(finalval));
})
.attr("stroke", function(d, i) {
var finalval = coming[i][selname] - going[i][selname];
if (finalval > 0) {
return "#65a89d";
}
else {
return "#a96a46";
}
})
.attr("fill", "none")
.attr("opacity", 0.5)
.attr("stroke-linecap", "round")
.on("mouseover", function(d) {
return toolOver2(d, this);
})
.on("mousemove", function(d, i) {
var m = d3.mouse(this);
mx = m[0];
my = m[1];
return toolMove2(mx, my, selname, d.state, coming[i][selname], going[i][selname]);
})
.on("mouseout", function(d) {
return toolOut2(d, this);
});
}
The problem is the projection does not work for circles and the lines. Can you please suggest a solution.
Thanks
EDITED:
So this is an attempt I made on trying to fix the issue: There are still some issues
//Create SVG element
var svg = d3.select(map.getPanes().overlayPane)
.append("svg")
var g = svg.append("g").attr("class", "leaflet-zoom-hide");
var svgCircles = svg.append("g").attr("class", "leaflet-zoom-hide");
var coming, going;
var by_name = {};
//Define the coming and going of csv
d3.csv("coming.csv", function(data) {
coming = data;
});
d3.csv("going.csv", function(data) {
going = data;
d3.json("lor_migration.geojson", function(json) {
//Projection
transform = d3.geo.transform({
point: projectPoint
});
path = d3.geo.path().projection(transform);
/* json.features.forEach(function(d) {
d.LatLng = new L.LatLng(d.geometry.coordinates[1], d.geometry.coordinates[0]);
});*/
function projectPoint(x, y) {
var point = map.latLngToLayerPoint(new L.LatLng(y, x));
this.stream.point(point.x, point.y);
}
for (var i = 0; i < data.length; i++) {
by_name[ data[i].state ] = {};
for (var propt in data[i]) {
by_name[ data[i].state ][propt] = +data[i][propt];
}
by_name[ data[i].state ].abbrev = data[i].abbrev;
by_name[ data[i].state ].state = data[i].state;
}
//Find the corresponding state inside the GeoJSON
json.features.forEach( function (j) {
var jsonState = j.properties.name;
if ( by_name[jsonState] ) {
j.properties.state = by_name[jsonState].state;
j.id = by_name[jsonState].state;
j.abbrev = by_name[jsonState].abbrev;
j.properties.total_imm = by_name[jsonState].total_imm;
j.properties.total_emm = by_name[jsonState].total_emm;
}
else {
console.log('No data for ' + jsonState)
}
})
//Bind data and create one path per GeoJSON feature
var feature = g.selectAll("path")
.data(json.features)
.enter()
.append("path")
.attr("id", function(d) {
return d.properties.state;
})
.attr("d", path)
.attr("stroke-width", 0.5)
.attr("fill-opacity", "0.5")
.style("stroke", "#666")
.style("fill", "#fff");
//Net migration circles
var circles = svgCircles.selectAll("circle")
.data(json.features)
.enter().append("circle")
.attr("cx", function(d) {
d.centroid = path.centroid(d);
return d.centroid[0];
})
.attr("cy", function(d) {
return d.centroid[1];
})
.attr("r", function(d) { //circle size
console.log("Value: " + value);
var diff = d.properties.total_imm - d.properties.total_emm;
// if (diff > 200){
return circleSize((Math.abs(diff)/Math.PI));
// }
})
.attr("class", "circ")
.attr("id", function(d) {
return d.abbrev;
})
.attr("fill", function(d) { //circle color
var diff = d.properties.total_imm - d.properties.total_emm;
if (diff > 0) {
return "#65a89d";
}
else {
return "#a96a46";
}
})
.attr("fill-opacity", "0.5")
.attr("stroke", "#fff")
.attr("stroke-weight", "0.5")
.on("mouseover", function(d) {
return toolOver(d, this);
})
.on("mousemove", function(d) {
var m = d3.mouse(this);
mx = m[0];
my = m[1];
return toolMove(mx, my, d);
})
.on("mouseout", function(d) {
return toolOut(d, this);
})
.on("click", function(d) {
clicked(d)
});
map.on("viewreset", reset);
reset();
//Reset function
function reset() {
var bounds = path.bounds(json);
topLeft = bounds[0],
bottomRight = bounds[1];
svg.attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");
g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
feature.attr("d", path);
circles.attr("cx", function(d) {
return map.latLngToLayerPoint(d.LatLng).x;
});
circles.attr("cy", function(d) {
return map.latLngToLayerPoint(d.LatLng).y;
});
svgCircles.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
}
});
});
The problem is if I try to use this function, then nothing shows:
json.features.forEach(function(d) {
d.LatLng = new L.LatLng(d.geometry.coordinates[1],
d.geometry.coordinates[0]);
});
Also I am getting the error:
TypeError: t is undefined, can't access property "lat" of it
Help please
So I fixed the circles at least. The problem was I was not returning the correct attributes. Should'Ve returned the centroids and not log lat. Here is the updated version of my reset function:
//Reset function
function reset() {
//Defining Bounds
var bounds = path.bounds(json);
topLeft = bounds[0],
bottomRight = bounds[1];
svg.attr("width", bottomRight[0] - topLeft[0])
.attr("height", bottomRight[1] - topLeft[1])
.style("left", topLeft[0] + "px")
.style("top", topLeft[1] + "px");
//Recalculating Projections for Json map
g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
feature.attr("d", path);
//Recalculating Projections for Circles
circles.attr("cx", function(d) {
d.centroid = path.centroid(d);
return d.centroid[0];
});
circles.attr("cy", function(d) {
return d.centroid[1];
});
g.attr("transform", "translate(" + -topLeft[0] + "," + -topLeft[1] + ")");
circles.attr("d", path);
}
I still need to figure out the projection of the lines. Any suggestions on that?
I´m trying to put labels on my D3 circle-pack, for that I´m using the circle-text plugin. I want do it like this example circle_pack circle_text.
The problem is in the update of the labels position and the ray of them, when i zoom into a node.
My code:
function zoom(d) {
var k = diameter / d.r / 2;
x.domain([d.x - d.r, d.x + d.r]);
y.domain([d.y - d.r, d.y + d.r]);
var focus0 = focus; focus = d;
var transition = d3.transition()
.duration(d3.event.altKey ? 7500 : 750)
.tween("zoom", function(d) {
var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
return function(t) { zoomTo(i(t)); };
});
circleText.radius(function (d) { return k * d.r- 5 ; });
transition.selectAll('g.label')
.attr('transform', function(d) {
return 'translate(' + x(d.x) + ',' + y(d.y) + ')';})
.call(circleText);
}
function zoomTo(v) {
var k = diameter / v[2]; view = v;
node.attr("transform", function(d) { return "translate(" + (d.x - v[0]) * k + "," + (d.y - v[1]) * k + ")"; });
node.attr("r", function(d) { return d.r * k;});
}
I want show percentage share of each block in labelled zoomable sunburst chart. I am referring http://bl.ocks.org/metmajer/5480307 this example.
here I want add (%x) share of each block. Please help.
Below is my index.html
<!DOCTYPE html>
<meta charset="utf-8"><style>
path {
stroke: #fff;
fill-rule: evenodd;
}
text {
font-family: Arial, sans-serif;
font-size: 12px;
}
</style> <body>
<script src="http://d3js.org/d3.v3.min.js">
</script> <script>
var width = 960,
height = 700,
radius = Math.min(width, height) / 2;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.linear()
.range([0, radius]);
var color = d3.scale.category20c();
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()
.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("atmLeads.json", function(error, root) {
var g = svg.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var path = g.append("path")
.attr("d", arc).attr("class",function(d){return "ring_"+ d.depth;})
.style("fill", function(d) { return color((d.children ? d : d.parent).name); })
.on("click", click);
var text = g.append("text")
.attr("transform", function(d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function(d) { return y(d.y); })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) { return d.name; });
function click(d) {
// fade out all text elements
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); });
}
});
}
});
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 computeTextRotation(d) {
return (x(d.x + d.dx / 2) - Math.PI / 2) / Math.PI * 180;
}
</script>
Try this code.
var path = g.append("path")
.attr("d", arc).attr("class", function(d) {
return "ring_" + d.depth;
})
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.on("click", click);
var totalSize = path.node().__data__.value;
var text = g.append("text")
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) {
var percentage = (100 * d.value / totalSize).toPrecision(3);
var percentageString = percentage + "%";
if (percentage < 0.1) {
percentageString = "< 0.1%";
}
return d.name +" "+percentageString;
});
var width = 960,
height = 700,
radius = Math.min(width, height) / 2;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.linear()
.range([0, radius]);
var color = d3.scale.category20c();
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()
.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));
});
var root = {
"name": "ATM Leads Converted",
"size": 34752,
"children": [
{
"name": "Converted",
"size": 417
}, {
"name": "Failure",
"size": 1366
}, {
"name": "Interested",
"size": 916
}, {
"name": "No Value",
"size": 48932
}, {
"name": "Not Interested",
"size": 14479
}, {
"name": "Not contactable",
"size": 2961
},
{
"name": "Success",
"size": 1142
}, {
"name": "Will Get Back",
"size": 1564
}, {
"name": "Wrong Number",
"size": 358
}
]
};
var g = svg.selectAll("g")
.data(partition.nodes(root))
.enter().append("g");
var path = g.append("path")
.attr("d", arc).attr("class", function(d) {
return "ring_" + d.depth;
})
.style("fill", function(d) {
return color((d.children ? d : d.parent).name);
})
.on("click", click);
var totalSize = path.node().__data__.value;
var text = g.append("text")
.attr("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.attr("x", function(d) {
return y(d.y);
})
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) {
var percentage = (100 * d.value / totalSize).toPrecision(3);
var percentageString = percentage + "%";
if (percentage < 0.1) {
percentageString = "< 0.1%";
}
return d.name +" "+percentageString;
});
function click(d) {
// fade out all text elements
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);
});
}
});
}
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 computeTextRotation(d) {
return (x(d.x + d.dx / 2) - Math.PI / 2) / Math.PI * 180;
}
path {
stroke: #fff;
fill-rule: evenodd;
}
text {
font-family: Arial, sans-serif;
font-size: 12px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
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?
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.