Text not displaying in the middle of circles in d3js - javascript

The following code packs circles in d3js.
csjson = {
"children": [{
"name": "DirtySprite",
"count": 8833
}, {
"name": "LineSprite",
"count": 1732
}]
};
var diameter = 200;
var pack = d3.layout.pack().size([diameter - 4, diameter - 4]).padding(2).sort(function(a, b) {
return Math.random();
}).value(function(d) {
return d.count;
});
var svg = d3.select("body").append("svg").attr("width", diameter).attr("height", diameter);
var vis = svg.datum((csjson)).selectAll(".node").data(pack.nodes).enter().append("g");
var titles = vis.append("title")
.attr("x", function(d) {
return d.x;
}).attr("y", function(d) {
return d.y;
}).text(function(d) {
return d.children ? "" : (d.name + ' ' + d.count);
});
var circles = vis.append("circle")
.attr("stroke", "black")
.style("fill", function(d) {
return !d.children ? "tan" : "beige";
})
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", function(d) {
return d.r;
});
var textLabels = vis.filter(function(d) {
return !d.children;
}).append("text").style("text-anchor", "middle")
.text(function(d) {
return d.name.substring(0, d.r / 3);
});
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
</body>
The circles form correctly however the labels are displaying on the upper-left corner of the screen rather than in the center of the circles.
var textLabels = vis.filter(function(d) {
return !d.children;
}).append("text").style("text-anchor", "middle")
.text(function(d) {
return d.name.substring(0, d.r / 3);
});
How to display the text in the middle of the circles?

The problem is because you are not setting the x and y value to the text DOM.
Instead of this:
var textLabels = vis.filter(function(d) {
return !d.children;
}).append("text").style("text-anchor", "middle")
.text(function(d) {
return d.name.substring(0, d.r / 3);
});
It has to be
var textLabels = vis.filter(function(d) {
return !d.children;
}).append("text").style("text-anchor", "middle") .attr("x", function(d) {
return d.x;
}).attr("y", function(d) {
return d.y;
})
.text(function(d) {
return d.name.substring(0, d.r / 3);
});
working fiddle here

You have to set the x and y position to the texts:
var textLabels = vis.filter(function(d) {
return !d.children;
}).append("text").style("text-anchor", "middle")
.attr("x", function(d) {
return d.x;
}).attr("y", function(d) {
return d.y;
}).text(function(d) {
return d.name.substring(0, d.r / 3);
});
This is just the generally principle, you may need to adjust x and y according to your needs.

Related

Insert text inside Circle in D3 chart

