Rotate D3 tree from vertical to horizontal - javascript

I am new in D3 and developing a family tree using d3v4(for some constraints not using latest version of d3. Though I am using latest d3 cdn but my requirement is d3v4). The family tree is vertical i.e., parent is in top and children are in bottom. But I want to turn the tree and want to make it horizontal i.e., parent is in left and children are in right.
Kindly find my code below:
<!DOCTYPE html>
<html>
<head>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<body>
<script>
var svg = d3.select("body").append("svg")
.attr("width", 600)
.attr("height", 600)
.append("g").attr("transform", "translate(50, 50)");
var data = [
{
"child": {
"type": "from",
"name": "John",
"factor": 5
},
"parent": ""
},
{
"child": {
"type": "cc",
"name": "Kevin",
"factor": 4
},
"parent": {
"type": "from",
"name": "John",
"factor": 5
}
},
{
"child": {
"type": "bcc",
"name": "Mary",
"factor": 6
},
"parent": {
"type": "from",
"name": "John",
"factor": 5
}
},
{
"child": {
"type": "cc",
"name": "Mars",
"factor": 5
},
"parent": {
"type": "from",
"name": "John",
"factor": 5
}
}
]
var datastructure = d3.stratify()
.id(function(d) { return d.child.name; })
.parentId(function(d) { return d.parent.name; })
(data);
var treeStructure = d3.tree().size([500, 300]);
var information = treeStructure(datastructure);
// console.log(information.descendants());
// console.log(information.links());
var circles = svg.append("g").selectAll("circle")
.data(information.descendants());
var markerBoxWidth = 20;
var markerBoxHeight = 20;
var refX = markerBoxWidth / 2;
var refY = markerBoxHeight / 2;
var markerWidth = markerBoxWidth / 2;
var markerHeight = markerBoxHeight / 2;
var arrowPoints = [[0, 0], [0, 20], [20, 10]];
svg
.append('defs')
.append('marker')
.attr('id', 'arrow')
.attr('viewBox', [0, 0, markerBoxWidth, markerBoxHeight])
.attr('refX', refX)
.attr('refY', refY)
.attr('markerWidth', markerBoxWidth)
.attr('markerHeight', markerBoxHeight)
.attr('orient', 'auto-start-reverse')
.append('path')
.attr('d', d3.line()(arrowPoints))
.attr('fill', 'red');
circles.enter().append("circle")
.attr("cx", function(d) {
// console.log(d)
return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) {
//console.log()
return d.data.child.factor * 3;
})
.attr("fill", function(d) {
if (d.data.child.type === 'from') {
return "blue";
} else if (d.data.child.type === 'cc') {
return "yellow";
} else if (d.data.child.type === 'bcc') {
return "red";
}
});
var connections = svg.append("g").selectAll("path")
.data(information.links());
connections.enter().append("path")
.attr("d", function(d) {
console.log(d);
return "M" + d.source.x + "," + (d.source.y + (d.source.data.child.factor * 3))
+ " C " +
d.source.x + "," + (d.source.y + d.target.y)/2 + " " +
d.target.x + "," + (d.source.y + d.target.y)/2 + " " +
d.target.x + "," + (d.target.y - (d.target.data.child.factor * 3));
}).attr("fill", "none").attr("stroke", "red")
.attr('marker-end', 'url(#arrow)')
.attr('fill', 'none');
var names = svg.append("g").selectAll("text")
.data(information.descendants());
const xScale = d3.scaleLinear()
.domain([0, 8])
.range([0, 1000]);
names.enter().append("text")
.text(function(d) { return d.data.child.name; })
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; });
</script>
</body>
</html>
Thanks in advance.

Related

How do I fix zoomable-icicle so that is show some graphics?

Why doesn't this work. There is no error:
The example is taking from this site: https://observablehq.com/#d3/zoomable-icicle
All code is here. It's only html and Javascript. What am I overlooking:
So far it looks like the return of the svg is not working og the color definition is wrong.
<!DOCTYPE html>
<html>
<head>
<title>Zoomable Icicle</title>
</head>
<body>Velcome
<h1 class="thechart"></h1>
<script src="https://d3js.org/d3.v6.min.js"></script>
<script>
JsonData = {
"name": "flare",
"children": [
{
"name": "analytics",
"children": [
{
"name": "cluster",
"children": [
{
"name": "AgglomerativeCluster",
"value": 3938
},
{
"name": "CommunityStructure",
"value": 3812
},
{
"name": "HierarchicalCluster",
"value": 6714
},
{
"name": "MergeEdge",
"value": 743
}
]
},
{
"name": "Visualization",
"value": 16540
}
]
}
]
}
height = 1200;
width = 975;
format = d3.format(",d");
color = d3.scaleOrdinal(d3.quantize(d3.interpolateRainbow, JsonData.children.length + 1));
partition = data => {
const root = d3.hierarchy(data)
.sum(d => d.value)
.sort((a, b) => b.height - a.height || b.value - a.value);
return d3.partition()
.size([height, (root.height + 1) * width / 3])
(root);
}
function chart(data) {
const root = partition(data);
let focus = root;
const svg = d3.create("svg")
.attr("viewBox", [0, 0, width, height])
.style("font", "10px sans-serif");
const cell = svg
.selectAll("g")
.data(root.descendants())
.join("g")
.attr("transform", d => `translate(${d.y0},${d.x0})`);
const rect = cell.append("rect")
.attr("width", d => d.y1 - d.y0 - 1)
.attr("height", d => rectHeight(d))
.attr("fill-opacity", 0.6)
.attr("fill", d => {
if (!d.depth) return "#ccc";
while (d.depth > 1) d = d.parent;
return color(d.data.name);
})
.style("cursor", "pointer")
.on("click", clicked);
const text = cell.append("text")
.style("user-select", "none")
.attr("pointer-events", "none")
.attr("x", 4)
.attr("y", 13)
.attr("fill-opacity", d => +labelVisible(d));
text.append("tspan")
.text(d => d.data.name);
const tspan = text.append("tspan")
.attr("fill-opacity", d => labelVisible(d) * 0.7)
.text(d => ` ${format(d.value)}`);
cell.append("title")
.text(d => `${d.ancestors().map(d => d.data.name).reverse().join("/")}\n${format(d.value)}`);
function clicked(event, p) {
focus = focus === p ? p = p.parent : p;
root.each(d => d.target = {
x0: (d.x0 - p.x0) / (p.x1 - p.x0) * height,
x1: (d.x1 - p.x0) / (p.x1 - p.x0) * height,
y0: d.y0 - p.y0,
y1: d.y1 - p.y0
});
const t = cell.transition().duration(750)
.attr("transform", d => `translate(${d.target.y0},${d.target.x0})`);
rect.transition(t).attr("height", d => rectHeight(d.target));
text.transition(t).attr("fill-opacity", d => +labelVisible(d.target));
tspan.transition(t).attr("fill-opacity", d => labelVisible(d.target) * 0.7);
}
function rectHeight(d) {
return d.x1 - d.x0 - Math.min(1, (d.x1 - d.x0) / 2);
}
function labelVisible(d) {
return d.y1 <= width && d.y0 >= 0 && d.x1 - d.x0 > 16;
}
return svg.node();
}
chart(JsonData);
console.log("OK");
</script>
</body>
</html>
I don't know what more to write but the engine want me to write some more.

