I was working on one of my topic modeling project where I try to use D3.js to visualize the result. And sadly this is my first experience with D3.js
I tried to link the main topics to their subtopics, so I followed the tutorial about how to use "force simulation" in D3. And now the nodes looks very good, however, the link never shows up.
Compare to the tutorial, the only difference is in this project I have to fix x-axis since all topic was binding to a time slot.
Also, after calling "function restart()", my text in the small bubble disappeared.
Please give me some advice on this.
data= [
{id: "Topic1", date: "2017-08-21", name: "Topic1", count: .4, subtopics: ["sub1", "sub2", "sub3", "sub5"]},
{id: "Topic2", date: "2017-08-23", name: "Topic2", count: 1, subtopics: ["sub3", "sub6", "sub7", "sub8"]},
{id: "Topic3", date: "2017-08-25", name: "Topic3",count: 2, subtopics: ["sub7", "sub9"]},
{id: "Topic4", date: "2017-08-27", name: "Topic4", count: 2, subtopics: ["sub8"]},
{id: "sub1",date:"2017-08-21", name:"sub1", count: .1, subtopics: []},
{id: "sub2",date:"2017-08-22", name:"sub2", count: .2, subtopics: []},
{id: "sub3",date:"2017-08-22", name:"sub3", count: .2, subtopics: []},
{id: "sub4",date:"2017-08-28", name:"sub4", count: .1, subtopics: []},
{id: "sub5",date:"2017-08-20", name:"sub5", count: .2, subtopics: []},
{id: "sub6",date:"2017-08-23", name:"sub6", count: .1, subtopics: []},
{id: "sub7",date:"2017-08-24", name:"sub7", count: .3, subtopics: []},
{id: "sub8",date:"2017-08-24", name:"sub8", count: .1, subtopics: []},
{id: "sub9",date:"2017-08-25", name:"sub9", count: .1, subtopics: []},
{id: "sub10",date:"2017-08-27", name:"sub10", count: .1, subtopics: []},
{id: "sub11",date:"2017-08-29", name:"sub11", count: .1, subtopics: []},
{id: "sub12",date:"2017-08-30", name:"sub12", count: .4, subtopics: []},
]
var vis_node = [];
var vis_link = [];
var r = d3.scaleSqrt()
.domain([0, d3.max(data, function (d) {
return d.count;
})])
.range([0, 65]);
var margin = {top: 50, right: 20, bottom: 100, left: 50},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//map elements to time
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
//separete big topic and subtopics and setup the visualization data
var parents_node = data.filter(function(d){return d.subtopics.length != 0;});
var child_node = data.filter(function(d){return d.subtopics.length == 0;});
vis_node = data;
//link all the big topic with common subtopic
for(i = 0;i<data.length;i++){
var tmps = data[i].subtopics
for(j = 0; j < tmps.length; j++){
for(k = i+1; k < data.length;k++){
if(data[k].subtopics.includes(tmps[j])){
vis_link.push({source: data[i], target: data[k]})
continue;
}
}
}
}
//link all the big topic with its subtopic
for(i = 0;i<parents_node.length;i++){
for(j = 0;j<child_node.length;j++){
if(parents_node[i].subtopics.includes(child_node[j].name)){
vis_link.push({source: parents_node[i], target: child_node[j]});
}
}
}
//setup a force field for the d3
var simulation = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(-700).distanceMin(100).distanceMax(280))
.force("link", d3.forceLink().id(function(d) { return d.index }))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("y", d3.forceY(0.0001))
.force("x", d3.forceX(0.0001))
var svg = d3.select('body').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom),
g = svg.append('g')
.attr('transform','translate(' + margin.left + ',' + margin.top + ')');
var formatNumber = d3.format('');
var x = d3.scaleTime()
.range([0, width]);
x.domain(d3.extent(data, function(d) { return d.date; }));
//begin to render the circle
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y-%m-%d")))
var node = g.selectAll('.node')
.data(vis_node)
.enter().append('g')
.attr("class", "node");
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append('circle')
.attr('r', function(d) {return Math.max(16, r(d.count)); })
.style("stroke", function(d){
if(d.subtopics.length == 0){
return "blue"
}
else{
return "pink"
}
})
.style("fill", "transparent");
node.append('text')
.text(function(d){return d.name})
.attr("text-anchor", "middle")
.style('fill', function(d){
if(d.subtopics.length == 0){
return "darkblue"
}
else{
return "darkred"
}
})
.style('font-size','20px')
.attr("pointer-events", "none");
node.on("click", function(d){
var subs = d.subtopics
child_node.forEach(function(d){
if(subs.includes(d.name)){
if(vis_node.includes(d)){
var index = vis_node.indexOf(d)
vis_node.splice(index, 1)
}
else{
vis_node.push(d);
}
}
})
restart();
});
//begin to render a link
var link = g.selectAll('.link')
.data(vis_link)
.enter().append('g')
.attr('class','link')
link.append('line')
.attr("stroke","black")
//when click on the big topic call restart function to redraw everything
function restart(){
vis_node.forEach(function(d){console.log(d.name)})
node = node.data(vis_node)
node.exit().remove()
node = node.enter().append('circle')
.attr('r', function(d) {return Math.max(16, r(d.count)); })
.style("stroke", function(d){
if(d.subtopics.length == 0){
return "blue"
}
else{
return "pink"
}
})
.style("fill", "transparent")
.merge(node);
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
node.append('text')
.text(function(d){return d.name})
.attr("text-anchor", "middle")
.style('fill', function(d){
if(d.subtopics.length == 0){
return "darkblue"
}
else{
return "darkred"
}
})
.style('font-size','20px')
.attr("pointer-events", "none");
simulation.nodes(vis_node);
simulation.alphaTarget(0.3).restart();
}
//I think something wrong here
var ticked = function() {
node.attr("transform", function (d) {
return "translate(" + x(d.date) + "," + d.y + ")";
})
link.attr("x1", function (d) {return x(d.source.date); })
.attr("y1", function (d) {return d.source.y;})
.attr("x2", function (d) {return x(d.target.date)})
.attr("y2", function (d) {return d.target.y;});
}
//add link and node to the force field
simulation.force("link").links(vis_link);
simulation.nodes(vis_node);
simulation.on("tick", ticked);
//drag related functions
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<title>Topic Explorer</title>
<base href="/">
<meta http-equiv="content-type" content="text/html;charset=utf-8"/>
<link rel="stylesheet" href="./styles/simple-style.css">
</head>
<body style="margin:10px 0">
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="./scripts/test.js"></script>
</body>
</html>
Since your link selection is a selection of groups...
var link = g.selectAll('.link')
.data(vis_link)
.enter().append('g')
.attr('class', 'link');
... you're applying the x1, x2, y1 and y2 attributes to the groups, not to the lines.
An easy solution is just naming another selection, just for the lines:
var line = link.append('line')
.attr("stroke", "black");
Here is your modified code:
data = [{
id: "Topic1",
date: "2017-08-21",
name: "Topic1",
count: .4,
subtopics: ["sub1", "sub2", "sub3", "sub5"]
},
{
id: "Topic2",
date: "2017-08-23",
name: "Topic2",
count: 1,
subtopics: ["sub3", "sub6", "sub7", "sub8"]
},
{
id: "Topic3",
date: "2017-08-25",
name: "Topic3",
count: 2,
subtopics: ["sub7", "sub9"]
},
{
id: "Topic4",
date: "2017-08-27",
name: "Topic4",
count: 2,
subtopics: ["sub8"]
},
{
id: "sub1",
date: "2017-08-21",
name: "sub1",
count: .1,
subtopics: []
},
{
id: "sub2",
date: "2017-08-22",
name: "sub2",
count: .2,
subtopics: []
},
{
id: "sub3",
date: "2017-08-22",
name: "sub3",
count: .2,
subtopics: []
},
{
id: "sub4",
date: "2017-08-28",
name: "sub4",
count: .1,
subtopics: []
},
{
id: "sub5",
date: "2017-08-20",
name: "sub5",
count: .2,
subtopics: []
},
{
id: "sub6",
date: "2017-08-23",
name: "sub6",
count: .1,
subtopics: []
},
{
id: "sub7",
date: "2017-08-24",
name: "sub7",
count: .3,
subtopics: []
},
{
id: "sub8",
date: "2017-08-24",
name: "sub8",
count: .1,
subtopics: []
},
{
id: "sub9",
date: "2017-08-25",
name: "sub9",
count: .1,
subtopics: []
},
{
id: "sub10",
date: "2017-08-27",
name: "sub10",
count: .1,
subtopics: []
},
{
id: "sub11",
date: "2017-08-29",
name: "sub11",
count: .1,
subtopics: []
},
{
id: "sub12",
date: "2017-08-30",
name: "sub12",
count: .4,
subtopics: []
},
]
var vis_node = [];
var vis_link = [];
var r = d3.scaleSqrt()
.domain([0, d3.max(data, function(d) {
return d.count;
})])
.range([0, 65]);
var margin = {
top: 50,
right: 20,
bottom: 100,
left: 50
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
//map elements to time
var parseTime = d3.timeParse("%Y-%m-%d");
data.forEach(function(d) {
d.date = parseTime(d.date);
d.close = +d.close;
});
//separete big topic and subtopics and setup the visualization data
var parents_node = data.filter(function(d) {
return d.subtopics.length != 0;
});
var child_node = data.filter(function(d) {
return d.subtopics.length == 0;
});
vis_node = data;
//link all the big topic with common subtopic
for (i = 0; i < data.length; i++) {
var tmps = data[i].subtopics
for (j = 0; j < tmps.length; j++) {
for (k = i + 1; k < data.length; k++) {
if (data[k].subtopics.includes(tmps[j])) {
vis_link.push({
source: data[i],
target: data[k]
})
continue;
}
}
}
}
//link all the big topic with its subtopic
for (i = 0; i < parents_node.length; i++) {
for (j = 0; j < child_node.length; j++) {
if (parents_node[i].subtopics.includes(child_node[j].name)) {
vis_link.push({
source: parents_node[i],
target: child_node[j]
});
}
}
}
//setup a force field for the d3
var simulation = d3.forceSimulation()
.force("charge", d3.forceManyBody().strength(-700).distanceMin(100).distanceMax(280))
.force("link", d3.forceLink().id(function(d) {
return d.index
}))
.force("center", d3.forceCenter(width / 2, height / 2))
.force("y", d3.forceY(0.0001))
.force("x", d3.forceX(0.0001))
var svg = d3.select('body').append('svg')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom),
g = svg.append('g')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var formatNumber = d3.format('');
var x = d3.scaleTime()
.range([0, width]);
x.domain(d3.extent(data, function(d) {
return d.date;
}));
//begin to render the circle
svg.append("g")
.attr("class", "axis")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x)
.tickFormat(d3.timeFormat("%Y-%m-%d")))
var node = g.selectAll('.node')
.data(vis_node)
.enter().append('g')
.attr("class", "node");
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append('circle')
.attr('r', function(d) {
return Math.max(16, r(d.count));
})
.style("stroke", function(d) {
if (d.subtopics.length == 0) {
return "blue"
} else {
return "pink"
}
})
.style("fill", "transparent");
node.append('text')
.text(function(d) {
return d.name
})
.attr("text-anchor", "middle")
.style('fill', function(d) {
if (d.subtopics.length == 0) {
return "darkblue"
} else {
return "darkred"
}
})
.style('font-size', '20px')
.attr("pointer-events", "none");
node.on("click", function(d) {
var subs = d.subtopics
child_node.forEach(function(d) {
if (subs.includes(d.name)) {
if (vis_node.includes(d)) {
var index = vis_node.indexOf(d)
vis_node.splice(index, 1)
} else {
vis_node.push(d);
}
}
})
restart();
});
//begin to render a link
var link = g.selectAll('.link')
.data(vis_link)
.enter().append('g')
.attr('class', 'link')
var line = link.append('line')
.attr("stroke", "black")
//when click on the big topic call restart function to redraw everything
function restart() {
vis_node.forEach(function(d) {
console.log(d.name)
})
node = node.data(vis_node)
node.exit().remove()
node = node.enter().append('circle')
.attr('r', function(d) {
return Math.max(16, r(d.count));
})
.style("stroke", function(d) {
if (d.subtopics.length == 0) {
return "blue"
} else {
return "pink"
}
})
.style("fill", "transparent")
.merge(node);
node.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
node.append('text')
.text(function(d) {
return d.name
})
.attr("text-anchor", "middle")
.style('fill', function(d) {
if (d.subtopics.length == 0) {
return "darkblue"
} else {
return "darkred"
}
})
.style('font-size', '20px')
.attr("pointer-events", "none");
simulation.nodes(vis_node);
simulation.alphaTarget(0.3).restart();
}
//I think something wrong here
var ticked = function() {
node.attr("transform", function(d) {
return "translate(" + x(d.date) + "," + d.y + ")";
})
line.attr("x1", function(d) {
return x(d.source.date);
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return x(d.target.date)
})
.attr("y2", function(d) {
return d.target.y;
});
}
//add link and node to the force field
simulation.force("link").links(vis_link);
simulation.nodes(vis_node);
simulation.on("tick", ticked);
//drag related functions
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
<!DOCTYPE html>
<meta charset="utf-8">
<head>
<title>Topic Explorer</title>
<base href="/">
<meta http-equiv="content-type" content="text/html;charset=utf-8" />
<link rel="stylesheet" href="./styles/simple-style.css">
</head>
<body style="margin:10px 0">
</div>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script src="./scripts/test.js"></script>
</body>
</html>
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>
I'm new to d3 and when I learnt how to draw a force chart, I had some problems about it. And at first, let we see my code here:
<html>
<head>
<meta charset="UTF-8">
<title>the force chart</title>
</head>
<body>
<script src="http://d3js.org/d3.v4.min.js"></script>
<script>
var width = 400;
var height = 400;
var svg = d3.select("body")
.append("svg")
.attr("width",width)
.attr("height",height);
var nodes = [ { "id": "English" },{ "id": "Italy" },
{ "id": "America" },{ "id": "Canada" },
{ "id": "Australia" },{ "id": "Japan" },
{ "id": "China" } ];
var edges = [ { "source": 0 , "target": 1 } , { "source": 0 , "target": 2 },
{ "source": 0 , "target": 3 } , { "source": 1 , "target": 4 },
{ "source": 1 , "target": 5 } , { "source": 1 , "target": 6 }, ];
var force = d3.forceSimulation(nodes)
.force("link",d3.forceLink()
.id(function(d){return d.id})
.distance(function(d){return 150}).strength([-400]))
.force("charge",d3.forceManyBody())
.force("center",d3.forceCenter(width , height));
force.restart(); //start
var svg_edges = svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke","#ccc")
.style("stroke-width",1);
var color = d3.scaleOrdinal(d3.schemeCategory20);
//add nodes
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("r",20)
.style("fill",function(d,i){
return color(i);
})
.call(d3.drag()); //to drag the nodes
//add information
var svg_texts = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d){
return d.id;
});
force.on("tick", function(){ //update the position of lines
svg_edges.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; })
//update the position of nodes
svg_nodes.attr("cx",function(d){ return d.x; })
.attr("cy",function(d){ return d.y; });
//update the position of information
svg_texts.attr("x",function(d){ return d.x; })
.attr("y",function(d){ return d.y; });
});
</script>
</body>
</html>
I want to draw a picture like this:
But my code can only show one node, just like this:
So I feel confused, because there is no error in Developer Tools. As I layout the force, I infer to https://github.com/d3/d3-force/blob/master/README.md#links . So I solve the problem which is result from the different versions. But why it still doesn't work? Could you help me? I'm very appreciate it if you help me! Thank you!
Besides the points already explained by #Vinod:
.append("circle")
and
.force("center", d3.forceCenter(width/2, height/2));
You have a trailing comma. But the most important is this, to show the edges:
First, add the edges to the simulation:
force.force("link")
.links(edges);
And then, change the links id. Right now, there is no property called id. So, it should be:
.force("link", d3.forceLink()
.id(function(d,i) {
return i
})
//the rest of the function
Here is a demo:
var width = 400;
var height = 400;
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var nodes = [{
"id": "English"
}, {
"id": "Italy"
}, {
"id": "America"
}, {
"id": "Canada"
}, {
"id": "Australia"
}, {
"id": "Japan"
}, {
"id": "China"
}];
var edges = [{
"source": 0,
"target": 1
}, {
"source": 0,
"target": 2
}, {
"source": 0,
"target": 3
}, {
"source": 1,
"target": 4
}, {
"source": 1,
"target": 5
}, {
"source": 1,
"target": 6
}];
var force = d3.forceSimulation()
.force("link", d3.forceLink()
.id(function(d,i) {
return i
})
.distance(function(d) {
return 150
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width/2, height/2));
force.restart(); //start
var svg_edges = svg.selectAll("line")
.data(edges)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", 1);
var color = d3.scaleOrdinal(d3.schemeCategory20);
//add nodes
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r", 20)
.style("fill", function(d, i) {
return color(i);
})
.call(d3.drag()); //to drag the nodes
//add information
var svg_texts = svg.selectAll("text")
.data(nodes)
.enter()
.append("text")
.style("fill", "black")
.attr("dx", 20)
.attr("dy", 8)
.text(function(d) {
return d.id;
});
force.nodes(nodes);
force.force("link")
.links(edges);
force.on("tick", function() { //update the position of lines
svg_edges.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;
})
//update the position of nodes
svg_nodes.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
});
//update the position of information
svg_texts.attr("x", function(d) {
return d.x;
})
.attr("y", function(d) {
return d.y;
});
});
<script src="https://d3js.org/d3.v4.min.js"></script>
There are several mistakes in your code
firstly you need to append circle like this
var svg_nodes = svg.selectAll("circle")
.data(nodes)
.enter()
.append("circle")
.attr("r",20)
.style("fill",function(d,i){
return color(i);
})
.call(d3.drag()); //to drag the nodes
where as your code append r which is no SVG tag moreover the center of the graph should not be width and height it should be width/2 and height/2 to make it at center
See this fiddle http://jsfiddle.net/Qh9X5/9515/
similarly for lines you are using edges data which don't have the x,y values you need to pass the xy value for drawing lines
For a complete solution note in v3.3 See this https://jsfiddle.net/3uehrfj8/1/ with all nodes and edges
<!DOCTYPE html>
<html>
<head>
<title></title>
<script src="d3.min.js"></script>
</head>
<body>
<script>
var color = d3.scale.category10();
var canvas = d3.select('body').append('svg')
.attr('width',800)
.attr('height',500)
d3.json('mydata.json',function (data){
var treemap = d3.layout.treemap()
.size([800,500])
.nodes(data)
var cells = canvas.selectAll(".cell")
.data(treemap)
.enter()
.append("g")
.attr("class","cell")
cells.append("rect")
.attr("x",function (d) { return d.x; })
.attr("y",function (d) { return d.y; })
.attr("width",function (d) { return d.dx; })
.attr("height",function (d) { return d.dy; })
.attr("fill",function (d) { return d.children ? null : color(d.parent.name); })
.attr("stroke",'#fff')
cells.append("text")
.attr("x",function (d) { return d.x + d.dx / 2 })
.attr("y",function (d) { return d.y + d.dy / 2 })
.attr("text-anchor", "middle")
.text(function (d) { return d.children ? null : d.name; })
})
</script>
</body>
</html>
this is my d3 code for creating treemap..in text part i have to display multiple text.,now it display d.name alone but i have to dispaly d.name and d.value...how to display multiple text in d3 treemap?
{
"name": "Max",
"value": 100,
"children": [
{
"name": "Sylvia",
"value": 75,
"children": [
{"name": "Craig","value": 25},
{"name": "Robin","value": 25},
{"name": "Anna","value": 25}
]
},
{
"name": "David",
"value": 75,
"children":[
{"name": "jeff", "value": 25},
{"name": "Buffy", "value": 25}
]
},
{
"name":"Mr X",
"value":75
}
]
}
this is my json file.
Simply add another text for values and adjust the y.
cells.append("text")
.attr("x",function (d) { return d.x + d.dx / 2 })
.attr("y",function (d) { return d.y + d.dy / 2 + 15 })//15 pixels below the name label.
.attr("text-anchor", "middle")
.text(function (d) { return d.children ? null : d.value; })
working code here
I have a treemap rendered with d3. Since I want to be responsive and economical (not running js if I do not really have to) I am using percentages for the divs. But the transitions are some kind of wired using percentages. After reading this issue I have tried several styleTweens but I do not have any luck ...
How can I use transitions for percentage values in d3?
Here is a fiddle of the below code: http://jsfiddle.net/0z7p68wb/ (just click somewhere on the treemap to start the animation)
var target = d3.select("#target")
render = function(data, oldData) {
// our custom d3 code
console.log("render!", data, oldData);
// draw rectangles
var margin = {margin: 0.2, padding: 2},
width = 100 - margin.margin * 2,
height = 100 - margin.margin * 2;
var treemap = d3.layout.treemap()
.size([100, 100])
//.sticky(true)
.value(function(d) { return d.size; });
// bind data
var nodes = target.datum(data)
.selectAll(".node")
.data(treemap.nodes);
// transform existing nodes
if (data !== oldData)
nodes.transition()
.duration(1500)
.call(position);
// append new nodes
nodes.enter().append("div")
.attr("class", "node")
.style("position", "absolute")
.style("display", function(d,i) { return i==0 ? "none" : "block"})
.style("background-color", "silver")
.call(position)
;
// remove obsolete nodes
nodes.exit().remove();
// set position of nodes
function position() {
this.style("left", function(d) { return d.x + "%"; })
.style("top", function(d) { return d.y + "%"; })
.style("width", function(d) { return Math.max(0, d.dx) + "%"; })
.style("height", function(d) { return Math.max(0, d.dy) + "%"; })
}
}
tree1 = {
name: "tree",
children: [
{ name: "Word-wrapping comes for free in HTML", size: 16000 },
{ name: "animate makes things fun", size: 8000 },
{ name: "data data everywhere...", size: 5220 },
{ name: "display something beautiful", size: 3623 },
{ name: "flex your muscles", size: 984 },
{ name: "physics is religion", size: 6410 },
{ name: "query and you get the answer", size: 2124 }
]
};
tree2 = {
name: "tree",
children: [
{ name: "Word-wrapping comes for free in HTML", size: 8000 },
{ name: "animate makes things fun", size: 10000 },
{ name: "data data everywhere...", size: 2220 },
{ name: "display something beautiful", size: 6623 },
{ name: "flex your muscles", size: 1984 },
{ name: "physics is religion", size: 3410 },
{ name: "query and you get the answer", size: 2124 }
]
};
tree = tree1;
render(tree, tree);
d3.select("#target").on("click", function(){
console.log("click");
tree = tree == tree1 ? tree2 : tree1;
render(tree, {});
});
Got it!
// transform existing nodes
if (data !== oldData)
nodes.transition()
.duration(1500)
.call(position)
.styleTween('left', function(d,i,a){
return d3.interpolateString(this.style.left, d.x + "%")
})
.styleTween('top', function(d,i,a){;
return d3.interpolateString(this.style.top, d.y + "%")
})
.styleTween('width', function(d,i,a){;
return d3.interpolateString(this.style.width, Math.max(0, d.dx) + "%")
})
.styleTween('height', function(d,i,a){;
return d3.interpolateString(this.style.height, Math.max(0, d.dy) + "%")
})
;