I want to insert some text inside my Circle which is plot in bubble chart using D3.js.
I am able to draw circle in svg as per the data provided to it, but facing a problem while append text to it.
below is my code:
<script type="text/javascript">
var sampleData = [{"x": 8,"y": 1}, {"x": 2,"y": 1}, {"x": 4,"y": 1},{"x": 5,"y": 1}];
// {"x": 6,"y": 40}, {"x": 8,"y": 100}, {"x": 10,"y": 60}];
$(function() {
InitChart();
});
function InitChart() {
// Chart creation code goes here
var vis = d3.select("#svgVisualize");
var xRange = d3.scale.linear().range([40, 400]).domain([0,10]);
var yRange = d3.scale.linear().range([200, 40]).domain([0,2]);
/* var xRange = d3.scale.linear().range([40, 400]).domain([d3.min(sampleData, function(d) {
return (d.x);
}), d3.max(sampleData, function(d) {
return d.x;
})]);
var yRange = d3.scale.linear().range([400, 40]).domain([d3.min(sampleData, function(d) {
return d.y;
}), d3.max(sampleData, function(d) {
return d.y;
})]); */
var xAxis = d3.svg.axis().scale(xRange).ticks(2);
var yAxis = d3.svg.axis().scale(yRange).ticks(2).orient("left");
vis.append("svg:g").call(xAxis).attr("transform", "translate(0,200)");
vis.append("svg:g").call(yAxis).attr("transform", "translate(40,0)");
var circles = vis.selectAll("circle").data(sampleData);
circles
.enter()
.insert("circle")
.attr("cx", function(d) { return xRange (d.x); })
//.attr("cy", function(d) { return yRange (d.y); })
.attr("cy", function(d) { return yRange (d.y); })
.attr("r", function(d) { return Math.log(d.x) * 30; })
.attr("stroke","black")
.style("fill", "yellow");
var text = vis.selectAll("text")
.data(sampleData)
.enter()
.insert("text");
//Add SVG Text Element Attributes
var textLabels = text
.attr("x", function(d) { return d.cx; })
.attr("y", function(d) { return d.cy; })
.text( function (d) { return "( " + d.cx + ", " + d.cy +" )"; })
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("fill", "red");
}
</script>
<body>
<svg id="svgVisualize" width="500" height="250" style="border:1px solid Red;"></svg>
</body>
Can anyone suggest what is the problem with above code?
Thanks
When you do this:
var text = vis.selectAll("text")
.data(sampleData)
.enter()
.insert("text");
You are selecting <text> elements that already exist in that SVG (in your case, the axes' ticks). Because of that, your "enter" selection is empty.
Solution: select something that doesn't exist, like null:
var text = vis.selectAll(null)
.data(sampleData)
.enter()
.insert("text");
Here is the updated code:
var sampleData = [{
"x": 8,
"y": 1
}, {
"x": 2,
"y": 1
}, {
"x": 4,
"y": 1
}, {
"x": 5,
"y": 1
}];
var vis = d3.select("svg");
var xRange = d3.scale.linear().range([40, 400]).domain([0, 10]);
var yRange = d3.scale.linear().range([200, 40]).domain([0, 2]);
var xAxis = d3.svg.axis().scale(xRange).ticks(2);
var yAxis = d3.svg.axis().scale(yRange).ticks(2).orient("left");
vis.append("svg:g").call(xAxis).attr("transform", "translate(0,200)");
vis.append("svg:g").call(yAxis).attr("transform", "translate(40,0)");
var circles = vis.selectAll("circle").data(sampleData);
circles
.enter()
.append("circle")
.attr("cx", function(d) {
return xRange(d.x);
})
//.attr("cy", function(d) { return yRange (d.y); })
.attr("cy", function(d) {
return yRange(d.y);
})
.attr("r", function(d) {
return Math.log(d.x) * 30;
})
.attr("stroke", "black")
.style("fill", "yellow");
var text = vis.selectAll(null)
.data(sampleData)
.enter()
.append("text");
var textLabels = text
.attr("x", function(d) {
return xRange(d.x);
})
.attr("text-anchor", "middle")
.attr("y", function(d) {
return yRange(d.y);
})
.text(function(d) {
return "( " + d.x + ", " + d.y + " )";
})
.attr("font-family", "sans-serif")
.attr("font-size", "10px")
.attr("fill", "red");
line, path{
fill: none;
stroke: black;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<svg width="400" height="220"></svg>
PS: Don't use insert, use append instead.
A better way of doing this is to use svg groups, then you can position both the circles and the text together:
var join = vis.selectAll(".points").data(sampleData);
var groups = join
.enter()
.append("g")
.attr("transform", function(d) { return "translate(" + [d.x, d.y] + ")"; });
.attr("cx", function(d) { return xRange (d.x); })
groups.append("circle")
.attr("r", function(d) { return Math.log(d.x) * 30; })
.attr("stroke","black")
.style("fill", "yellow");
groups.append("text")
.data(sampleData)
.text( function (d) { return "( " + d.cx + ", " + d.cy +" )"; })
.attr("font-family", "sans-serif")
.attr("font-size", "20px")
.attr("fill", "red");

3d.js - Updating nodes on a force directed graph

I am currently developing a program to create a graph(vertexes + edges) and apply an algorithm. I chose 3d.js to make this program.
I am confronted a problem about the repaintfunction when I add a vertex.
When I add the first vertex, it's work but for the following, my vertexes multiply rapidly(vertex&label)...
Screenshots:
Source code:
var graph = (function() {
return {
modules: {},
nodes: [],
links: []
}
})();
graph.modules.engin = (function() {
var w, h, circleWidth, svgApp, force, link, node, nodes;
var palette = {
"white": "#FFFFFF",
"gray": "#708284",
"orange": "#BD3613",
"red": "#D11C24",
"blue": "#2176C7"
};
return {
init: function() {
var w = 960,
h = 450;
circleWidth = 7;
svgApp = d3.select("body")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("id", "svg")
.attr("pointer-events", "all")
.attr("viewBox", "0 0 " + w + " " + h)
.attr("perserveAspectRatio", "xMinYMid")
.append('svg:g');
force = d3.layout.force()
.nodes(graph.nodes)
.links([])
.gravity(0.1)
.charge(-1000)
.size([w, h]);
//console.log(nodes);
//nodes.push({})
graph.modules.engin.repaint();
graph.modules.engin.insertNode('V1');
graph.modules.engin.insertNode('V2');
graph.modules.engin.insertNode('V3');
},
repaint: function() {
console.log('update');
nodes = force.nodes();
var links = force.links();
link = svgApp.selectAll(".link")
.data(links);
link.enter().append("line")
.attr("class", "link")
.attr("stroke", palette.gray)
.attr("fill", "none");
link.exit().remove();
node = svgApp.selectAll("circle.node")
.data(nodes);
node.enter().append("g")
.attr("class", "node")
.on("mouseover", function(d, i) { //MOUSEOVER
if (!d.root) {
d3.select(this).selectAll("circle")
.transition()
.duration(250)
.style("cursor", "none")
.attr("r", circleWidth + 3)
.attr("fill", palette.orange);
d3.select(this).select("text")
.transition()
.style("cursor", "none")
.duration(250)
.style("cursor", "none")
.attr("font-size", "1.5em")
.attr("x", 15)
.attr("y", 5)
} else {
d3.select(this).selectAll("circle")
.style("cursor", "none");
d3.select(this).select("text")
.style("cursor", "none");
}
})
.on("mouseout", function(d, i) { //MOUSEOUT
if (!d.root) {
//CIRCLE
d3.select(this).selectAll("circle")
.transition()
.duration(250)
.attr("r", circleWidth)
.attr("fill", palette.blue);
//TEXT
d3.select(this).select("text")
.transition()
.duration(250)
.attr("font-size", "1em")
.attr("x", 8)
.attr("y", 4)
}
})
.call(force.drag);
node.append("svg:circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", circleWidth)
.attr("fill", function(d, i) {
if (!d.root) {
return palette.blue;
} else {
return palette.red
}
});
node.append("text")
.text(function(d, i) {
return d.name;
})
.attr("x", function(d, i) {
return circleWidth + 5;
})
.attr("y", function(d, i) {
if (!d.root) {
return circleWidth
} else {
return circleWidth + 5
}
})
.attr("font-family", "Bree Serif")
.attr("fill", function(d, i) {
return palette.white;
})
.attr("font-size", function(d, i) {
return "1em";
})
.attr("text-anchor", function(d, i) {
if (!d.root) {
return "beginning";
} else {
return "end"
}
});
node.exit().remove();
force.on("tick", function(e) {
node.attr("transform", function(d, i) {
return "translate(" + d.x + "," + d.y + ")";
});
link.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;
})
});
force.start();
},
insertNode: function(name) {
nodes.push({
name: name
})
graph.modules.engin.repaint();
}
}
})();
$(document).ready(function() {
graph.modules.engin.init();
});
html {
background-color: #042029;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<button onclick="addNodes()">Restart Animation</button>
Thank you,
YM
I solved my problem: variable ref mistake and selector elements:
Source code:
var graph = (function() {
return {
modules: {},
nodes: [],
links: []
}
})();
graph.modules.engin = (function() {
var w, h, circleWidth, svgApp, force;
var palette = {
"white": "#FFFFFF",
"gray": "#708284",
"orange": "#BD3613",
"red": "#D11C24",
"blue": "#2176C7"
};
return {
init: function() {
h = $('body').height();
w = $('body').width();
circleWidth = 7;
svgApp = d3.select("body")
.append("svg:svg")
.attr("pointer-events", "all")
.attr("width", w)
.attr("height", h)
.attr("viewBox", "0 0 " + w + " " + h)
.attr("perserveAspectRatio", "xMinYMid");
force = d3.layout.force();
graph.nodes = force.nodes();
graph.links = force.links();
graph.modules.engin.repaint();
graph.modules.engin.insertNode('V1');
graph.modules.engin.insertNode('V2');
graph.modules.engin.insertNode('V3');
},
repaint: function() {
console.log('update');
var nodes = force.nodes();
var links = force.links();
console.log('BEFORE REPAINT');
console.log(nodes);
console.log(links);
console.log(graph.nodes);
console.log(graph.links);
var link = svgApp.selectAll("line.link")
.data(links);
var linkEnter = link.enter().append("line")
.attr("class", "link")
.attr("stroke", palette.gray)
.attr("fill", "none");
link.exit().remove();
var node = svgApp.selectAll("g.node")
.data(nodes, function(d) {
return d.name;
});
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.on("mouseover", function(d, i) {
if (!d.root) {
d3.select(this).select("circle")
.transition()
.duration(250)
.style("cursor", "none")
.attr("r", circleWidth + 3)
.attr("fill", palette.yellow);
d3.select(this) //.select("text")
.transition()
.style("cursor", "none")
.duration(250)
.style("cursor", "none")
.attr("font-size", "1.5em")
.attr("x", 15)
.attr("y", 5)
} else {
d3.select(this).select("circle")
.style("cursor", "none");
d3.select(this).select("text")
.style("cursor", "none");
}
})
.on("mouseout", function(d, i) {
if (!d.root) {
d3.select(this).select("circle")
.transition()
.duration(250)
.attr("r", circleWidth)
.attr("fill", palette.blue);
d3.select(this) //.select("text")
.transition()
.duration(250)
.attr("font-size", "1em")
.attr("x", 8)
.attr("y", 4)
}
})
.call(force.drag);
nodeEnter.append("svg:circle")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.attr("r", circleWidth)
.attr("fill", function(d, i) {
if (!d.root) {
return palette.blue;
} else {
return palette.red
}
}).attr("id", function(d) {
return "Node-" + d.name;
});
nodeEnter.append("svg:text")
.text(function(d, i) {
return d.name;
})
.attr("x", function(d, i) {
return circleWidth + 5;
})
.attr("y", function(d, i) {
if (!d.root) {
return circleWidth - 2
} else {
return circleWidth + 5
}
})
.attr("font-family", "Bree Serif")
.attr("fill", function(d, i) {
return palette.white;
})
.attr("font-size", function(d, i) {
return "1em";
})
.attr("text-anchor", function(d, i) {
if (!d.root) {
return "beginning";
} else {
return "end"
}
});
node.exit().remove();
force.on("tick", function(e) {
node.attr("transform", function(d, i) {
return "translate(" + d.x + "," + d.y + ")";
});
link.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;
})
});
force.gravity(0.1)
.charge(-1000)
.size([w, h])
.start();
},
insertNode: function(name) {
graph.nodes.push({
name: name
})
graph.modules.engin.repaint();
}
}
})();
$(document).ready(function() {
graph.modules.engin.init();
});
html {
background-color: #042029;
}
body {
height: 600px;
width: 100%;
}
<script src="https://d3js.org/d3.v3.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js"></script>
<button onclick="addNodes()">Restart Animation</button>