D3v6 nested graph - nested join()?

I want to visualize the "children" insight each node. I guess the D3v6 .join() function can be nested. Unfortunately I can´t find any example. The snippet below contains an outerGraph with 3 nodes and children as attribute. So far those children aren´t used yet.
The innerGraph instead visualize the small nodes which will be obsolete as soon as the children approach is working. Another Idea would be to work with those two graphs and create a gravity / cluster, which will be the parent.
Goal: Either utilize the children attribute or combine both graphs with the help of an cluster /gravity or even nested join(). I am appreciating any hint / tip. The visuals result should be:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>D3v6 nested nodes</title>
<script src="https://d3js.org/d3.v6.min.js"></script>
</head>
<style>
body {
background-color: whitesmoke;
}
</style>
<body>
<script>
var svg = d3.select("body").append("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight);
var width = window.innerWidth
var height = window.innerHeight
var outerLinkContainer = svg.append("g").attr("class", "outerLinkContainer")
var outerNodeContainer = svg.append("g").attr("class", "outerNodeContainer")
var innerLinkContainer = svg.append("g").attr("class", "innerLinkContainer")
var innerNodeContainer = svg.append("g").attr("class", "innerNodeContainer")
//###############################################
//############# outer force Layouts #############
//###############################################
var outerGraph = {
"nodes": [
{
"id": "A",
"children": [
{
"id": "A1"
},
{
"id": "A2"
}
]
},
{
"id": "B",
"children": [
{
"id": "B1"
},
{
"id": "B2"
}
]
},
{
"id": "C",
"children": [
{
"id": "C1"
},
{
"id": "C2"
}
]
}
],
"links": [
{
"source": "A",
"target": "B"
},
{
"source": "B",
"target": "C"
},
{
"source": "C",
"target": "A"
},
]
}
var outerLayout = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2))
.force("charge", d3.forceManyBody().strength(-500))
.force("link", d3.forceLink().id(function (d) {
return d.id;
}).distance(200))
var outerLinks = outerLinkContainer.selectAll(".link")
.data(outerGraph.links)
.join("line")
.attr("class", "link")
.style("stroke", "black")
.style("opacity", 0.2)
var outerNodes = outerNodeContainer.selectAll("g.outer")
.data(outerGraph.nodes, function (d) { return d.id; })
.join("circle")
.attr("class", "outer")
.style("fill", "pink")
.style("stroke", "blue")
.attr("r", 40)
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
outerLayout
.nodes(outerGraph.nodes)
.on("tick", ticked)
outerLayout
.force("link")
.links(outerGraph.links)
//###############################################
//############## inner force Layout #############
//###############################################
var innerGraph = {
"nodes": [
{ "id": "A1" },
{ "id": "A2" }
],
"links": [
{
"source": "A1",
"target": "A2"
}
]
}
var innerlayout = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2))
.force("charge", d3.forceManyBody().strength(-500))
.force("link", d3.forceLink().id(function (d) {
return d.id;
}).distance(200))
var innerLinks = innerLinkContainer.selectAll(".link")
.data(innerGraph.links)
.join("line")
.attr("class", "link")
.style("stroke", "black")
var innerNodes = innerNodeContainer.selectAll("g.inner")
.data(innerGraph.nodes, function (d) { return d.id; })
.join("circle")
.style("fill", "orange")
.style("stroke", "blue")
.attr("r", 6)
.attr("class", "inner")
.attr("id", function (d) { return d.id; })
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
innerlayout
.nodes(innerGraph.nodes)
.on("tick", ticked)
innerlayout
.force("link")
.links(innerGraph.links)
//###############################################
//################## functons ###################
//###############################################
function ticked() {
outerLinks
.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
innerLinks
.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
outerNodes.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
innerNodes.attr("transform", function (d) {
return "translate(" + (d.x) + "," + (d.y) + ")";
});
}
function dragStarted(event, d) {
if (!event.active)
outerLayout.alphaTarget(0.3).restart();
innerlayout.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragEnded(event, d) {
if (!event.active)
outerLayout.alphaTarget(0)
innerlayout.alphaTarget(0)
d.fx = undefined;
d.fy = undefined;
}
</script>
</body>
</html>
I will update the post as soon as I found a solution.
Here's a slightly hack way to do it - I am a bit disappointed in the outcome because if you play with the outerNodes then the links between innerNodes cross over in an unattractive way.
The changes I made in your code:
update innerGraph so nodes have a parent property (plus add the links required to match your screenshot in the question)
add an additional class on outerNodes so that each outer node can be identified e.g. .outer_A, .outer_B etc
add an additional class on innerNodes so that each inner node can be identified e.g. .child_A1, .child_A2 etc
in ticked - for innerNodes return a point for the inner node so that it is sitting inside centre of it's parent at roughly 20px from the centre on the vector between the original force simulation selected point and the parent's centre.
in ticked - for innerLinks, force the source and target coordinates to update per the previous step
Those last two points are per here and here.
So it works - but only just. Vertical scrolling in the stack snippet seems to upset it a bit but it's maybe better if you try it out on your own dev environment. I still think you could you look at other tools - maybe this one from cytoscape.js and also the webcola example I mentioned in the comments?
var svg = d3.select("body").append("svg")
.attr("width", window.innerWidth)
.attr("height", window.innerHeight);
var width = window.innerWidth
var height = window.innerHeight
var outerLinkContainer = svg.append("g").attr("class", "outerLinkContainer")
var outerNodeContainer = svg.append("g").attr("class", "outerNodeContainer")
var innerLinkContainer = svg.append("g").attr("class", "innerLinkContainer")
var innerNodeContainer = svg.append("g").attr("class", "innerNodeContainer")
//###############################################
//############# outer force Layouts #############
//###############################################
var outerGraph = {
"nodes": [
{
"id": "A",
"children": [
{
"id": "A1"
},
{
"id": "A2"
}
]
},
{
"id": "B",
"children": [
{
"id": "B1"
},
{
"id": "B2"
}
]
},
{
"id": "C",
"children": [
{
"id": "C1"
},
{
"id": "C2"
}
]
}
],
"links": [
{
"source": "A",
"target": "B"
},
{
"source": "B",
"target": "C"
},
{
"source": "C",
"target": "A"
},
]
}
var outerLayout = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2))
.force("charge", d3.forceManyBody().strength(-500))
.force("link", d3.forceLink().id(function (d) {
return d.id;
}).distance(200))
var outerLinks = outerLinkContainer.selectAll(".link")
.data(outerGraph.links)
.join("line")
.attr("class", "link")
.style("stroke", "black")
.style("opacity", 0.2)
var outerNodes = outerNodeContainer.selectAll("g.outer")
.data(outerGraph.nodes, function (d) { return d.id; })
.join("circle")
.attr("class", d => `outer outer_${d.id}`)
.style("fill", "pink")
.style("stroke", "blue")
.attr("r", 40)
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
outerLayout
.nodes(outerGraph.nodes)
.on("tick", ticked)
outerLayout
.force("link")
.links(outerGraph.links)
//###############################################
//############## inner force Layout #############
//###############################################
var innerGraph = {
"nodes": [
{ "id": "A1", "parent": "A" },
{ "id": "A2", "parent": "A" },
{ "id": "B1", "parent": "B" },
{ "id": "B2", "parent": "B" },
{ "id": "C1", "parent": "C" },
{ "id": "C2", "parent": "C" }
],
"links": [
{
"source": "A1",
"target": "A2"
},
{
"source": "A2",
"target": "B2"
},
{
"source": "A1",
"target": "C2"
},
{
"source": "B1",
"target": "B2"
},
{
"source": "B1",
"target": "C1"
},
{
"source": "C2",
"target": "C1"
}
]
}
var innerlayout = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2))
.force("charge", d3.forceManyBody().strength(-500))
.force("link", d3.forceLink().id(function (d) {
return d.id;
}).distance(200))
var innerLinks = innerLinkContainer.selectAll(".link")
.data(innerGraph.links)
.join("line")
.attr("class", "link linkChild")
.style("stroke", "black")
var innerNodes = innerNodeContainer.selectAll("g.inner")
.data(innerGraph.nodes, function (d) { return d.id; })
.join("circle")
.style("fill", "orange")
.style("stroke", "blue")
.attr("r", 6)
.attr("class", d => `inner child_${d.id}`)
.attr("id", function (d) { return d.id; })
.call(d3.drag()
.on("start", dragStarted)
.on("drag", dragged)
.on("end", dragEnded)
)
innerlayout
.nodes(innerGraph.nodes)
.on("tick", ticked)
innerlayout
.force("link")
.links(innerGraph.links)
//###############################################
//################## functons ###################
//###############################################
function ticked() {
outerLinks
.attr("x1", function (d) {
return d.source.x;
})
.attr("y1", function (d) {
return d.source.y;
})
.attr("x2", function (d) {
return d.target.x;
})
.attr("y2", function (d) {
return d.target.y;
});
outerNodes.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
innerNodes.attr("transform", function (d) {
var parent = d3.select(`.outer_${d.parent}`);
var pr = parent.node().getBoundingClientRect();
var prx = pr.left + (pr.width / 2);
var pry = pr.top + (pr.height / 2);
var distance = Math.sqrt( ((d.x - prx) ** 2) + ((d.y - pry) ** 2 ));
var ratio = 20 / distance;
var childX = ((1 - ratio) * prx) + (ratio * d.x);
var childY = ((1 - ratio) * pry) + (ratio * d.y);
return "translate(" + (childX) + "," + (childY) + ")";
});
innerLinks.attr("x1", d => {
var m1 = d3.select(`.child_${d.source.id}`).node().transform.baseVal[0].matrix;
return m1.e;
}).attr("y1", d => {
var m1 = d3.select(`.child_${d.source.id}`).node().transform.baseVal[0].matrix;
return m1.f;
}).attr("x2", d => {
var m2 = d3.select(`.child_${d.target.id}`).node().transform.baseVal[0].matrix;
return m2.e;
}).attr("y2", d => {
var m2 = d3.select(`.child_${d.target.id}`).node().transform.baseVal[0].matrix;
return m2.f;
});
}
function dragStarted(event, d) {
if (!event.active)
outerLayout.alphaTarget(0.3).restart();
innerlayout.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(event, d) {
d.fx = event.x;
d.fy = event.y;
}
function dragEnded(event, d) {
if (!event.active)
outerLayout.alphaTarget(0)
innerlayout.alphaTarget(0)
d.fx = undefined;
d.fy = undefined;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/6.5.0/d3.min.js"></script>

show checkbox inside node while hovering on that node

I wanted to show the checkbox when hovering on the node so that when the checkbox is clicked the name of that node should be shown at the top of the spider graph and if unticked then that name should disappear.
I used the mouseover event to show the checkbox but is not working
.on("mouseover", function(d) {
d3.select(this).transition()
.duration(200)
.style('cursor', 'pointer')
.html('<input type="checkbox" name="name" class="checkbox" />')
})
Here is the full code with demo
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.min.js"></script>
</head>
<body>
<svg className='spider-graph-svg'>
</svg>
<script>
var data = {
"name": "root#gmail.com",
"children": [{
"name": "Person Name 1",
"children": [{
"name": "Branch 4.1"
}, {
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}
]
}, {
"name": "Person name 2",
"children": [{
"name": "Branch 4.1"
}, {
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}
]
}, {
"name": "Person Name 3",
"children": [{
"name": "Branch 4.1"
}, {
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}
]
}, {
"name": "Person Name 4",
"children": [{
"name": "Branch 4.1"
}, {
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}
]
}]
};
const LAST_CHILDREN_WIDTH = 13;
let flagForChildren = false;
let groups = [];
data.children.forEach(d => {
let a = [];
if (d.children.length > 0) {
flagForChildren = true;
}
for (let i = 0; i < d.children.length; i += 2) {
let b = d.children.slice(i, i + 2);
if (b[0] && b[1]) {
a.push(Object.assign(b[0], {
children: [b[1]]
}));
} else {
let child = b[0];
if (i >= 6) {
child = Object.assign(child, {
children: [{
name: "..."
}]
});
}
a.push(child);
}
}
d.children = a;
groups.push(d);
});
data.children = groups;
let split_index = Math.round(data.children.length / 2);
let rectangleHeight = 45;
let leftData = {
name: data.name,
children: JSON.parse(JSON.stringify(data.children.slice(0, split_index)))
};
let leftDataArray = [];
leftDataArray.push(leftData);
// Right data
let rightData = {
name: data.name,
children: JSON.parse(JSON.stringify(data.children.slice(split_index)))
};
// Create d3 hierarchies
let right = d3.hierarchy(rightData);
let left = d3.hierarchy(leftData);
// Render both trees
drawTree(right, "right");
drawTree(left, "left");
// draw single tree
function drawTree(root, pos) {
let SWITCH_CONST = 1;
if (pos === "left") {
SWITCH_CONST = -1;
}
const margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = window.innerWidth - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
let svg = d3
.select("svg")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.right + margin.left)
.attr('view-box', '0 0 ' + (width + margin.right) + ' ' + (height + margin.top + margin.bottom))
.style("margin-top", "20px")
.style("margin-left", "88px");
const myTool = d3.select("body").append("div")
.attr("class", "mytooltip")
.style("opacity", "0")
.style("display", "none");;
// Shift the entire tree by half it's width
let g = svg.append("g").attr("transform", "translate(" + width / 2 + ",0)");
let deductWidthValue = flagForChildren ? 0 : width * 0.33;
// Create new default tree layout
let tree = d3
.tree()
// Set the size
// Remember the tree is rotated
// so the height is used as the width
// and the width as the height
.size([height - 50, SWITCH_CONST * (width - deductWidthValue) / 2])
.separation((a, b) => a.parent === b.parent ? 4 : 4.25);
tree(root);
let nodes = root.descendants();
let links = root.links();
// Set both root nodes to be dead center vertically
nodes[0].x = height / 2;
// Create links
let link = g
.selectAll(".link")
.data(links)
.enter();
link
.append("line")
.attr("class", function(d) {
if (d.target.depth === 2) {
return 'link'
} else {
return 'hard--link'
}
})
.attr("x1", function(d) {
if (
d.target.depth === 3
) {
return 0;
}
return d.source.y + 100 / 2; //d.source.y + 100/2
})
.attr("x2", function(d) {
if (
d.target.depth === 3
) {
return 0;
} else if (d.target.depth === 2) {
return d.target.y;
}
return d.target.y + 100 / 2; //d.target.y + 100/2;
})
.attr("y1", function(d) {
if (
d.target.depth === 3
) {
return 0;
}
return d.source.x + 50 / 2;
})
.attr("y2", function(d) {
if (
d.target.depth === 3
) {
return 0;
} else if (d.target.depth === 2) {
return d.target.x + LAST_CHILDREN_WIDTH / 2;
}
return d.target.x + 50 / 2;
});
//Rectangle width
let node = g
.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.on("mouseover", function(d) {
d3.select(this).transition()
.duration(200)
.style('cursor', 'pointer')
.html('<input type="checkbox" name="name" class="checkbox" />')
})
.attr("class", function(d) {
return "node" + (d.children ? " node--internal" : " node--leaf");
})
.attr("transform", function(d) {
if (d.parent && d.parent.parent) { // this is the leaf node
if (d.parent.parent.parent) {
return (
"translate(" +
d.parent.y +
"," +
(d.x + LAST_CHILDREN_WIDTH + 15) +
")"
);
}
return "translate(" + d.y + "," + d.x + ")";
}
return "translate(" + d.y + "," + d.x + ")";
});
// topic rect
node
.append("rect")
.attr("height", (d, i) => d.parent && d.parent.parent ? 15 : rectangleHeight)
.attr("width", (d, i) => d.parent && d.parent.parent ? 15 : rectangleWidth(d))
.attr("rx", (d, i) => d.parent && d.parent.parent ? 5 : 5)
.attr("ry", (d, i) => d.parent && d.parent.parent ? 5 : 5)
// topic edges
node.append('line')
.attr('x1', d => {
if (d.depth === 2) {
return 10
}
})
.attr('x2', d => {
if (d.depth === 2) {
return 10
}
})
.attr('y1', d => {
if (d.depth === 2) {
if (d.children) {
return 0;
}
return 40;
}
})
.attr('y2', d => {
if (d.depth === 2) {
return 40
}
})
.attr('class', 'hard--link')
// topic names
node
.append("text")
.attr("dy", function(d, i) {
return d.parent && d.parent.parent ? 10 : rectangleHeight / 2;
})
.attr("dx", function(d, i) {
if (!(d.parent && d.parent.parent)) {
return 12;
} else {
return 20;
}
})
.style("fill", function(d, i) {
return d.parent && d.parent.parent ? "Black" : "White";
})
.text(function(d) {
let name = d.data.topic_name || d.data.name;
return name.length > 12 ? `${name.substring(0, 12)}...` : name;
})
.style("text-anchor", function(d) {
if (d.parent && d.parent.parent) {
return pos === "left" && "end"
}
})
.style("font-size", "12")
.attr("transform", function(d) {
if (d.parent && d.parent.parent) {
return pos === "left" ? "translate(-30,0)" : "translate(5,0)"
}
})
}
function rectangleWidth(d) {
const MIN_WIDTH = 50;
const MAX_WIDTH = 100;
let dynamicLength = 6;
if (d.data.topic_name) {
dynamicLength = d.data.topic_name.length;
} else if (d.data.name) {
dynamicLength = d.data.name.length;
}
dynamicLength = dynamicLength < 3 ? MIN_WIDTH : MAX_WIDTH;
return dynamicLength;
}
</script>
</body>
</html>
Since the input element is not an svg element, you cannot simply insert a checkbox like and make it work like an html element. We would have to make use of foreignObject and append the input inside it.
Once you create those small rects, we need to append a foreignObject containing our checkboxes and hide them initially like so:
// topic rect
node
.append("rect")
.attr("height", (d, i) => d.parent && d.parent.parent ? 15 : rectangleHeight)
.attr("width", (d, i) => d.parent && d.parent.parent ? 15 : rectangleWidth(d))
.attr("rx", (d, i) => d.parent && d.parent.parent ? 5 : 5)
.attr("ry", (d, i) => d.parent && d.parent.parent ? 5 : 5)
node.append('foreignObject').attr('width', '40')
.attr('height', '40').append('xhtml:input')
.attr('type', 'checkbox').style('display', 'none')
// An on click function for the checkboxes
.on("click",function(d){
console.log(d)
});
When the user hovers over the rects we can then simply unhide the checkboxes, and hide them again if they are not checked (else keep showing until user unchecks) :
.on("mouseover", function(d) {
d3.select(this).select('input').style('display', '');
})
.on('mouseout', function(d) {
if (!d3.select(this).select('input').property('checked')) {
d3.select(this).select('input').style('display', 'none');
}
});
Here is the full code:
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width">
<title>JS Bin</title>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.2.3/d3.min.js"></script>
</head>
<body>
<svg className='spider-graph-svg'>
</svg>
<script>
var prevElem;
var data = {
"name": "root#gmail.com",
"children": [{
"name": "Person Name 1",
"children": [{
"name": "Branch 4.1"
}, {
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}
]
}, {
"name": "Person name 2",
"children": [{
"name": "Branch 4.1"
}, {
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}
]
}, {
"name": "Person Name 3",
"children": [{
"name": "Branch 4.1"
}, {
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}
]
}, {
"name": "Person Name 4",
"children": [{
"name": "Branch 4.1"
}, {
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}, {
"name": "Branch 4.2"
},
{
"name": "Branch 4.2"
}
]
}]
};
const LAST_CHILDREN_WIDTH = 13;
let flagForChildren = false;
let groups = [];
data.children.forEach(d => {
let a = [];
if (d.children.length > 0) {
flagForChildren = true;
}
for (let i = 0; i < d.children.length; i += 2) {
let b = d.children.slice(i, i + 2);
if (b[0] && b[1]) {
a.push(Object.assign(b[0], {
children: [b[1]]
}));
} else {
let child = b[0];
if (i >= 6) {
child = Object.assign(child, {
children: [{
name: "..."
}]
});
}
a.push(child);
}
}
d.children = a;
groups.push(d);
});
data.children = groups;
let split_index = Math.round(data.children.length / 2);
let rectangleHeight = 45;
let leftData = {
name: data.name,
children: JSON.parse(JSON.stringify(data.children.slice(0, split_index)))
};
let leftDataArray = [];
leftDataArray.push(leftData);
// Right data
let rightData = {
name: data.name,
children: JSON.parse(JSON.stringify(data.children.slice(split_index)))
};
// Create d3 hierarchies
let right = d3.hierarchy(rightData);
let left = d3.hierarchy(leftData);
// Render both trees
drawTree(right, "right");
drawTree(left, "left");
// draw single tree
function drawTree(root, pos) {
let SWITCH_CONST = 1;
if (pos === "left") {
SWITCH_CONST = -1;
}
const margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = window.innerWidth - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
let svg = d3
.select("svg")
.attr("height", height + margin.top + margin.bottom)
.attr("width", width + margin.right + margin.left)
.attr('view-box', '0 0 ' + (width + margin.right) + ' ' + (height + margin.top + margin.bottom))
.style("margin-top", "20px")
.style("margin-left", "88px");
const myTool = d3.select("body").append("div")
.attr("class", "mytooltip")
.style("opacity", "0")
.style("display", "none");;
// Shift the entire tree by half it's width
let g = svg.append("g").attr("transform", "translate(" + width / 2 + ",0)");
let deductWidthValue = flagForChildren ? 0 : width * 0.33;
// Create new default tree layout
let tree = d3
.tree()
// Set the size
// Remember the tree is rotated
// so the height is used as the width
// and the width as the height
.size([height - 50, SWITCH_CONST * (width - deductWidthValue) / 2])
.separation((a, b) => a.parent === b.parent ? 4 : 4.25);
tree(root);
let nodes = root.descendants();
let links = root.links();
// Set both root nodes to be dead center vertically
nodes[0].x = height / 2;
// Create links
let link = g
.selectAll(".link")
.data(links)
.enter();
link
.append("line")
.attr("class", function(d) {
if (d.target.depth === 2) {
return 'link'
} else {
return 'hard--link'
}
})
.attr("x1", function(d) {
if (
d.target.depth === 3
) {
return 0;
}
return d.source.y + 100 / 2; //d.source.y + 100/2
})
.attr("x2", function(d) {
if (
d.target.depth === 3
) {
return 0;
} else if (d.target.depth === 2) {
return d.target.y;
}
return d.target.y + 100 / 2; //d.target.y + 100/2;
})
.attr("y1", function(d) {
if (
d.target.depth === 3
) {
return 0;
}
return d.source.x + 50 / 2;
})
.attr("y2", function(d) {
if (
d.target.depth === 3
) {
return 0;
} else if (d.target.depth === 2) {
return d.target.x + LAST_CHILDREN_WIDTH / 2;
}
return d.target.x + 50 / 2;
});
//Rectangle width
let node = g
.selectAll(".node")
.data(nodes)
.enter()
.append("g")
.on("mouseover", function(d) {
d3.select(this).select('input').style('display', '')
})
.on('mouseout', function(d) {
if (!d3.select(this).select('input').property('checked')) {
d3.select(this).select('input').style('display', 'none')
}
})
.attr("class", function(d) {
return "node" + (d.children ? " node--internal" : " node--leaf");
})
.attr("transform", function(d) {
if (d.parent && d.parent.parent) { // this is the leaf node
if (d.parent.parent.parent) {
return (
"translate(" +
d.parent.y +
"," +
(d.x + LAST_CHILDREN_WIDTH + 15) +
")"
);
}
return "translate(" + d.y + "," + d.x + ")";
}
return "translate(" + d.y + "," + d.x + ")";
});
// topic rect
node
.append("rect")
.attr("height", (d, i) => d.parent && d.parent.parent ? 15 : rectangleHeight)
.attr("width", (d, i) => d.parent && d.parent.parent ? 15 : rectangleWidth(d))
.attr("rx", (d, i) => d.parent && d.parent.parent ? 5 : 5)
.attr("ry", (d, i) => d.parent && d.parent.parent ? 5 : 5)
// Create foreign object checkboxes and keep them hidden
node.append('foreignObject').attr('width', '40').attr('height', '40').append('xhtml:input').attr('type', 'checkbox').style('display', 'none')
// topic edges
node.append('line')
.attr('x1', d => {
if (d.depth === 2) {
return 10
}
})
.attr('x2', d => {
if (d.depth === 2) {
return 10
}
})
.attr('y1', d => {
if (d.depth === 2) {
if (d.children) {
return 0;
}
return 40;
}
})
.attr('y2', d => {
if (d.depth === 2) {
return 40
}
})
.attr('class', 'hard--link')
// topic names
node
.append("text")
.attr("dy", function(d, i) {
return d.parent && d.parent.parent ? 10 : rectangleHeight / 2;
})
.attr("dx", function(d, i) {
if (!(d.parent && d.parent.parent)) {
return 12;
} else {
return 20;
}
})
.style("fill", function(d, i) {
return d.parent && d.parent.parent ? "Black" : "White";
})
.text(function(d) {
let name = d.data.topic_name || d.data.name;
return name.length > 12 ? `${name.substring(0, 12)}...` : name;
})
.style("text-anchor", function(d) {
if (d.parent && d.parent.parent) {
return pos === "left" && "end"
}
})
.style("font-size", "12")
.attr("transform", function(d) {
if (d.parent && d.parent.parent) {
return pos === "left" ? "translate(-30,0)" : "translate(5,0)"
}
})
}
function rectangleWidth(d) {
const MIN_WIDTH = 50;
const MAX_WIDTH = 100;
let dynamicLength = 6;
if (d.data.topic_name) {
dynamicLength = d.data.topic_name.length;
} else if (d.data.name) {
dynamicLength = d.data.name.length;
}
dynamicLength = dynamicLength < 3 ? MIN_WIDTH : MAX_WIDTH;
return dynamicLength;
}
</script>
</body>
</html>
I'm sure you can do the rest from here.

How to avoid curved links in a d3 radial tree diagram?

I am working with this code to get a radial tree diagram for my data. However, I'd like to modify it to avoid curved links. Instead I am interested in linear straight connections. The curved links make the illustration to be less sophisticated specially when we have lower number of children nodes. For instance, you may look at the parent node and its links with the nodes on the first layer (circle). How can I use straight lines for these connections?
This is the part of the code I would like to modify to satisfy my needs:
var link = g.selectAll(".link")
.data(root.links())
.enter().append("path")
.attr("class", "link")
.attr("d", d3.linkRadial()
.angle(function(d) { return d.x; })
.radius(function(d) { return d.y; }));
where function is currently defined as
function radialPoint(x, y) {
return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];
}
Thanks.
To get linear straight connections, don't use a path generator - d3.linkRadial (or d3.linkHorizontal etc) - use a line:
var link = g.selectAll(".link")
.data(tree(root).links())
.enter().append("line")
.attr("class", "link")
.attr("stroke","#ccc")
.attr("x1", function(d) { return radialPoint(d.source.x,d.source.y)[0]; })
.attr("y1", function(d) { return radialPoint(d.source.x,d.source.y)[1]; })
.attr("x2", function(d) { return radialPoint(d.target.x,d.target.y)[0]; })
.attr("y2", function(d) { return radialPoint(d.target.x,d.target.y)[1]; }) ;
This will keep your links straight, the snippet below should demonstrate this.
var data = { "name": "Root", "children": [
{ "name": "A", "children": [ {"name": "A-1" }, {"name": "A-2" }, {"name":"A-3"}, {"name":"A-4"}, { "name":"A-5"} ] },
{ "name": "B", "children": [ {"name": "B-1" } ] },
{ "name": "C" },
{ "name": "D", "children": [ {"name": "D-1" }, {"name": "D-2" }, {"name": "D-3", "children": [ {"name": "D-3-i"}, {"name":"D-3-ii"} ] } ] },
{ "name": "E" },
{ "name": "F" }
] };
var width = 960;
var height = 500;
margin = {left: 100, top: 100, right: 50, bottom: 50}
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g").attr('transform','translate('+ width/2 +','+ height/2 +')');
var root = d3.hierarchy(data);
var tree = d3.tree()
.size([2 * Math.PI, height/2]);
var link = g.selectAll(".link")
.data(tree(root).links())
.enter().append("line")
.attr("class", "link")
.attr("stroke","#ccc")
.attr("x1", function(d) { return radialPoint(d.source.x,d.source.y)[0]; })
.attr("y1", function(d) { return radialPoint(d.source.x,d.source.y)[1]; })
.attr("x2", function(d) { return radialPoint(d.target.x,d.target.y)[0]; })
.attr("y2", function(d) { return radialPoint(d.target.x,d.target.y)[1]; })
;
var node = g.selectAll(".node")
.data(root.descendants())
.enter().append("g")
.attr("class", function(d) { return "node" + (d.children ? " node--internal" : " node--leaf"); })
.attr("transform", function(d) { return "translate(" + radialPoint(d.x, d.y) + ")"; })
node.append("circle")
.attr("r", 2.5);
node.append("text")
.text(function(d) { return d.data.name; })
.attr('y',-10)
.attr('x',-10)
.attr('text-anchor','middle');
function radialPoint(x, y) {
return [(y = +y) * Math.cos(x -= Math.PI / 2), y * Math.sin(x)];
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/4.5.0/d3.min.js"></script>

d3 - dougnut bubble pie chart

I am trying to create a merged pie/bubble chart.
-- looking for a starting bubble chart base - maybe this one.
http://jsfiddle.net/xsafy/
^ need to cluster these bubbles - maybe sub bubbles per slice.
//bubble chart base.
http://jsfiddle.net/NYEaX/1450/
(function() {
var diameter = 250;
var svg = d3.select('#graph').append('svg')
.attr('width', diameter)
.attr('height', diameter);
var bubble = d3.layout.pack()
.size([diameter, diameter])
.value(function(d) {
return d.size;
})
.padding(3);
var color = d3.scale.ordinal()
.domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing"])
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56"]);
function randomData() {
var data1 = {
"children": [
{
name: "AA",
className: "aa",
size: 170
},
{
name: "BB",
className: "bb",
size: 393
},
{
name: "CC",
className: "cc",
size: 293
},
{
name: "DD",
className: "dd",
size: 89
}
]
};
var data2 = {
"children": [
{
name: "AA",
className: "aa",
size: 120
},
{
name: "BB",
className: "bb",
size: 123
},
{
name: "CC",
className: "cc",
size: 193
},
{
name: "DD",
className: "dd",
size: 289
}
]
};
var j = Math.floor((Math.random() * 2) + 1);
console.log("j", j);
if (j == 1) {
return data1;
} else {
return data2;
}
}
change(randomData());
d3.select(".randomize")
.on("click", function() {
change(randomData());
});
function change(data) {
console.log("data", data);
// generate data with calculated layout values
var nodes = bubble.nodes(data)
.filter(function(d) {
return !d.children;
}); // filter out the outer bubble
var vis = svg.selectAll('circle')
.data(nodes);
vis.enter()
.insert("circle")
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', function(d) {
return d.r;
})
.style("fill", function(d) {
return color(d.name);
})
.attr('class', function(d) {
return d.className;
});
vis
.transition().duration(1000)
vis.exit()
.remove();
};
})();
//doughnut chart base.
derived from these examples.
http://bl.ocks.org/dbuezas/9306799
https://bl.ocks.org/mbostock/1346410
http://jsfiddle.net/NYEaX/1452/
var svg = d3.select("#graph")
.append("svg")
.append("g")
svg.append("g")
.attr("class", "slices");
svg.append("g")
.attr("class", "labels");
svg.append("g")
.attr("class", "lines");
var width = 560,
height = 450,
radius = Math.min(width, height) / 2;
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.outerRadius(radius * 0.85)
.innerRadius(radius * 0.83);
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
svg.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var key = function(d) {
return d.data.label;
};
var color = d3.scale.ordinal()
.domain(["Lorem ipsum", "dolor sit", "amet", "consectetur", "adipisicing"])
.range(["#98abc5", "#8a89a6", "#7b6888", "#6b486b", "#a05d56"]);
function randomData() {
var data1 = [{
"label": "AA",
"value": 0.911035425558026
}, {
"label": "BB",
"value": 0.08175111844879179
}, {
"label": "CC",
"value": 0.25262439557273275
}, {
"label": "DD",
"value": 0.8301366989535612
}, {
"label": "EE",
"value": 0.0517762265780517
}];
var data2 = [{
"label": "AA",
"value": 0.243879179
}, {
"label": "BB",
"value": 0.243879179
}, {
"label": "CC",
"value": 0.2342439557273275
}, {
"label": "DD",
"value": 0.2349535612
}, {
"label": "EE",
"value": 0.2345780517
}];
var j = Math.floor((Math.random() * 2) + 1);
if (j == 1) {
return data1;
} else {
return data2;
}
}
change(randomData());
d3.select(".randomize")
.on("click", function() {
change(randomData());
});
function change(data) {
/* ------- PIE SLICES -------*/
var slice = svg.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) {
return color(d.data.label);
})
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
};
This is as close I have got so far.. I've merged the two charts together as such - although there is a bug still trying to update the bubbles -- at least get them to scale/morph/move/animate. Ideally I want them to stick close to their group segments..
here is the fiddle.
https://jsfiddle.net/tk5xog0g/1/
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>doughpie</title>
<link rel="stylesheet" href="css/generic.css">
<script src="https://ajax.googleapis.com/ajax/libs/jquery/3.1.0/jquery.min.js"></script>
<script src="http://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<button class="randomize">randomize</button>
<div data-role="doughpie" data-width="450" data-height="450" id="graph"></div>
<script>
//__invoke pie chart
$('[data-role="doughpie"]').each(function(index) {
createDoughnut(this);
});
function bubbledata(data){
//loop through data -- and MERGE children
var childs = [];
$.each(data, function( index, value ) {
childs.push(value.children);
});
var merged = [].concat.apply([], childs);//flatterns multidimensional array
return $.extend(true, {}, {"children": merged});// return deep clone
}
function createDoughnut(el){
var width = $(el).data("width"),
height = $(el).data("height"),
radius = Math.min(width, height) / 2;
var svg = d3.select($(el)[0])
.append("svg")
.attr("width", width)
.attr("height", height)
//_create doughpie shell
var doughpie = svg.append("g")
.attr("class", "doughpie");
doughpie.append("g")
.attr("class", "slices");
doughpie.append("g")
.attr("class", "labels");
doughpie.append("g")
.attr("class", "lines");
var pie = d3.layout.pie()
.sort(null)
.value(function(d) {
return d.value;
});
var arc = d3.svg.arc()
.outerRadius(radius * 0.85)
.innerRadius(radius * 0.83);
var outerArc = d3.svg.arc()
.innerRadius(radius * 0.9)
.outerRadius(radius * 0.9);
doughpie.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
var key = function(d) {
return d.data.label;
};
var color = d3.scale.ordinal()
.range(["#46a2de", "#7b3cce", "#31d99c", "#de5942", "#ffa618"]);
//_create doughpie shell
//_create bubble
var diameter = width/2;//take half/width
var bubs = svg.append("g")
.attr("class", "bubs");
bubs.attr("transform", "translate(" + diameter / 2 + "," + diameter / 2 + ")");
var bubble = d3.layout.pack()
.size([diameter, diameter])
.value(function(d) {
return d.size;
})
.padding(3);
//_create bubble
function randomData() {
var data1 = [{
"label": "AA",
"value": 0.911035425558026,
"children": [
{
name: "some text aa",
group: "AA",
size: 120
}
]
}, {
"label": "BB",
"value": 0.08175111844879179,
"children": [
{
name: "some text bb",
group: "BB",
size: 123
}
]
}, {
"label": "CC",
"value": 0.25262439557273275,
"children": [
{
name: "some text cc",
group: "CC",
size: 193
}
]
}, {
"label": "DD",
"value": 0.8301366989535612,
"children": [
{
name: "some text dd",
group: "DD",
size: 29
},
{
name: "some text dd",
group: "DD",
size: 289
}
]
}, {
"label": "EE",
"value": 0.0517762265780517,
"children": [
{
name: "some text ee",
group: "EE",
size: 389
},
{
name: "some text ee",
group: "EE",
size: 89
}
]
}];
var data2 = [{
"label": "AA",
"value": 0.243879179,
"children": [
{
name: "some text aa",
group: "AA",
size: 120
}
]
}, {
"label": "BB",
"value": 0.243879179,
"children": [
{
name: "some text bb",
group: "BB",
size: 123
}
]
}, {
"label": "CC",
"value": 0.2342439557273275,
"children": [
{
name: "some text cc",
group: "CC",
size: 193
}
]
}, {
"label": "DD",
"value": 0.2349535612,
"children": [
{
name: "some text dd",
group: "DD",
size: 29
},
{
name: "some text dd",
group: "DD",
size: 289
}
]
}, {
"label": "EE",
"value": 0.2345780517,
"children": [
{
name: "some text ee",
group: "EE",
size: 389
},
{
name: "some text ee",
group: "EE",
size: 89
}
]
}];
var j = Math.floor((Math.random() * 2) + 1);
if (j == 1) {
return data1;
} else {
return data2;
}
}
change(randomData());
d3.select(".randomize")
.on("click", function() {
change(randomData());
});
function change(data) {
/* ------- ANIMATE PIE SLICES -------*/
var slice = doughpie.select(".slices").selectAll("path.slice")
.data(pie(data), key);
slice.enter()
.insert("path")
.style("fill", function(d) {
return color(d.data.label);
})
.style("transform", function(d, i){
//return "translate(0, 0)";
})
.attr("class", "slice");
slice
.transition().duration(1000)
.attrTween("d", function(d) {
this._current = this._current || d;
var interpolate = d3.interpolate(this._current, d);
this._current = interpolate(0);
return function(t) {
return arc(interpolate(t));
};
})
slice.exit()
.remove();
/* ------- ANIMATE PIE SLICES -------*/
/* ------- ANIMATE BUBBLES -------*/
// generate data with calculated layout values
var data = bubbledata(data);
var nodes = bubble.nodes(data)
.filter(function(d) {
return !d.children;
}); // filter out the outer bubble
var bubbles = bubs.selectAll('circle')
.data(nodes);
bubbles.enter()
.insert("circle")
.attr('transform', function(d) {
return 'translate(' + d.x + ',' + d.y + ')';
})
.attr('r', function(d) {
return d.r;
})
.style("fill", function(d) {
return color(d.group);
});
bubbles
.transition().duration(1000)
bubbles.exit()
.remove();
/* ------- ANIMATE BUBBLES -------*/
};
}
</script>
</body>
</html>
I thought I will try and first plot mid dot points on the arcs -- and maybe from THOSE -- start to get a batch of zone coordinates to master control the coloured bubbles.
I've been trying to follow this -- How to get coordinates of slices along the edge of a pie chart?
https://jsfiddle.net/tk5xog0g/28/
/*placing mid dots*/
var midDotsArrayCooridnates = [];
slice
.each(function(d) {
x = 0 + (radius * 0.85) * Math.cos(d.startAngle);
y = 0 + (radius * 0.85) * Math.sin(d.startAngle);
var obj = {
"x": x,
"y": y
}
midDotsArrayCooridnates.push(obj);
});
$.each(midDotsArrayCooridnates, function(index, value) {
var dot = doughpie.select(".slicedots").append('circle')
.attr('cx', value.x)
.attr('cy', value.y)
.attr('r', 5)
.style("fill", "red")
.attr('class', "ccc")
});
/*placing mid dots*/

Categories