Capturing/Saving the current state of d3.js Sunburst Chart - javascript

I am a newbie to d3.js . I am working in d3.js zoomable sunburst chart http://bl.ocks.org/mbostock/4348373. When the user zoom into a particular arc and I need to capture this state of the sunburst diagram . When the user comes back to the sunburst diagram or load the graph again he should see the state where he left.
Note : I dont want to serialise the svg elements to show the state of the sunburst. If i serialise it then the chart will be static and user cant click on the sunburst and traverse to other arcs.
Proposed Solution :
one solution came to my mind is simulate mouse clicks on the sunburst nodes till the last node user looks into.
I am not able to devise an algorithm for this .
Please guide me whether any other solution is possible ..

The approach you said is easy to implement. I have made a small sample for it.
Click the Start Save button to start saving the state of Sunburst. Click Stop Save button after performing a few zooming actions. You can make any further changes to the chart. Now, clicking Load button will show you the saved state of the chart.
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));
});
var root = getData();
var savedData = partition.nodes(root);
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);
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;
}
var saveData = false;
var savedData = [];
d3.select("#saveBtn").on("click", function() {
if (d3.select(this).attr("value") == "Start Save") {
savedData = [];
d3.select(this).attr("value", "Stop Save");
saveData = true;
} else {
d3.select(this).attr("value", "Start Save");
saveData = false;
}
});
d3.select("#loadBtn").on("click", function() {
var root = g.filter(function(d) {
return d.name == "Root"
}).data();
click(root[0]);
savedData.forEach(function(val) {
var node = g.filter(function(d, i) {
return i == val
}).data();
click(node[0]);
});
});
function click(d, i) {
if (saveData) {
if(d.name=="Root"){
savedData = [];
} else{
savedData.push(i);
}
}
// 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("transform", function(d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.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)
};
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 getData() {
return {
"name": "Root",
"children": [{
"name": "A1",
"children": [{
"name": "B1",
"size": 30
}, {
"name": "B2",
"size": 40
}, {
"name": "B3",
"children": [{
"name": "C1",
"size": 10
}, {
"name": "C2",
"size": 15
}]
}]
}, {
"name": "A2",
"children": [{
"name": "B4",
"size": 40
}, {
"name": "B5",
"size": 30
}, {
"name": "B6",
"size": 10
}]
}, {
"name": "A3",
"children": [{
"name": "B7",
"size": 50
}, {
"name": "B8",
"size": 15
}
]
}]
}
};
path {
stroke: #fff;
fill-rule: evenodd;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<div id="main"></div>
<input type="button" value="Start Save" id="saveBtn"/>
<input type="button" value="Load" id="loadBtn"/>
I don't know where do you plan to save the state of the sunburst. I would suggest that Localstorage would be a nice option.
Hope this helps.

Related

I wanted to make last child or root node in d3 zoomable sunburst as a hyperlink, trying to find the last child node and make the transition to url

I'm new to d3js so I watched many tutorial video and developed the chart, but now I got stuck to make the last child node as a hyperlink, I also refered this How can I make last node in d3 zoomable sunburst as a hyperlink? and tried to solve this but I couldn't. Since I'm new could anyone please help me in solving this.
Here I'm attaching the js file please help me in which location do I need to check for the child node and while transition I wanted to make it as hyperlink.
`
function getData() {
return {
"name": "Main",
"children": [
{
"name": "Section1",
"url": "",
"children": [{
"name": "Test11 ",
"size": 1,
"url": "https://www.google.com"
}, {
"name": "Section12 ",
"size": 1
}, {
"name": "Section13 ",
"size": 1
}, {
"name": "Section14 ",
"size": 1
}]
},
{
"name": "Section2",
"url": "",
"children": [{
"name": "Section21 ",
"size": 1,
"url": "https://www.google.com"
}, {
"name": "Section22 ",
"size": 1
}, {
"name": "Section23 ",
"size": 1
}, {
"name": "Section24 ",
"size": 1
}]
}, {
"name": "Section3",
"url": "",
"children": [{
"name": "Section31 ",
"size": 1,
"url": "https://www.google.com"
}, {
"name": "Section32 ",
"size": 1
}, {
"name": "Section33 ",
"size": 1
}, {
"name": "Section34 ",
"size": 1
}]
}, {
"name": "Section4",
"url": "",
"children": [{
"name": "Section41 ",
"size": 1,
"url": "https://www.google.com"
}, {
"name": "Section42 ",
"size": 1
}, {
"name": "Section43 ",
"size": 1
}, {
"name": "Section44 ",
"size": 1
}]
},]
}
};
var colorScheme = ["#1c1c1e", "#FFCC00", "#007AFF", "#FF2C55", "#FF9501", "#AF52DE", "#5AC8FB", "#5855D6", "#35C75A", "#00a86b", "#ca3433", "#00E676"];
dataset = getData();
var width = 500,
height = 500,
radius = Math.min(width / 2, height / 2) * .80;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.linear()
.range([-90, radius]);
var color = d3.scale.ordinal().range(colorScheme);
var divHeight = height;
var divWidth = width;
var svg = d3.select("#bodyChart").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + divWidth / 2 + "," + height / 2 + ") rotate(-90 0 0)");
var partition = d3.layout.partition()
.value(function (d) {
return d.size;
});
var tooltip = d3.select("body")
.append("div")
.attr("class", "tooltip")
.style("position", "absolute")
.style("z-index", "10")
.style("opacity", 0);
var arc = d3.svg.arc()
.startAngle(function (d) {
if (d.size != 0)
return Math.max(0, Math.min(2 * Math.PI, x(d.x)));
})
.endAngle(function (d) {
if (d.depth == 0)
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
if (d.depth == 2) {
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
}
if (d.size != 0)
return Math.max(0, Math.min(2 * Math.PI, x(d.x + d.dx)));
})
.innerRadius(function (d) {
if (d.size != 0)
return Math.max(0, y(d.y));
})
.outerRadius(function (d) {
if (d.size != 0)
return Math.max(0, y(d.y + d.dy) * 1.25);
});
var root = dataset;
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)
.on("mouseover", function (d) {
tooltip.html('<a href= "' + d.url + '"' +
"</a>" + d.name);
//function () {return d.name + "<br>" + d.url; });
return tooltip.transition()
.duration(100)
.style("opacity", 0.9);
})
.on("mousemove", function (d) {
return tooltip
.style("top", (d3.event.pageY - 2) + "px")
.style("left", (d3.event.pageX + 2) + "px");
})
.on("mouseout", function () { return tooltip.style("opacity", 0); });
var text = g.append("text")
.attr("x", function (d) {
return y(d.y);
})
.attr("dx", function (d) {
return d.depth == 0 ? "9" : "9"
}) // margin
.attr("dy", ".35em")
.attr("transform", function (d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.text(function (d) {
return d.size == null ? d.name : d.name;
})
.style("fill", "black");
function computeTextRotation(d) {
var angle = x(d.x + d.dx / 2) - Math.PI / 2;
return angle / Math.PI * 180;
}
function click(d) {
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) {
if (e.x >= d.x && e.x < (d.x + d.dx)) {
var arcText = d3.select(this.parentNode).select("text");
arcText.transition().duration(750)
.attr("opacity", 1)
.attr("transform", function () {
return "rotate(" + computeTextRotation(e) + ")"
})
.attr("x", function (d) {
return y(d.y);
});
}
});
}
var insertLinebreaks = function (t, d, width) {
var el = d3.select(t);
var p = d3.select(t.parentNode);
p.append("g")
.attr("x", function (d) {
return y(d.y);
})
.attr("transform", function (d) {
return "rotate(" + computeTextRotation(d) + ")";
})
.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');
el.remove();
};
d3.select(self.frameElement).style("height", height + "px");
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);
};
};
}
Thank you in advance.

How to change the alignment of circles in packed circle d3

I'm trying to change the alignment of the circle so that I can see the text in the middle Just like in this image.
I need help on how to align all the circles towards the circumference of the outer circle. I tried to add the text but it is overlapping with the circle in the center. Is it possible to change the alignment of the circle?
My code is mentioned below.
var root = {
"name": "flare",
"threat_level": "High",
"size": 15000,
"children": [{
"name": "Ghost",
"threat_level": "High",
"size": 1200
},
{
"name": "Wiper",
"threat_level": "Medium",
"size": 1330
},
{
"name": "PowerLiks",
"threat_level": "Medium",
"size": 1333
},
{
"name": "Fareit",
"threat_level": "Medium",
"size": 1300
},
{
"name": "Tribe",
"threat_level": "High",
"size": 1330
},
{
"name": "Oilrig",
"threat_level": "High",
"size": 1330
}
]
}
var svg = d3.select("svg"),
margin = 20,
diameter = +svg.attr("width"),
g = svg.append("g").attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
const color = (type) => type == 'High' ? '#F72047' : (type == 'Medium' ? '#FFFFFF' : '#fff0') //colorScale
var defs = svg.append("defs");
var levels = ['High', 'Medium', 'Low']
levels.forEach((d) => {
//Create a radial Sun-like gradient
defs.append("radialGradient")
.attr("id", "sun-gradient_" + d)
.selectAll("stop")
.data([{
offset: "0%",
color: "#1A1D27"
},
{
offset: "80%",
color: "#1A1D27"
},
{
offset: "100%",
color: d == 'High' ? "#CB1F40" : "#959595"
}
])
.enter().append("stop")
.attr("offset", function (d) {
return d.offset;
})
.attr("stop-color", function (d) {
return d.color;
});
// .interpolate(d3.interpolateHcl);
})
var pack = d3.pack()
.size([diameter - margin, diameter - margin])
.padding(50);
root = d3.hierarchy(root)
.sum(function (d) {
return d.size;
})
.sort(function (a, b) {
return b.value - a.value;
});
var focus = root,
nodes = pack(root).descendants(),
view;
var circle = g.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("class", function (d) {
return d.parent ? d.children ? "node" : "node node--leaf" : "node node--root";
})
.style("fill", (d) => `url(#${'sun-gradient_' + d.data.threat_level})`)
.style('stroke', (d) => color(d.data.threat_level))
.style('stroke-width', 1)
.on("click", function (e, d) {
if (focus !== d) zoom(d);
e.stopPropagation();
});
var text = g.selectAll("text")
.data(nodes)
.enter().append("text")
.attr("class", "label")
.text(function (d) {
return d.parent === root ? d.data.name : d.data.size;
})
.style('font-family', 'Metropolis Bold')
.style('font-size', (d) => d.parent === root ? '12px' : '24px')
.style('text-anchor', 'middle')
.style('fill', (d) => d.parent === root ? '#FFFFFF' : '#F72047')
.style('text-transform', 'uppercase')
var node = g.selectAll("circle,text");
svg
.style("background", color(-1))
.on("click", function () {
zoom(root);
});
zoomTo([root.x, root.y, root.r * 2 + margin]);
function zoom(d) {
var focus0 = focus;
focus = d;
var transition = d3.transition()
.duration(750)
.tween("zoom", function (d) {
var i = d3.interpolateZoom(view, [focus.x, focus.y, focus.r * 2 + margin]);
return function (t) {
zoomTo(i(t));
};
});
transition.selectAll("text")
.filter(function (d) {
return d.parent === focus || this.style.display === "inline";
})
.style("fill-opacity", function (d) {
return d.parent === focus ? 1 : 0;
})
.on("start", function (d) {
if (d.parent === focus) this.style.display = "inline";
})
.on("end", function (d) {
if (d.parent !== focus) this.style.display = "none";
});
}
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 + ")";
});
circle.attr("r", function (d) {
return d.r * k;
});
}
<svg width="600" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
The packed circle layout is designed to take a group of objects and pack them as tightly as possible within a circle. So this isn't what you want.
Method 1: Radial Tree Layout
If you want to arrange a set of objects evenly around the circumference of a circle with a given radius, you want something similar to a "radial tree layout". You can see an example of that in Chapter 6 of the book you linked to (Tree, Cluster, and Radial Layouts), though you'll not want the lines between the nodes.
There's also an example given in the answer to this question: d3.js - how to arrange the `squre` box around the `circle` properly
Method 2: Use sin and cos
Alternatively, you can divide the circumference of the circle by the number of objects you want to place, then use sin and cos to calculate their center point x and y manually.
There's an example of that here: d3.js radially position elements around an object
And a variation here: https://spin.atomicobject.com/2015/06/12/objects-around-svg-circle-d3-js/