Static Force Directed Graph links not correct length [duplicate]

This question already has answers here:
d3.js linkStrength influence on linkDistance in a force graph
(2 answers)
Closed 2 years ago.
I am trying to create a static force directed graph. One that loads without any animation in. Here's what I'm trying to emulate: http://bl.ocks.org/mbostock/1667139
I have the following D3 graph:
var width = $("#theVizness").width(),
height = $("#theVizness").height();
var color = d3.scale.ordinal().range(["#ff0000", "#fff000", "#ff4900"]);
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("#theVizness").append("svg")
.attr("width", width)
.attr("height", height);
var loading = svg.append("text")
.attr("class", "loading")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text("Loading...");
d3.json("https://dl.dropboxusercontent.com/u/5772230/ForceDirectData.json", function (error, json) {
var nodes = json.nodes;
force.nodes(nodes)
.links(json.links)
.linkDistance(function (d) {
return d.value * 1.5;
})
.friction(0.4);
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
var files = svg.selectAll(".file")
.data(json.nodes)
.enter().append("circle")
.attr("class", "file")
.attr("r", 10)
.attr("fill", function (d) {
return color(d.colorGroup);
});
var totalNodes = files[0].length;
files.append("title")
.text(function (d) { return d.name; });
force.start();
for (var i = totalNodes * totalNodes; i > 0; --i) force.tick();
force.stop();
nodes[0].x = width / 2;
nodes[0].y = height / 2;
link.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; });
files.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("class", function(d){
var classString = "file"
if (d.index === 0) classString += " rootFile";
return classString;
})
.attr("r", function(d){
var radius = 10;
if (d.index === 0) radius = radius * 2;
return radius;
});
loading.remove();
});
Here's my data: https://dl.dropboxusercontent.com/u/5772230/ForceDirectData.json
{
"nodes":[
{"name":"File1.exe","colorGroup":0},
{"name":"File2.exe","colorGroup":0},
{"name":"File3.exe","colorGroup":0},
{"name":"File4.exe","colorGroup":0},
{"name":"File5.exe","colorGroup":0},
{"name":"File6.exe","colorGroup":0},
{"name":"File7.exe","colorGroup":0},
{"name":"File8.exe","colorGroup":0},
{"name":"File8.exe","colorGroup":0},
{"name":"File9.exe","colorGroup":0}
],
"links":[
{"source":1,"target":0,"value":10},
{"source":2,"target":0,"value":35},
{"source":3,"target":0,"value":50},
{"source":4,"target":0,"value":50},
{"source":5,"target":0,"value":65},
{"source":6,"target":0,"value":65},
{"source":7,"target":0,"value":81},
{"source":8,"target":0,"value":98},
{"source":9,"target":0,"value":100}
]
}
Fiddle
From my understanding of the bl.ocks page, this graph is running the tick method a certain amount of times. But my issue is the lengths of my links between the nodes are not proportionate to what I have in my JSON file.
I've opted for the static graph because I did not want to have the graph animate in, like in the standard graph.
Why are my links to the nodes nor correctly proportioned to match my JSON file?
I do not understand your question.
Is this what you mean?
var width = $("#theVizness").width(),
height = $("#theVizness").height();
var color = d3.scale.ordinal().range(["#ff0000", "#fff000", "#ff4900"]);
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([width, height]);
var svg = d3.select("#theVizness").append("svg")
.attr("width", width)
.attr("height", height);
var loading = svg.append("text")
.attr("class", "loading")
.attr("x", width / 2)
.attr("y", height / 2)
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text("Loading...");
d3.json("https://dl.dropboxusercontent.com/u/5772230/ForceDirectData.json", function (error, json) {
var nodes = json.nodes;
force.nodes(nodes)
.links(json.links)
.linkDistance(function (d) {
return d.value * 1.5;
})
.friction(0.4);
var link = svg.selectAll(".link")
.data(json.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", 1);
var files = svg.selectAll(".file")
.data(json.nodes)
.enter().append("circle")
.attr("class", "file")
.attr("r", 10)
.attr("fill", function (d) {
return color(d.colorGroup);
});
var totalNodes = files[0].length;
files.append("title")
.text(function (d) { return d.name; });
force.start();
for (var i = totalNodes * totalNodes; i > 0; --i) force.tick();
nodes[0].x = width / 2;
nodes[0].y = height / 2;
link.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; });
files.attr("cx", function (d) { return d.x; })
.attr("cy", function (d) { return d.y; })
.attr("class", function(d){
var classString = "file"
if (d.index === 0) classString += " rootFile";
return classString;
})
.attr("r", function(d){
var radius = 10;
if (d.index === 0) radius = radius * 2;
return radius;
});
force.on("tick", function() {
link.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; });
files.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
loading.remove();
});
JSFiddle

