D3 Force Nodes Clustering at Top Left - d3.js

https://jsfiddle.net/abalasky/b28mxfL6/7/
function update() {
//Add Nodes
simulation.nodes(graph.nodes)
updateNodes();
simulation.on("tick", ticked);
//Add links
simulation.force('link').links(graph.links)
updateLinks();
}
Link to fiddle above. Was following along with this great D3 guide https://tomroth.com.au/fdg-minimal/. Was able to have my force graph up quickly but now that i am trying to repackage everything in functions all my nodes are clustering at the top. I've tried to move around where i call .on('tick') but have been banging my head against the wall with this for hours. Any guidance would beg greatly appreciated.

The problem is in the definition of node and link variables:
In the original code, these contain the enter selection, e.g.
//draw circles for the nodes
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes_data)
.enter()
.append("circle")
.attr("r", 5)
.attr("fill", "red");
In the jsfiddle code, the selection itself is assigned to the variable:
node = nodesG.selectAll("circle.node")
.data(graph.nodes)
This causes tick not to have anything on what to iterate.
Here is a corrected version of the jsfiddle: https://jsfiddle.net/cb9t7fjo/
how to identify the cause of this type of problems
console.log is your friend in situations like this:
console.log('tick', node) inside the tick function shows that the tick function is executed as expected.
console.log(d.x) inside node.attr('x') shows you that this is never executed.
This helps identify that the problem is with node (and respectively link) variables.

Related

D3.js binding nested data

I'm really new to coding, and also to asking questions about coding. So let me know if my explanation is overly complex, or if you need more context on anything, etc.
I am creating an interactive map of migration flows on the Mediterranean Sea. The flows show origin and destination regions of the migrant flows, as well as the total number of migrants, for Italy and Greece. Flows should be displayed in a Sankey diagram like manner. Because I am displaying the flows on a map and not in a diagram fashion, I am not using D3’s Sankey plugin, but creating my own paths.
My flow map, as of now (curved flows are on top of each other, should line up next to each other)
For generating my flows I have four points:
2 points for the straight middle part of the flow (country total)
1 point each for the curved outer parts (origin and destination region), using the two points of the straight middle part as starting points
The straight middle and both curved outer parts are each generated independently from their own data source. Flow lines are updated by changing the data source and calling the function again. The flow lines are generated using the SVG path mini-language. In order for the curved outer parts of the flows to show correctly, I need them to be lined up next to each other. To line them up correctly, I need to shift their starting points. The distance of the shift for each path element is determined by the width of the path elements before it. So, grouping by country, each path element i needs to know the sum of the width of the elements 0-i in the same group.
After grouping my data with d3.nest(), which would allow me to iterate over each group, I am not able to bind the data correctly to the path elements
I also can't figure out a loop function that adds up values for all elements 0-i. Any help here? (Sorry if this is kind of unrelated to the issue of binding nested data)
Here is a working function for the curved paths, working for unnested data:
function lineFlow(data, flowSubGroup, flowDir) {
var flowSelect = svg.select(".flowGroup").select(flowSubGroup).selectAll("path");
var flow = flowSelect.data(data);
var flowDirection = flowDir;
flow.enter()
.append("path").append("title");
flow
.attr("stroke", "purple")
.attr("stroke-linecap", "butt")
.attr("fill", "none")
.attr("opacity", 0.75)
.transition()
.duration(transitionDur)
.ease(d3.easeCubic)
.attr("d", function(d) {
var
slope = (d.cy2-d.cy1)/(d.cx2-d.cx1),
dist = (Math.sqrt(Math.pow((d.rx2-d.rx1),2)+Math.pow((d.ry2-d.ry1),2)))*0.5,
ctrlx = d.rx1 + Math.sqrt((Math.pow(dist,2))/(1+Math.pow(slope,2)))*flowDirection,
ctrly = slope*(ctrlx-d.rx1)+d.ry1;
return "M"+d.rx1+","+d.ry1+"Q"+ctrlx+","+ctrly+","+d.rx2+","+d.ry2})
.attr("stroke-width", function(d) {return (d.totalmig)/flowScale});
flowSelect
.select("title")
.text(function(d) {
return d.region + "\n"
+ "Number of migrants: " + addSpaces(d.totalmig)});
};
I tried adapting the code to work with data grouped by country:
function lineFlowNested(data, flowSubGroup, flowDir) {
var g=svg.select(".flowGroup").select(flowSubGroup).append("g").data(data).enter();
var gflowSelect=g.selectAll("path");
var gflow=gflowSelect.data (function(d) {return d.values});
gflow.enter()
.append("path");
gflow.attr("stroke", "purple")
.attr("stroke-linecap", "butt")
.attr("fill", "none")
.attr("opacity", 0.75)
// .transition()
// .duration(transitionDur)
// .ease(d3.easeCubic)
.attr("d", function(d) {
var
slope = (d.cy2-d.cy1)/(d.cx2-d.cx1),
dist = (Math.sqrt(Math.pow((d.rx2-d.rx1),2)+Math.pow((d.ry2-d.ry1),2)))*0.5,
ctrlx = d.rx1 - Math.sqrt((Math.pow(dist,2))/(1+Math.pow(slope,2)))*flowDirection,
ctrly = slope*(ctrlx-d.rx1)+d.ry1;
return "M"+d.rx1+","+d.ry1+"Q"+ctrlx+","+ctrly+","+d.rx2+","+d.ry2})
.attr("stroke-width", function(d) {return (d.totalmig)/flowScale});
};
which isn't working. What am I doing wrong? Thanks for any hints!