D3 zoomable sunburst not fully circle

I'm just getting started with d3js and I'm trying to make a zoomable sunburst. I've copied a tutorial but because most of the data given there were very complex I tried to change the data into much simpler ones. Unfortunately after that , the outer svg becomes an incomplete circle. There's a line at the bottom of it. Not sure whether I have to change the data or the outerradius. Thanks in advance.
var flaredata = {
"name": "Root",
"children": [{
"name": "A1",
"children": [{
"name": "B1",
"size": 30
},
{
"name": "B2",
"size": 40
},
{
"name": "B3",
"size": 40
}
]
},
{
"name": "A2",
"children": [{
"name": "B4",
"size": 40
},
{
"name": "B5",
"size": 30
},
{
"name": "B6",
"size": 10
}
]
},
{
"name": "A3",
"children": [{
"name": "B7",
"size": 50
},
{
"name": "B8",
"size": 15
}
]
}
]
}
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.linear()
.range([0, radius]);
var color = d3.scale.category20c();
var svg = d3.select("section").append("svg")
.attr("width", width)
.attr("height", height)
.datum(flaredata) // this line is modified according to provided solution here: https://stackoverflow.com/questions/17019572/d3-sunburst-doesnt-draw-with-inline-json
// data() was replaced with datum() as suggested by Lars Kotthoff
.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("flare.json", function(error, root) {
var g = svg.selectAll("g")
.data(partition.nodes) // removed "root" argument here
.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);
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;
}
The circle is drawn completely. The problem might be that your SVG is not big enough to display all of it with the given y-translation of 10.
One of the most easy way might be to change this line of code:
radius = (Math.min(width, height) / 2) - 5; // <-- -5 is just a suggestion.
If you want to display it in the biggest size possible without any borders you can change the following line of code instead which is "the main source" of your problem:
.attr("transform", "translate(" + width / 2 + "," + (height / 2 + 10) + ")"); // + 10 as y translation makes the element appear with a top distance of 260 in this case.
If you remove the +10 it should perfectly fit inside the SVG.