D3.js not aligning column values along y axis accordingly

Brand new to D3.js and damn what a learning curve....
Below is the graph as it stand when my page is loaded with the original static data. As you can see from the picture, the columns rise sequentially from left to right as they should.
I have the D3 code placed inside a Backbone view. The goal of this program is to change the values on a set interval. When the values change I want to use the transform aspect of D3 to animate the column moving either up or down depending on the number.
However, I'd like them to all move up and down based on a set y axis that applies to each column. It seems, instead, that each column moves the numbers up and down based upon a different y axis, and this changes in each column with each iteration. For example, a column with a value of 115311 could wind up moving down if a value of 188000 is generated, then up again for a value of 112000.
For example, here are the columns after the second iteration of numbers are created (also note, the rect elements that serve as the columns are not being re-drawn. If anyone has any idea why that is happening as well it would be greatly appreciated)
Model
var DataPoint = Backbone.Model.extend({
initialize: function (lbl, ctrct, rtrn) {
this.set({
label: lbl,
contract: ctrct,
annReturn: rtrn
})
},
randomize: function () {
this.set({
contract: (Math.random() * (140000 - 100000 + 1) + 100000).toFixed(2)
});
}
});
Collection
var DataSeries = Backbone.Collection.extend({
model: DataPoint,
fetch: function () {
this.reset();
this.add([
new DataPoint("1/7yrs", "111830.17", "1.63%"),
new DataPoint("2/7yrs", "115311.17", "2.07%"),
new DataPoint("3/7yrs", "118984.65", "2.52%"),
new DataPoint("4/7yrs", "122859.65", "2.98%"),
new DataPoint("5/7yrs", "126947.77", "3.46%"),
new DataPoint("6/7yrs", "131260.74", "3.94%"),
new DataPoint("7/7yrs", "135810.92", "4.44%")
])
},
randomize: function () {
this.each(function (m) {
m.randomize();
});
}
});
View
var BarGraph = Backbone.View.extend({
"el": "#graph",
options: {barDemo: ""},
initialize: function (options) {
_.bindAll(this, "render", "frame");
this.collection.bind("change", this.render);
this.options.barDemo = d3.selectAll($(this.el)).append("svg:svg")
.attr("width", width)
.attr("height", height + 60);
this.collection.fetch();
},
render: function () {
var data = this.collection.models;
var x = d3.scale.linear().domain([0, data.length]).range([0, width]);
var y = d3.scale.linear().domain([0, d3.max(data, function (d) {
console.log(d);
console.log();
return d.attributes.contract
})])
.rangeRound([0, height]);
this.options.barDemo.selectAll("rect")
.data(data)
.enter()
.append("svg:rect")
.attr("x", function (d, i) {
//console.log(x(i));
return x(i);
})
.attr("y", function (d) {
//console.log(height - y(d.attributes.contract));
return height - y(d.attributes.contract);
})
.attr("height", function (d) {
console.log(y(d.attributes.contract));
return y(d.attributes.contract);
})
.attr("width", barWidth)
.attr("fill", "#2d578b");
this.options.barDemo.selectAll("text")
.data(data)
.enter()
.append("svg:text")
.attr("x", function (d, i) {
//console.log(x(i) + barWidth);
return x(i) + barWidth;
})
.attr("y", function (d) {
return height - y(d.attributes.contract);
})
.attr("dx", -barWidth / 2)
.attr("dy", "1.2em")
.attr("text-anchor", "middle")
.text(function(d) {
//console.log(d.contract);
return d.attributes.contract
})
.attr("fill", "white");
this.options.barDemo.selectAll("text.yAxis")
.data(data)
.enter().append("svg:text")
.attr("x", function (d, i) { return x(i) + barWidth; })
.attr("y", height)
.attr("dx", -barWidth / 2)
.attr("dy", "15px")
.attr("text-anchor", "middle")
.attr("style", "font-size: 12; font-family; Helvetica, sans-serif")
.text(function (d) { return d.label; })
.attr("transform", "translate(0, 18)")
.attr("class", "yAxis");
var rect = this.options.barDemo.selectAll("rect").data(data, function (d, i) { return i; });
rect.enter().insert("rect", "text").attr("x", function (d, i) {
return x(i);
}).attr("height", function (d) {
return x(d.attributes.contract);
}).attr("width", barWidth);
rect.transition().duration(1000).attr("height", function (d) {
return x(d.attributes.contract);
}).attr("width", barWidth);
rect.exit().remove();
var text = this.options.barDemo.selectAll("text").data(data, function (d, i) {
return i;
});
text.enter().append("text")
.attr("x", function (d, i) { return x(i) + barWidth; })
.attr("y", function (d) { return height - y(d.attributes.contract); })
.attr("dx", -barWidth / 2)
.attr("dy", "1.2em")
.attr("text-anchor", "middle")
.text(function (d) { return d.attributes.contract })
.attr("fill", "white");
text
.transition()
.duration(1100)
.attr("y", function (d) {
return y(d.attributes.contract);
})
.text(function (d) { return d.attributes.contract; });
},
frame: function () {
//this.chart.append("line").attr("y1", 0).attr("y2", h - 10).style("stroke", "#000");
//this.chart.append("line").attr("x1", 0).attr("x2", w).attr("y1", h - 10).attr("y2", h - 10).style("stroke", "#000");
}
});
Startup code
$(function() {
var dataSeries = new DataSeries();
new BarGraph({
collection: dataSeries
}).render();
setInterval(function () {
dataSeries.randomize();
}, 2000);
});
Below is the code that finally fixed my render function. I needed to adjust the y and height attributes in when the rect was changed and selected, mainly, among a couple other minor changes.
render: function () {
var data = this.collection.models;
var x = d3.scale.linear().domain([0, data.length]).range([0, width]);
var y = d3.scale.linear().domain([100000, 140000]).range([height, 0]);
this.options.barDemo.selectAll("rect")
.data(data)
.enter()
.append("svg:rect")
.attr("x", function (d, i) { return x(i); })
.attr("y", function (d) { return y(d.attributes.contract); })
.attr("height", function (d) { return height - y(d.attributes.contract); })
.attr("width", barWidth)
.attr("fill", "#2d578b");
this.options.barDemo.selectAll("text")
.data(data)
.enter()
.append("svg:text")
.attr("x", function (d, i) { return x(i) + barWidth; })
.attr("y", function (d) { return y(d.attributes.contract); })
.attr("dx", -barWidth / 2)
.attr("dy", "1.2em")
.attr("text-anchor", "middle")
.text(function(d) { return d.attributes.contract; })
.attr("fill", "white");
this.options.barDemo.selectAll("text.yAxis")
.data(data)
.enter().append("svg:text")
.attr("x", function (d, i) { return x(i) + barWidth; })
.attr("y", height)
.attr("dx", -barWidth / 2)
.attr("dy", "15px")
.attr("text-anchor", "middle")
.attr("style", "font-size: 12; font-family; Helvetica, sans-serif")
.text(function (d) { return d.attributes.label; })
.attr("transform", "translate(0, 18)")
.attr("class", "yAxis");
var rect = this.options.barDemo.selectAll("rect").data(data, function (d, i) { return i; });
rect.enter().append("rect", "text").attr("x", function (d, i) {
return x(i);
}).attr("y", function (d) {
return y(d.attributes.contract);
}).attr("height", function (d) {
return height - y(d.attributes.contract);
}).attr("width", barWidth);
rect.transition().duration(1000).attr("y", function(d) {
return y(d.attributes.contract);
}).attr("height", function (d) {
return height - y(d.attributes.contract);
}).attr("width", barWidth);
rect.exit().remove();
var text = this.options.barDemo.selectAll("text").data(data, function (d, i) {
return i;
});
text
.transition()
.duration(1100)
.attr("y", function (d) {
return y(d.attributes.contract);
})
.text(function (d) {
console.log("from transition");
console.log(d);
return d.attributes.contract;
});
},