d3.js sankey diagram: where does ".dy" and .dx" get set?

I am busy cannibalizing the great tutorial on D3.js sankey diagrams at http://www.d3noob.org/2013/02/sankey-diagrams-description-of-d3js-code.html . I want to tweak the stroke-width setting on the paths.
There is a significant block of code at this URL for context, but I want to ask about this passage:
var link = svg.append("g").selectAll(".link")
.data(graph.links)
.enter().append("path")
.attr("class", "link")
.attr("d", path)
.style("stroke-width", function(d) { return Math.max(1, d.dy); })
.sort(function(a, b) { return b.dy - a.dy; });
See the "d.dy" reference there? How/where exactly does that get set? I don't see an explicit reference to in its the larger code passage, or in its data source. I will go and continue googling on it now, but if you know a simple answer or resource I would appreciate your help. Right now my best guess is that some part of the sankey plugin, or the link class, sets it on the fly by looking at the size and position of the node rectangles.
It happens internally in sankey.js in the .layout call. See dx here and dy here.

d3.geo.circle angle does not work?

So, I've tried following Jason Davies' answer to the Circle clip and projection with D3 orthographic thread...
I've tried 3 different possibilities :
svg.append("g").attr("class","points")
.selectAll("path")
.data(places.features)
.enter()
.append("path")
//THIS COMMENTED PART DOES NOT WORK
//though the console spits out an array
/*.datum(function(d){
console.log(d.geometry.coordinates)
return d3.geo.circle()
.origin(d.geometry.coordinates)
.angle(2)
})*/
//THIS UNCOMMENTED PART WORKS
//and places the circles accordingly with a fixed size radius
.datum(d3.geo.circle()
.origin(function(d){return d.geometry.coordinates})
.angle(2)
)
//THIS COMMENTED PART DOES NOT WORK
//which is a shame, as my goal is to change the angle of each circle
/*.datum(d3.geo.circle()
.origin(function(d){return d.geometry.coordinates})
.angle(function(d){return 2})
)*/
.attr("class", "point")
.attr("d", path)
How do I get the last commented part to work so that I can change the radius of my circles? Thanx a lot.
After updating to d3 version "5.7.0" I had same issue.
Fixed by using geoCircle.center(center).radius(angle) instead of geo.circle().angle(angle).
Here you can find how it could work.

error computing area of a closed space in a force layout using d3js

I have a graph which I've laid out using force layout. Basically i want to color the area contained within the cycles in the graph. Here's my code.
I was trying to paint the cycles when the force layout stabilized.
force.on("tick",function(){
// ... I update the position of nodes and the links
if(force.alpha() < 0.006){
force.stop();
// I dont know if there's an easier way of doing this
var xnodes = [];
force.nodes().forEach(function(d){
xnodes.push([d.x, d.y]);
});
// I tried creating a path and filling with with green
vis.select("path.area")
.data([xnodes])
.enter().append("path")
.style("fill", "#00ff00")
.attr("class", "area")
.attr("svg:d", d3.svg.area());
}
});
When i run it in chrome, the debugger shows that it does create a path but the area of the path is 0px x 0px. I'm really confused with this. I even tried setting the array manually. I get the same error.
var ynodes = [[352.3554660996234,304.3361316660001],[449.88953454311286,313.14153937680953],
[392.0458559272036,389.6656922220398],[352.3554660996234,304.3361316660001]];
vis.select("path.area")
.data([ynodes])
.enter().append("path")
.style("fill", "#00ff00")
.attr("class", "area")
.attr("svg:d", d3.svg.area());
However when i put this code in a blank html file (after you include the necessary libraries) it works fine. It does draw a path and fills it with green. I'm really confused here. Any help would be much appreciated. Thanks.

joining multiple parts of a g element to the same data in d3

I am trying to figure out how to transition paths and text bound to the same data within a g element.
This gist shows the behavior I want. When you click the change button the path and text positions transition smoothly. The problem is I accomplish this by separately joining the path and text elements to the data. I am not the only one using join this way, but I think it violates the goal of maintaining one to one mapping between elements and data with d3.
Is there a way to emulate the
var path = svg.selectAll("path")
.data(dataset);
path
.enter().append("path")
.attr("d", function(d) { return line(d.values) + "Z"; })
.style("fill", function(d, i) { return color(i); });
path
.transition().duration(500)
.attr("d", function(d) { return line(d.values) + "Z"; });
path
.exit().remove();
pattern for elements linked to the same data within a g element?
Here is one of my failed attempts. Rather than updating the paths and text, the change button causes new g elements to be added.
Added on edit: failed attempt now works after correcting the typo found by explunit
You have a typo in your class name on the join. This code:
var g = svg.selectAll(".shapes")
.data(dataset);
Should be this:
var g = svg.selectAll(".shape")
.data(dataset);
When I change it as above it works fine for me.

Resources