I've been trying to do a widget with some information from Google Analytics. I am getting the real time visitors and then displaying them in bubbles with flags with the help of d3.js. I do manage to get them on the screen and the force simulation is working properly. However I can't seem to grasp how could I cluster the bubbles according to their respective country, as I don't know which countries will be displayed in advance, and I'm not good at web programming so as do some on the fly links and use the forceLink. Maybe there is a better way that I'm missing? I would greatly appreciate any pointers that you could give me.
(function() {
var width = 500;
height = 500;
var svg = d3.select("#chart")
.append("svg")
.attr("height", height)
.attr("width", width)
.append("g")
.attr("transform", "translate(0,0)")
// SO question preparation :
var data_csv = "Country,Count\nUkraine,1\nDenmark,1\nDenmark,1";
data = d3.csvParse(data_csv);
d3.queue().await(ready, data);
var forceX = d3.forceX(function(d) {
return width / 2
}).strength(0.05)
var forceY = d3.forceY(function(d) {
return height / 2
}).strength(0.05)
var simulation = d3.forceSimulation()
.force("xTowardsTheCenter", forceX)
.force("yTowardsTheCenter", forceY)
.force("antiColliding", d3.forceCollide(function(d) {
return radiusScale(d.Count) + 1;
}))
.force("charge", d3.forceManyBody().strength(-15))
// this function sets up a variation scale so that the circles can have different sizes without
// occupying all of the screen
var radiusScale = d3.scaleSqrt().domain([1, 40]).range([30, 130]);
function ready(error, datapoints) {
var circles = svg.selectAll(".Country")
.data(datapoints)
.enter().append("circle")
.attr("id", function(d) {
return d.Country;
})
.attr("class", "country")
.attr("r", function(d) {
return radiusScale(d.Count);
})
.attr("text", function(d) {
return d.Count;
})
.style("stroke", "black")
.style("fill", "blue")
simulation.nodes(datapoints)
.on('tick', ticked)
function ticked() {
circles
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
}
})();
<!DOCTYPE html>
<html>
<title>Neets visitors </title>
<b>Neets visitors</b>
<body>
<div id="chart"></div>
<script src="https://d3js.org/d3.v4.js"></script>
<script src="bubble.js"></script>
</body>
</html>
Expected result
Related
I am trying to update a force-directed graph written using d3js version 3 to d3js version 7.
The following code snippet is the working implementation using d3js v3:
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
graph = {
nodes: [],
links: [],
}
var simulation = d3.layout.force()
.size([width, height])
.nodes(graph.nodes)
.links(graph.links)
.on("tick", function() {
svg.selectAll('.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 })
svg.selectAll('.node')
.attr("cx", function (d) { return d.x })
.attr("cy", function (d) { return d.y })
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
});
function update() {
// update links
var link = svg.selectAll('.link').data(graph.links);
link.enter()
.insert('line', '.node')
.attr('class', 'link')
.style('stroke', '#d9d9d9');
link
.exit()
.remove()
// update nodes
var node = svg.selectAll('.node').data(graph.nodes);
var g = node.enter()
.append('g')
.attr('class', 'node');
g.append('circle')
.attr("r", 20)
.style("fill", "#d9d9d9");
g.append('text')
.attr("class", "text")
.text(function (d) { return d.name });
node
.exit()
.remove();
// update simulation
simulation
.linkDistance(100)
.charge(-200)
.start();
};
function addNode(node) {
graph.nodes.push(node);
update();
};
function connectNodes(source, target) {
graph.links.push({
source: source,
target: target,
});
update();
};
addNode({
id: "you",
name: "you",
});
let index = 1;
// add a new node every three seconds and connect to 'you'
const interval = window.setInterval(() => {
let id = Math.random().toString(36).replace('0.','');
id = id.slice(0,4);
addNode({
id: id,
name: id
});
connectNodes(0, index);
index++;
}, 3000);
// no more than 8 nodes
setTimeout(() => {
clearInterval(interval)
}, 3000 * 8);
<html>
<head>
<script src="https://d3js.org/d3.v3.min.js"></script>
</head>
<body>
<svg width="400" height="200"></svg>
</body>
</html>
The following code snippet my attempt of implementing the above code snippet using d3js v7:
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
graph = {
nodes: [],
links: [],
}
var simulation = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2).strength(0.01))
.nodes(graph.nodes)
.force("link", d3.forceLink(graph.links).distance(100))
.on("tick", function() {
svg.selectAll('.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 })
svg.selectAll('.node')
.attr("cx", function (d) { return d.x })
.attr("cy", function (d) { return d.y })
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
});
function update() {
// update links
var link = svg.selectAll('.link').data(graph.links);
link.enter()
.insert('line', '.node')
.attr('class', 'link')
.style('stroke', '#d9d9d9');
link
.exit()
.remove()
// update nodes
var node = svg.selectAll('.node').data(graph.nodes);
var g = node.enter()
.append('g')
.attr('class', 'node');
g.append('circle')
.attr("r", 20)
.style("fill", "#d9d9d9");
g.append('text')
.attr("class", "text")
.text(function (d) { return d.name });
node
.exit()
.remove();
// update simulation
simulation
.nodes(graph.nodes)
.force("link", d3.forceLink(graph.links).distance(100))
.force("charge", d3.forceManyBody().strength(-200))
.restart()
};
function addNode(node) {
graph.nodes.push(node);
update();
};
function connectNodes(source, target) {
graph.links.push({
source: source,
target: target,
});
update();
};
addNode({
id: "you",
name: "you",
});
let index = 1;
// add a new node every three seconds and connect to 'you'
const interval = window.setInterval(() => {
let id = Math.random().toString(36).replace('0.','');
id = id.slice(0,4);
addNode({
id: id,
name: id
});
connectNodes(0, index);
index++;
}, 3000);
// no more than 8 nodes
setTimeout(() => {
clearInterval(interval)
}, 3000 * 8);
<html>
<head>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<svg width="400" height="200"></svg>
</body>
</html>
The d3js v7 code snippet does not produce the same results as d3js v3 - why is this? The exact changes I have done are seen in this diff: https://www.diffchecker.com/wdq7AFbU.
Even without adding any connections, there is a difference between the two implementations. The v3 implementation makes the "you" node fly in from random directions, whilst with the v7 implementation the "you" node always flies in from the same direction.
There also seems to be some discrepancy on how the force is being applied since the new nodes in the v7 implementation get stuck in the top-left corner.
I've noticed the attributes of DOMs are reflecting the status alright. It's just that the simulation just stopped prematurely.
In short, the default value of d3.force.alphaDecay is too short for the intended result; alphaDecay dictates the end of simulation. Try expand the value a little bit. The latest default value for alphaDecay is 0.001, according to d3-force github readme. In my testing session, setting the value to 1/5(0.0002) seems to be enough for the same result.
try run the code below. it works fine.
Tips
When working with DOMs and SVGs, try add matching data-ooo tag to see if the d3.selection is working properly. I've added properties of node data such as .index and .target, .source to attributes like data-index,data-id,data-target,data-source... and noticed that everything is in place.
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
graph = {
nodes: [],
links: [],
}
var simulation = d3.forceSimulation()
.force("center", d3.forceCenter(width / 2, height / 2).strength(0.01))
.nodes(graph.nodes)
.force("link", d3.forceLink(graph.links).distance(100))
.on("tick", function() {
svg.selectAll('.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 })
svg.selectAll('.node')
.attr("cx", function (d) { return d.x })
.attr("cy", function (d) { return d.y })
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
}).alphaDecay(0.0002) // just added alpha decay to delay end of execution
function update() {
// update links
var link = svg.selectAll('.link').data(graph.links);
link.enter()
.insert('line', '.node')
.attr('class', 'link')
.style('stroke', '#d9d9d9');
link
.exit()
.remove()
// update nodes
var node = svg.selectAll('.node').data(graph.nodes);
var g = node.enter()
.append('g')
.attr('class', 'node');
g.append('circle')
.attr("r", 20)
.style("fill", "#d9d9d9");
g.append('text')
.attr("class", "text")
.text(function (d) { return d.name });
node
.exit()
.remove();
// update simulation
simulation
.nodes(graph.nodes)
.force("link", d3.forceLink(graph.links).distance(100))
.force("charge", d3.forceManyBody().strength(-200))
.restart()
};
function addNode(node) {
graph.nodes.push(node);
update();
};
function connectNodes(source, target) {
graph.links.push({
source: source,
target: target,
});
update();
};
addNode({
id: "you",
name: "you",
});
let index = 1;
// add a new node every three seconds and connect to 'you'
const interval = window.setInterval(() => {
let id = Math.random().toString(36).replace('0.','');
id = id.slice(0,4);
addNode({
id: id,
name: id
});
connectNodes(0, index);
index++;
}, 3000);
// no more than 8 nodes
setTimeout(() => {
clearInterval(interval)
}, 3000 * 8);
<html>
<head>
<script src="https://d3js.org/d3.v7.min.js"></script>
</head>
<body>
<svg width="400" height="200"></svg>
</body>
</html>
Edit: What is alpha and alphaDecay?
doing simulation.restart().alpha(0.3) seem to give the same effect which was mentioned in an answer to my earlier post. Is there any difference between the two?
d3-force github readme says alpha stands for entropy. In easy words, alpha stands for the life of simulation; alpha=1 stands for start and alpha=0 stands for the end.
https://github.com/d3/d3-force#simulation_alpha
alpha is roughly analogous to temperature in simulated annealing. It decreases over time as the simulation “cools down”. When alpha reaches alphaMin, the simulation stops
here's a simple pseudocode that illustrates the idea.
alpha = 1
alphaDecay = 0.002
function tick() {
alpha = alpha - alphaDecay
}
loop {
tick()
if alpha equals to 0 then end simulation
}
the previous answer mentioned in the comment increased alpha when restart because he wanted to give simulation more time after a reset.
in my answer, I've set alphaDecay to a lower number so that the simulation can work for a longer period of time.
increasing alphaDecay/decreasing alpha = simulation ends quicker
decreasing alphaDecay/increasing alpha = simulation ends later
Edit: Changes in d3-force since D3 v4?
Also, there is still some difference between the v3 and v7 implementation; 1) the collisions in v3 is more elastic and 2) the new nodes being added come in from random directions. Do you know what could be fixed to get 1) and 2) in the v7 implementation?
please read this d3-force v1 github changelog; d3-force became a separate package since d3 v4 and this changelog explains the changes.
1. d3-force has become more accurate.
The changelog mentions many improvements:
The force simulation now uses velocity Verlet integration rather than position Verlet, tracking the nodes’ positions (node.x, node.y) and velocities (node.vx, node.vy) rather than their previous positions (node.px, node.py).
The new link force replaces force.linkStrength and employs better default heuristics to improve stability.
The physics integration of d3-force has improved for better accuracy. This is why it looks different from v3 implementation.
Although it is possible to tune the simulation look like in a specific way but what does more elastic mean? Does it mean stronger reaction force? or does it mean faster animation(but in same amount of time)? it surely can be tuned in, only if the request was more detailed. And every d3 package has surprisingly simple structure and formulas. It is possible to look inside and change its inner function.
2. manipulating positions of nodes
https://github.com/d3/d3-force#simulation_nodes
manipulate .x and .y of nodes during the simulation to change their positions.
addNode({ id: /* ... */, x: 0, y: 100}) // like this
edit: there were some typos in my answer about the increase-decrease relation of alpha and time.
I refered to this video in YouTube to make a bubble graph. However, the author didn't use a nest function to group his data. After I pre-processed my data using nest() function, I don't know how to pass the value to a function called radiusScale() in my code. I was thinking maybe I should pass the value of
d3.entries(groupByAgeAndtime)[i]["value"]
to radiusScale().
Here is my code snippet for my problem.
var radiusScale = d3.scaleSqrt()
.domain([d3.min(Object.values(groupByAgeAndtime), function(d){
return d.mean_time_in_hospital;
}),d3.max(Object.values(groupByAgeAndtime), function(d){
return d.mean_time_in_hospital;
})])
.range([50,150]);
for (i = 0; i < 10; i++)
{
console.log(d3.entries(groupByAgeAndtime)[i]["value"]);
}
var simulation = d3.forceSimulation()
.force("x",d3.forceX(width/2).strength(0.05))
.force("y",d3.forceY(height/2).strength(0.05))
.force("collide", d3.forceCollide(function(d){
return radiusScale(d.mean_time_in_hospital) + 2;
}))
var circles = svg.selectAll(".artist")
.data(groupByAgeAndtime)
.enter()
.append("circle")
.attr("class","artist")// the "artist" will transform into class name in HTML
.attr("r", function(d){
return radiusScale(Object.values(groupByAgeAndtime))
})
.attr("fill","lightblue")
.on("click",function(d){
console.log(d)
})
This is the screenshot: for the thing I want to pass to the function radiusScale. I think after passing the correct value, the circle will appear immediately. If not, can anyone tell me what is the value I should pass to get a circle?
Here is my JSFiddle for my js, html and .csv file. I would really appreciate anyone who can tell me what value should I pass to the function.
The grouped data groupByAgeAndtime using d3.nest() has to be used on your simulation and circle drawing.
Note that your radiusScale now gets the correct value to be mapped to chosen range range([50, 150]);
var simulation = d3.forceSimulation()
.force("x", d3.forceX(width / 2).strength(0.05))
.force("y", d3.forceY(height / 2).strength(0.05))
.force("collide", d3.forceCollide(function(d) {
return radiusScale(d.mean_time_in_hospital);
}))
simulation.nodes(Object.values(groupByAgeAndtime))
.on('tick', ticked)
The same for the circles, and the circles radius now matches the simulation radius
var circles = svg.selectAll(".artist")
.data(Object.values(groupByAgeAndtime))
.enter()
.append("circle")
.attr("class", "artist")
.attr("r", function(d) {
return radiusScale(d.mean_time_in_hospital)
})
.attr("fill", "lightblue")
.on("click", function(d) {
console.log(d)
})
Here is the functional example, your text still needs to be implemented.
I've pasted your csv data here https://hastebin.com/raw/pasacimala
(function() {
var width = 800,
height = 350;
var svg = d3.select("#chart")
.append("svg")
.attr("height", height)
.attr("width", width)
.attr("viewBox", `0 0 ${width} ${height}`)
.attr("preserveAspectRatio","xMidYMid meet")
.append("g")
.attr("transform", "translate(0,0)");
// import csv file
d3.csv("https://cors-anywhere.herokuapp.com/https://hastebin.com/raw/pasacimala")
.then(function(d) {
//data preprocessing
d.forEach(e => {
e.age = e.age.replace("[", "").replace(")", "");
e.time_in_hospital = + e.time_in_hospital;
});
return d; //must return something
})
.then((data, err) => ready(err, data))
function ready(error, datapoints) {
var groupByAgeAndtime = d3.nest()
.key(function(d) {
return d.age;
})
//.key(function(d) { return d.time_in_hospital; })
.rollup(function(v) {
return {
mean_time_in_hospital: d3.mean(v, function(d) {
return d.time_in_hospital;
})
}
})
.object(datapoints); //specify the dataset used
/**************************************** SCALING PART **************************************************/
var radiusScale = d3.scaleSqrt()
.domain([d3.min(Object.values(groupByAgeAndtime), function(d) {
return d.mean_time_in_hospital;
}), d3.max(Object.values(groupByAgeAndtime), function(d) {
return d.mean_time_in_hospital;
})])
.range([50, 150]);
/* for (i = 0; i < 10; i++) {
//console.log(d3.entries(groupByAgeAndtime)[i]["key"]);
console.log(d3.entries(groupByAgeAndtime)[i]["value"]);
} */
console.log(Object.values(groupByAgeAndtime))
// STUCK HERE
var simulation = d3.forceSimulation()
.force("x", d3.forceX(width / 2).strength(0.05))
.force("y", d3.forceY(height / 2).strength(0.05))
.force("collide", d3.forceCollide(function(d) {
return radiusScale(d.mean_time_in_hospital);
}))
// END OF STUCK HERE
var circles = svg.selectAll(".artist")
.data(Object.values(groupByAgeAndtime))
.enter()
.append("circle")
.attr("class", "artist")
.attr("r", function(d) {
return radiusScale(d.mean_time_in_hospital)
})
.attr("fill", "lightblue")
.on("click", function(d) {
console.log(d)
})
// append = add something
// text
var texts = svg.selectAll('.text')
.data(Object.keys(groupByAgeAndtime))
.enter()
.append('text')
.text(e => e)
.attr("text-anchor", "middle")
.attr('color', 'black')
.attr('font-size', '13')
simulation.nodes(Object.values(groupByAgeAndtime))
.on('tick', ticked)
function ticked() {
texts
.attr("x", function(d) {
return d.x
})
.attr("y", function(d) {
return d.y
})
circles
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
}
}
})();
<script src="https://d3js.org/d3.v5.min.js"></script>
<div id="chart"></div>
I am very new to d3js v3 and I was trying out a new program where there are lines and the according to the data, circles get embedded into them.
This is what I have so far.
var width = 500,
height = 500;
var animals = ['dog', 'cat', 'bat'];
var fruits = ['apple', 'banana'];
var svg = d3.select("body")
.append("svg")
.attr("width", width)
.attr("height", height);
var line1 = svg.append("line")
.attr("x1", 350)
.attr("y1", 5)
.attr("x2", 350)
.attr("y2", 350)
.attr("stroke-width", 2)
.attr("stroke", "black");
var line2 = svg.append("line")
.attr("x1", 80)
.attr("y1", 5)
.attr("x2", 100)
.attr("y2", 350)
.attr("stroke-width", 2)
.attr("stroke", "black");
var animal_scale = d3.scale.ordinal()
.domain(animals)
.rangePoints([5, 350],.2);
var fruit_scale = d3.scale.ordinal()
.domain(fruits)
.rangePoints([5, 350],.2);
var animal_circles = svg.selectAll('circle')
.data(animals)
.enter()
.append('circle')
.attr('cx', function(d) {
// is there a way to calc it automatically according to line 1
})
.attr('cy', function(d) {
return animal_scale(d);
})
.attr('id', function(d) {
return d;
})
.attr('r', 20);
var fruits_circles = svg.selectAll('circle')
.data(fruits)
.enter()
.append('circle')
.attr('cx', function(d) {
// is there a way to calc it automatically according to line 2
})
.attr('cy', function(d) {
return fruit_scale(d);
})
.attr('id', function(d) {
return d;
})
.attr('r', 20);
<!DOCTYPE html>
<html>
<meta charset=utf-8>
<head>
<title></title>
</head>
<body>
<script src="https://d3js.org/d3.v3.min.js"></script>
</body>
</html>
I looked at some sources and being new, its kinda hard to understand most of it. I eventually want to be able to move and drag the circles between lines at the end of the project.There are some issues with the current code, as it does not display the second set of circles too.
Could someone please help me understand further how to do this. It would be a great way for me to learn.
You can select objects by class name and set data. Here is my fast solution for drag-n-drop: jsFiddle. You can modify drag function to add limits to cx position
Hello I currently have a stacked bar chart in d3,js that currently won't transition.
The chart is able to update but unfortunately no transition :(
I am under the feeling that there is a 1 line fix to this.
Please help!!!
Took this from
http://bl.ocks.org/anotherjavadude/2940908
<!DOCTYPE html>
<html>
<head>
<title>Simple Stack</title>
<script src="http://d3js.org/d3.v3.js"></script>
<style>
svg {
border: solid 1px #ccc;
font: 10px sans-serif;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div id="viz"></div>
<script type="text/javascript">
var w = 960,
h = 500
// create canvas
var svg = d3.select("#viz").append("svg:svg")
.attr("class", "chart")
.attr("width", w)
.attr("height", h )
.append("svg:g")
.attr("transform", "translate(10,470)");
x = d3.scale.ordinal().rangeRoundBands([0, w-800])
y = d3.scale.linear().range([0, h-100])
z = d3.scale.ordinal().range(["blue", "lightblue"])
// console.log("RAW MATRIX---------------------------");
// 3 columns: ID,c1,c2
var matrix = [
[ 1, 5871, 8916]
];
// console.log(matrix)
var matrix2 = [
[ 1, 21, 800]
];
function rand_it(x){
return Math.floor((Math.random() * x) + 1);
}
function render(matrix){
var t = d3.transition()
.duration(300);
// remove
svg.selectAll("g.valgroup")
.remove();
svg.selectAll("rect")
.transition(t)
.remove();
var remapped =["c1","c2"].map(function(dat,i){
return matrix.map(function(d,ii){
return {x: ii, y: d[i+1] };
})
});
console.log("NEW ONE !!!\n",matrix[0]);
// console.log("LAYOUT---------------------------");
var stacked = d3.layout.stack()(remapped)
x.domain(stacked[0].map(function(d) { return d.x; }));
y.domain([0, d3.max(stacked[stacked.length - 1], function(d) { return d.y0 + d.y; })]);
// Add a group for each column.
var valgroup = svg.selectAll("g.valgroup")
.data(stacked)
.enter().append("svg:g")
.classed("valgroup", true)
.style("fill", function(d, i) { return z(i); })
.style("stroke", function(d, i) { return d3.rgb(z(i)).darker(); });
// Add a rect for each date.
var rect = valgroup.selectAll("rect")
.data(function(d){return d;})
.enter().append("svg:rect")
.transition(t)
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return -y(d.y0) - y(d.y); })
.attr("height", function(d) { return y(d.y); })
.attr("width", x.rangeBand());
// column
rect.selectAll("rect")
.transition() // this is to create animations
.duration(500) // 500 millisecond
.ease("bounce")
.delay(500)
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return -y(d.y0) - y(d.y); })
.attr("height", function(d) { return y(d.y); })
.attr("width", x.rangeBand());
};
render(matrix);
setInterval( function() { render([[1, rand_it(10), rand_it(50)]]); console.log("2"); }, 5000 );
</script>
</body>
</html>
You are not using the transition() correctly. A transition changes from a previous value to a final value. So, in this code:
var something = svg.append("something").attr("x", 10);
something.transition().duration(500).attr("x", 20);
The x attribute of something will change from 10 to 20 in 500ms.
But when you do, as you did:
var rect = valgroup.selectAll("rect")
.data(function(d){return d;})
.enter().append("svg:rect")
.transition(t)
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return -y(d.y0) - y(d.y); })
.attr("height", function(d) { return y(d.y); })
.attr("width", x.rangeBand());
Where are the previous values? This is an "enter" selection. To make things more complicated, you did:
svg.selectAll("rect")
.transition(t)
.remove();
In the beginning of the function, so, there is no rectangle to make any transition.
I made a few changes in your code, removing the remove() and creating some "update" selections: https://jsfiddle.net/gerardofurtado/3ahrabyj/
Please have in mind that this is not an optimised code, even less a beautiful code: I made just the bare minimum changes to make the transitions to work, you'll have to make a lot of improvements here.
This source code is working only when I put a breakpoint in Firefox Debugger.
Without the breakpoint the circles are not shown and the svg contains no elements in it. On Chrome it's not working in any case.
I run Linux
I usually put the breakpoint on the line .nodes(d3.values(nodes))
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.6/d3.min.js" charset="utf-8"></script>
<body>
<scrip>
var nodes;
d3.json("./nodes-mod.json", function(error, json){
nodes = json;
});
var width = 420,
var height = 420;
var force = d3.layout.force()
.nodes(d3.values(nodes))
//links(links)
.size([width, height])
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var fill = d3.scale.category10();
var node = svg.selectAll(".node")
.data(d3.values(nodes))
.enter().append("circle")
.attr("class", "node")
.attr("cx", function(d, i) { return i*10 + 50; })
.attr("cy", function(d, i) { return i*10 + 50; })
.attr("r", 28)
.style("fill", function(d, i) { return fill(i & 3); })
.style("stroke", function(d, i) { return d3.rgb(fill(i & 3)).darker(2); })
.call(force.drag)
.on("mousedown", function() { d3.event.stopPropagation(); });
</script>
</body>
</html>
Disclaimer:
the following answer is not a right answer, please see the comment d3 renders a force layout only if a breakpoint is used in firefox to get the right approach.
I used a solution posted in https://stackoverflow.com/a/24953/428399
by /u/Marius
and wrapped part of your code:
setTimeout(function(){
var force = d3.layout.force()
.nodes(d3.values(nodes))
....
.call(force.drag)
.on("mousedown", function() { d3.event.stopPropagation(); });
}, 2000);
Now it should work.