d3 pack layout circles overlapping after movement/size transitions

I have circles arranged with the pack layout, from a dataset which periodically updates the radii.
The code I started out with is this standard example for a bubble chart: http://bl.ocks.org/mbostock/4063269
Whenever the circle sizes change, they transition. Often when circles grow, they move to overlap other circles. I don't want them to overlap each other.
I'm still pretty new to d3, have moved the code around a lot and tried everything I can think of, but no luck.
The function makeBubbles is passed raw Json (see below).
function makeBubbles(root){
var diameter = $(window).width(),
diameterh = $(window).height(),
format = d3.format(",d"),
color = d3.scale.category20();
var bubble = d3.layout.pack()
.sort(null)
.size([diameter, diameterh])
.value(function(d){return d.value; })
.padding(1.5);
var svg = d3.select("svg")
.attr("width", diameter)
.attr("height", diameterh)
.attr("class", "bubble");
var node = svg.selectAll(".node")
.data(bubble.nodes(classes(root)).filter(function(d) { return !d.children; }), function(d){ console.log(d); return d.className; });
node.append("title")
.text(function(d) { return d.className + ": " + format(d.value); });
node.append("circle")
.style("fill", function(d) { return color(d.packageName); })
.on("click", function(d) { window.location = d.url; })
.attr("r", 0)
.transition()
.duration(1000)
.attr("r", function(d) { return d.r; });
node.transition().duration(1000).attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
node.exit().transition().duration(200).attr("transform", "scale(0.001)").remove();
node.append("text")
.attr("dy", ".3em")
.style("text-anchor", "middle")
.text(function(d) { return d.className.substring(0, d.r / 6); })
.attr("opacity",0)
.transition().duration(1000)
.attr("opacity",1);
// Returns a flattened hierarchy containing all leaf nodes under the root.
function classes(root) {
var classes = [];
function recurse(name, node) {
if (node.children) node.children.forEach(function(child) { recurse(node.name, child); });
else classes.push({packageName: name, className: node.name, value: node.size, url: node.url});
}
recurse(null, root);
return {children: classes};
}
d3.select(self.frameElement).style("height", diameterh + "px");
}
Data passed looks something like this (varying as the dataset is updated):
{"name":"bubbles","children":[{"name":"tourism","children":[{"name":"tourism","children":[{"name":"practical","children":[{"name":"ACCOMM","size":13,"url":"#"},{"name":"HIRE","size":2,"url":"#"}]},{"name":"activity","children":[{"name":"EVENT","size":6,"url":"#"},{"name":"TOUR","size":3,"url":"#"}]},{"name":"leisure","children":[{"name":"RESTAURANT","size":168,"url":"#"},{"name":"ATTRACTION","size":8,"url":"#"}]}]}]}]}
I had a similar problem.
I slightly modified (mostly simplified) your code, and here you can find working example.
My approach is not to use transformations. Without them, the code looks more readable and maintainable. So, I propose a simple solution, I hope you can use it in your case.
Label. transition is maybe not the best, but you can change it.
On jsfiddle, its impossible to integrate json files, so the data is inside javascript. In your code, you would need to handle loading json, but the core idea from my example can be applied without change.
The key function is:
function updateVis() {
if (dataSource == 0)
pack.value(function(d) { return d.size; });
if (dataSource == 1)
pack.value(function(d) { return 100; });
if (dataSource == 2)
pack.value(function(d) { return 1 +
Math.floor(Math.random()*501); });
var data1 = pack.nodes(data);
titles.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.text(function(d) {
return (d.children ? "" : d.name + ": " + format(d.value));
});
circles.transition()
.duration(5000)
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", function(d) { return d.r; });
labels.transition()
.duration(5000)
.attr("opacity", 0)
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.each("end", function(d){
d3.select(this).text(function(d) {
return d.children ? "" : d.name.substring(0, d.r / 4);
});
d3.select(this).transition()
.duration(1000)
.attr("opacity", 1);
});
};

Categories