d3js circle turns black - String is not a function

I had an error with my d3 chart that sometimes at loading it give me this error "used JS COnsole":
uncaught TypeError: string is not a function
here is the code
var margin = { top: 290, right: 360, bottom: 290, left: 360 },
radius = Math.min(margin.top, margin.right, margin.bottom, margin.left) - 10;
var x = d3.scale.linear()
.range([0, 2 * Math.PI]);
var y = d3.scale.linear()
.range([0, radius]);
var color = d3.scale.ordinal()
.domain(["foo", "bdo", "baz"])
.range(colorbrewer.Spectral[8]);
var svg = d3.select("#chart2").append("svg")
.attr("width", '100%')
.attr("height", '100%')
.attr('viewBox', '0 0 ' + Math.min(margin.left + margin.right, margin.top + margin.bottom) + ' ' + Math.min(margin.left + margin.right, margin.top + margin.bottom))
.attr('preserveAspectRatio', 'xMinYMin')
.append("g")
.attr("id", "container")
.attr("transform", "translate(" + Math.min(margin.left + margin.right, margin.top + margin.bottom) / 2 + "," + Math.min(margin.left + margin.right, margin.top + margin.bottom) / 2 + ")");
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))); })
// lw 3'erna al y(d.y) we 2smnaha 3la 2 he3'er al radius le al first level
.innerRadius(function (d) { return Math.max(0, y(d.y)); })
.outerRadius(function (d) { return Math.max(0, y(d.y + d.dy)); });
d3.json("#Html.Raw( Url.Action("GetD3_JSON", "Account", new { type=ViewBag.type, year=ViewBag.CurrentYear, month=ViewBag.CurrentMonth, day=ViewBag.CurrentDay, project=ViewBag.ProjectGUID, code = (ViewContext.RouteData.Values["Action"].ToString() == "Income") ? 1 : (ViewContext.RouteData.Values["Action"].ToString() == "Expenses") ? -1 : 0 }) )", function (error, root) {
// 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;
});
// 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 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)
.on("mouseover", function (d) {
tooltip.show([d3.event.clientX, d3.event.clientY], '<div>' + d.name + '</div><div>' + d.value + '</div>')
})
.on('mouseout', function () {
tooltip.cleanup()
})
.each(stash);
// Define the legeneds
var legend = d3.select("#legend").append("svg")
.attr("class", "legend")
.attr("width", radius)
.attr("height", radius)
.selectAll("g")
.data(partition.nodes(root))
.enter().append("g")
.attr("transform", function (d, i) { return "translate(0," + i * 20 + ")"; });
legend.append("rect")
.attr("width", 18)
.attr("height", 18)
.style("fill", function (d) { return color((d.children ? d : d.parent).name); })
.on("click", click);
legend.append("text")
.attr("x", 24)
.attr("y", 9)
.attr("dy", ".35em")
.text(function (d) { return d.name; });
// Define Labels on the arcs
var text = g.append("text")
.attr("dy", ".35em") // vertical-align
.attr("transform", function (d) { return "rotate(" + computeTextRotation(d) + ")"; })
.attr("x", function (d) { return y(d.y); })
.attr("dx", "6") // margin
.attr("display", 'block')
.text(function (d) {
return d.name;
})
.on("click", click);
var center = svg.append("circle")
.attr("r", radius / 3)
.style("fill", "white")
.on("click", click);
center.append("title")
center.datum(root);
function click(d) {
// fade out all text elements
text.transition().attr("opacity", 0);
path.transition()
.duration(500)
.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(250)
.attr("opacity", 1)
.attr("transform", function () { return "rotate(" + computeTextRotation(e) + ")" })
.attr("x", function (d) { return y(d.y); });
}
});
}
});
d3.select(self.frameElement).style("height", margin.top + margin.bottom + "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 * 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;
}
// Stash the old values for transition.
function stash(d) {
d.x0 = d.x;
d.dx0 = d.dx;
}
/*****
* A no frills tooltip implementation.
*****/
(function () {
var tooltip = window.tooltip = {}
tooltip.show = function (pos, content, gravity, dist, parentContainer, classes) {
var container = d3.select('body').selectAll('.tooltip').data([1])
container.enter().append('div').attr('class', 'tooltip ' + (classes ? classes : 'xy-tooltip'))
container.html(content)
gravity = gravity || 'n'
dist = dist || 20
var body = document.getElementsByTagName('body')[0]
var height = parseInt(container[0][0].offsetHeight)
, width = parseInt(container[0][0].offsetWidth)
, windowWidth = window.innerWidth
, windowHeight = window.innerHeight
, scrollTop = body.scrollTop
, scrollLeft = body.scrollLeft
, left = 0
, top = 0
switch (gravity) {
case 'e':
left = pos[0] - width - dist
top = pos[1] - (height / 2)
if (left < scrollLeft) left = pos[0] + dist
if (top < scrollTop) top = scrollTop + 5
if (top + height > scrollTop + windowHeight) top = scrollTop - height - 5
break
case 'w':
left = pos[0] + dist
top = pos[1] - (height / 2)
if (left + width > windowWidth) left = pos[0] - width - dist
if (top < scrollTop) top = scrollTop + 5
if (top + height > scrollTop + windowHeight) top = scrollTop - height - 5
break
case 's':
left = pos[0] - (width / 2)
top = pos[1] + dist
if (left < scrollLeft) left = scrollLeft + 5
if (left + width > windowWidth) left = windowWidth - width - 5
if (top + height > scrollTop + windowHeight) top = pos[1] - height - dist
break
case 'n':
left = pos[0] - (width / 2)
top = pos[1] - height - dist
if (left < scrollLeft) left = scrollLeft + 5
if (left + width > windowWidth) left = windowWidth - width - 5
if (scrollTop > top) top = pos[1] + 20
break
}
container.style('left', left + 'px')
container.style('top', top + 'px')
return container
}
tooltip.cleanup = function () {
// Find the tooltips, mark them for removal by this class (so other tooltip functions won't find it)
var tooltips = d3.selectAll('.tooltip').attr('class', 'tooltip-pending-removal').transition().duration(250).style('opacity', 0).remove()
}
})()
the error with this line
.style("fill", function (d) { return color((d.children ? d : d.parent).name); })
but i fixed it by fixing the structure of the json file "ignore the arabic fonts"
{
"children": [
{
"name": "المصاريف",
"children": [
{
"name": "Expense_1",
"children": [
{
"name": "<غير مصنف>",
"size": 500.0000
},
{
"name": "ExpSubCat1_1",
"size": 5772.0000
},
{
"name": "ExpSubCat1_2",
"size": 5471.0000
}
]
},
{
"name": "ffffff",
"children": [
{
"name": "<غير مصنف>",
"size": 19.0000
}
]
}
]
},
{
"name": "الدخل",
"children": [
{
"name": "ffff",
"children": [
{
"name": "<غير مصنف>",
"size": 10334.0000
}
]
},
{
"name": "Income_1",
"children": [
{
"name": "IncSubCat1_1",
"size": 6371.0000
},
{
"name": "<غير مصنف>",
"size": 11211.0000
},
{
"name": "IncSubCat1_2",
"size": 7160.0000
}
]
},
{
"name": "Income_2",
"children": [
{
"name": "<غير مصنف>",
"size": 39.0000
},
{
"name": "IncSubCat2_2",
"size": 7256.0000
},
{
"name": "IncSubCat2_1",
"size": 583090.0000
}
]
}
]
},
{
"name": "الربح",
"size": 601700.0000
}
]
}

D3: Rotating labels in the third and fourth quad in sunburst example

I am using D3 and javascript for the first time to draw sunburst, following examples at http://bl.ocks.org/mbostock/ and http://bl.ocks.org/mbostock/4063423. The labels in the thirsd and fourth quads are upside down. I found coffee wheel example here http://www.jasondavies.com/coffee-wheel/ and tried to incorporate in my example but was unsuccessful in rotating the labels. I did found a similar post here How to properly rotate text labels in a D3 sunburst diagram, but was unable to solve the issue as one of the link is not working.
<!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 = 1600,
height = 1300,
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("ex.json", function(error, root) {
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);
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>
ex.json
[{
"name": "aaa",
"size": 5000,
"children":
[
{
"name": "aaab",
"size": 2952,
"children": [
{"name": "xxx", "size": 45},
{"name": "xxy", "size": 29},
{"name": "xxz", "size": 28},
{"name": "xxa", "size": 4}
]
},
{
"name": "aaac",
"size": 251,
"children": [
{
"name": "ddd",
"size": 7,
"children": [
{"name": "ppp", "size": 4},
{"name": "qqq", "size": 2}
]
},
{"name": "xxt", "size": 4},
{"name": "xxu", "size": 1},
{"name": "xxv", "size": 1}
]
},
{"name": "aaad","size": 222}
]
}][1]
Change your computeTextRotation function to the following:
function computeTextRotation(d) {
var ang = (x(d.x + d.dx / 2) - Math.PI / 2) / Math.PI * 180;
return (ang > 90) ? 180 + ang : ang;
}
And set the point of rotation to be the position of centroid of the respective arc:
var text = g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")rotate(" + computeTextRotation(d) + ")"; })
.attr('text-anchor', function (d) { return computeTextRotation(d) > 180 ? "end" : "start"; })
.attr("dx", "6") // margin
.attr("dy", ".35em") // vertical-align
.text(function(d) { return d.name; });
Example: http://jsfiddle.net/Hm49x/
With your data: http://plnkr.co/edit/tct95toQ2IjyUkQJQPB9?p=preview

Categories

Resources