Network line with arrow in US map - javascript

I am trying to give an arrow sign on a US network map to indicate the direction of the link.I have followed the http://bl.ocks.org/d3noob/5141278 link for this and many other but can't able to achieve the same.
I have created a fiddler for this.Kindly have a look on this and please provide some solution to achieve this.
https://jsfiddle.net/AmitSah/bp6p3p92/
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
radius = 10;
/*svg.call(d3.zoom().on("zoom", function () {
svg.attr("transform", d3.event.transform);
}));*/
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.node_id; }).strength(1) )
//.force("charge", d3.forceManyBody())
.force("collide", d3.forceCollide(radius + 1).iterations(4))
.force("center", d3.forceCenter(width / 2, height / 2));
// projection definition
var projection = d3.geoAlbers()
.scale(1280)
.translate([width / 2, height / 2]);
// path generator definition for major cities, including point radius
var path = d3.geoPath()
.projection(projection)
.pointRadius(2);
// draws the states
d3.json("https://raw.githubusercontent.com/LogicalInsightscg/Web-Mobile-Dashboard/master/us_new.json", function(error, us_new) {
if (error) return console.error(error);
svg.selectAll(".states")
.data(topojson.feature(us_new, us_new.objects.states).features)
.enter().append("path")
.attr("class", function(d) { return "states " + d.id; })
.attr("d", path);
// adding state boundaries
svg.append("path")
.datum(topojson.mesh(us_new, us_new.objects.states))
.attr("d", path)
.attr("class", "state-boundary");
d3.json("https://raw.githubusercontent.com/LogicalInsightscg/Web-Mobile-Dashboard/master/nodesandlinks.json", function(error, graph) {
if (error) throw error;
// build the arrow.
svg.append("svg:defs").selectAll("marker")
.data(["end"]) // Different link/path types can be defined here
.enter().append("svg:marker") // This section adds in the arrows
.attr("id", String)
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("svg:line")
.attr("d", "M0,-5L10,0L0,5");
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.referrals); })
.attr("marker-end", "url(#end)");
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); })
//.attr("fixed", true)
.attr("cx", function(d) {
return projection([d.longitude, d.latitude])[0];
})
.attr("cy", function(d) {
return projection([d.longitude, d.latitude])[1];
})
/* .call(d3.drag()
.on("end", dragended)); */
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
node.attr('fx', function(d) { return projection([d.longitude, d.latitude])[0]; })
.attr('fy', function(d) { return projection([d.longitude, d.latitude])[1]; });
link.attr('x1', function(d) { return projection([d.source.longitude, d.source.latitude])[0]; })
.attr('y1', function(d) { return projection([d.source.longitude, d.source.latitude])[1]; })
.attr('x2', function(d) { return projection([d.target.longitude, d.target.latitude])[0]; })
.attr('y2', function(d) { return projection([d.target.longitude, d.target.latitude])[1]; });
node.append("title")
.text(function(d) { return d.node_name; });
function ticked() {
link
.attr("distance", 10)
// .attr("x1", function(d) { return d.source.x; })
// .attr("y1", function(d) { return d.source.y; })
.attr('x1', function(d) { return projection([d.source.longitude, d.source.latitude])[0]; })
.attr('y1', function(d) { return projection([d.source.longitude, d.source.latitude])[1]; })
.attr('x2', function(d) { return projection([d.target.longitude, d.target.latitude])[0]; })
.attr('y2', function(d) { return projection([d.target.longitude, d.target.latitude])[1]; });
node.attr('cx', function(d) {if (d.group > 0) { return projection([d.longitude, d.latitude])[0]; }
else { return d.x; }})
.attr('cy', function(d) {if (d.group > 0) { return projection([d.longitude, d.latitude])[1]; }
else { return d.y; }});
}
/* function ticked() {
link.attr('x1', function(d) { return projection([d.source.longitude, d.source.latitude])[0]; })
.attr('y1', function(d) { return projection([d.source.longitude, d.source.latitude])[1]; })
.attr('x2', function(d) { return projection([d.target.longitude, d.target.latitude])[0]; })
.attr('y2', function(d) { return projection([d.target.longitude, d.target.latitude])[1]; });
node.attr('r', width/100)
.attr('cx', function(d) { return projection([d.longitude, d.latitude])[0]; })
.attr('cy', function(d) { return projection([d.longitude, d.latitude])[1]; });
} */
});
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
Thanks in advance.
Amit Sah

Related

D3.js: How to highlight connected edges and nodes in a Force-Directed Graph?

I'm trying to build a Force-Directed Graph with Link Highlighting.
Initially the graph will look like the following:
But after hovering on a particular node I want it to become like the following (only connected nodes and edges will be highlighted):
I've tried the following code. But the highlighting part is not working.
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("miserables.json", function (error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter()
.append("line")
.attr("stroke-width", function (d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(graph.nodes)
.enter()
.append("g")
.on("mouseover", mouseover)
.on("mouseout", mouseout)
.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);
var circles = node.append("circle")
.attr("r", 10)
.attr("fill", function (d) { return color(d.group); });
var lables = node.append("text")
.text(function (d) {
return d.id;
})
.attr("x", 0)
.attr("dy", ".35em")
.attr("text-anchor", "middle");
node.append("title")
.text(function (d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
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; });
node
.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
})
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
graph.links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
linkedByIndex[d.target.index + "," + d.source.index] = 1;
});
function neighboring(a, b) {
return a.index == b.index || linkedByIndex[a.index + "," + b.index];
}
function mouseover(d) {
d3.selectAll("link").transition().duration(500)
.style("opacity", function (o) {
return o.source === d || o.target === d ? 1 : 0;
});
d3.selectAll("node").transition().duration(500)
.style("opacity", function (o) {
return neighboring(d, o) ? 1 : 0;
});
}
function mouseout() {
d3.selectAll("link").transition().duration(500)
.style("opacity", 1);
d3.selectAll("node").transition().duration(500)
.style("opacity", 1);
}
The full code can be found at https://plnkr.co/edit/BA9OfjVQjOwyHDkh
Could you please help me find the issue here and make it work?

Adding Multiple shaped nodes to a force directed network diagram in d3v4

I am Trying to add rectangle and circle nodes in d3v4, the graph works although the nodes are all grouped together in one corner and their positions are not being updated. I can't work out what i'm doing wrong?
I have tried looking for examples online but cant seem to find any that are using d3v4 specifically
<!DOCTYPE html>
<meta charset="UTF-8">
<style>
</style>
<body>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
// Properties
var width = 800;
var height = 600;
var nominal_stroke = 4;
// Simulation
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(-400))
.force("center", d3.forceCenter(width / 2, height / 2));
// Create SVG window
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g");
svg.style("cursor", "move");
// Load JSON data
d3.json("./network.json", function(error, graph) {
console.log(graph);
if (error) throw error;
// Draw links
var link = g.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", nominal_stroke)
.style("stroke", "#999")
.style("stroke-opacity", 0.6);
// Draw nodes
var node = g.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// Setup node properties
var circle = node.append("path")
.attr("d", d3.symbol()
.type(function (d) {
if
(d.shape == "rect") {
return d3.symbolSquare;
} else if
(d.shape == "circle") {
return d3.symbolCircle;
}
})
.size(400))
.style("stroke", "#999")
.style("stroke-opacity", 0.6)
.style("fill", function (d) {
return "blue"
});
// Add titles
node.append("title")
.text(function (d) {return d.id;});
// Start Simulation
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
// Refresh page
function ticked() {
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; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
// Zoom handler
svg.call(d3.zoom()
.scaleExtent([1 / 2, 8])
.on("zoom", zoomed));
function zoomed() {
node.attr("transform", d3.event.transform);
link.attr("transform", d3.event.transform);
}
});
// 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.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = d.x;
d.fy = d.y;
}
function openLink() {
return function (d) {
var url = "";
if (d.url != "") {
url = d.url
}
window.open(url)
}
}
</script>
</body>
You should not use cx and cy in the ticked function, since you're dealing with <path>s, not <circle>s. You should use translate instead.
Therefore, it has to be:
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
Here is your code with that change (I'm using fake data here):
var width = 600;
var height = 400;
var nominal_stroke = 4;
// Simulation
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody().strength(-400))
.force("center", d3.forceCenter(width / 2, height / 2));
// Create SVG window
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
var g = svg.append("g");
svg.style("cursor", "move");
graph = {
nodes: [{
id: 1,
shape: "rect"
}, {
id: 2,
shape: "circle"
}, {
id: 3,
shape: "rect"
}, {
id: 4,
shape: "rect"
}, {
id: 5,
shape: "circle"
}, {
id: 6,
shape: "circle"
}, {
id: 7,
shape: "circle"
}],
links: [{
source: 1,
target: 2
}, {
source: 1,
target: 3
}, {
source: 1,
target: 4
}, {
source: 1,
target: 5
}, {
source: 3,
target: 6
}, {
source: 3,
target: 7
}]
}
// Draw links
var link = g.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", nominal_stroke)
.style("stroke", "#999")
.style("stroke-opacity", 0.6);
// Draw nodes
var node = g.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
// Setup node properties
var circle = node.append("path")
.attr("d", d3.symbol()
.type(function(d) {
if (d.shape == "rect") {
return d3.symbolSquare;
} else if (d.shape == "circle") {
return d3.symbolCircle;
}
})
.size(400))
.style("stroke", "#999")
.style("stroke-opacity", 0.6)
.style("fill", function(d) {
return "blue"
});
// Add titles
node.append("title")
.text(function(d) {
return d.id;
});
// Start Simulation
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
// Refresh page
function ticked() {
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;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
}
// Zoom handler
svg.call(d3.zoom()
.scaleExtent([1 / 2, 8])
.on("zoom", zoomed));
function zoomed() {
g.attr("transform", d3.event.transform);
}
// 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.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = d.x;
d.fy = d.y;
}
<script src="https://d3js.org/d3.v4.min.js"></script>
PS: Your zoom function is not working, which is a different problem. I also fixed it.

d3 js nodes are out of the window

using d3.js to visualize 50 nodes and their links, some of the nodes doesn't appear within the window (they are out of screen) - unless I drag them in:
This is my code, any suggestion how to make all the nodes appear on load? :
var width = window.innerWidth;
var height = window.innerHeight - 100;
var svg = d3.select("#sharedActivityGraph")
.append("svg")
.attr("width", width)
.attr("height", height);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink())
.force("charge", d3.forceManyBody().strength(-600))
.force("center", d3.forceCenter(width / 2, height / 2));
var links = svg.selectAll("foo")
.data(gSharedActivityGraphEdges)
.enter()
.append("line")
.style("stroke", "#ccc")
.style("stroke-width", function (e) { return e.width });
var color = d3.scaleOrdinal(d3.schemeCategory20);
var node = svg.selectAll("foo")
.data(gSharedActivityGraphNodes)
.enter()
.append("g")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.on('mouseover', function (d) {
console.log(d);
SharedActivityShowInfo(d);
node.filter(function (d1) { return (d !== d1
&& d1.adjacents.indexOf(d.id) == -1);
}).select("image").style("opacity", 0.2);
node.filter(function (d1) { return (d == d1
|| d1.adjacents.indexOf(d.id) !== -1);
}).select("image").style("opacity", 1);
})
.on('mouseout', function () {
SharedActivityHideInfo();
node.select("image").style("opacity", 1);
});
var nodeCircle = node.append("circle")
.attr("r", function (d) { return 0.5 * Math.max(d.width, d.height) })
.attr("stroke", "gray")
.attr("stroke-width", "2px")
.attr("fill", "white");
var nodeImage = node.append("image")
.attr("xlink:href", function (d) { return d.image })
.attr("height", function (d) { return d.height + "" })
.attr("width", function (d) { return d.width + "" })
.attr("x", function (d) {return -0.5 * d.width })
.attr("y", function (d) {return -0.5 * d.height })
.attr("clip-path", function (d) { return "circle(" + (0.48 * Math.max(d.width, d.height)) + "px)"});
simulation.nodes(gSharedActivityGraphNodes);
simulation
.force("link")
.links(gSharedActivityGraphEdges);
simulation.on("tick", function() {
links.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;
})
node.attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"});
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
Ended up with adding the following lines:
node.attr("cx", function(d) { return d.x = Math.max(d.width, Math.min(width - d.width, d.x)); })
.attr("cy", function(d) { return d.y = Math.max(d.height, Math.min(height - heightDelta - d.height, d.y)); });
below the line:
node.attr("transform", function (d) { return "translate(" + d.x + "," + d.y + ")"});
#TomShanley thanks for the ref!

Perpetual Motion in the Force Layout

In the example of Mike Bostock - https://bl.ocks.org/mbostock/4062045
Is it possible to have the nodes in simple|random|slight perpetual motion to make it visualising appealing? If yes, How do I begin with the same?
It can be as simple as:
simulation
.nodes(graph.nodes)
.on("tick", ticked)
// on the end of the simulation, restart it
.on("end", function(){
simulation.alphaTarget(0.5).restart();
});
After it does the initial "settle", this will give the appearance of it "floating around":
<!DOCTYPE html>
<meta charset="utf-8">
<style>
.links line {
stroke: #999;
stroke-opacity: 0.6;
}
.nodes circle {
stroke: #fff;
stroke-width: 1.5px;
}
</style>
<svg width="960" height="600"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
d3.json("https://jsonblob.com/api/901c4b8a-1162-11e7-a0ba-a1c27e793e26", function(error, graph) {
if (error) throw error;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked)
.on("end", function(){
simulation.alphaTarget(0.5).restart();
})
simulation.force("link")
.links(graph.links);
function ticked() {
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; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
</script>

How to make the distance from nodes in d3.js force layout larger?

I am trying to represent my data with d3js force layout. I have the following code:
<script>
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height");
var color = d3.scaleOrdinal(d3.schemeCategory20);
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
var jsonData = JSON.parse(data);
var totalAmount=jsonData.total;
d3.json("myData.json", function(error, graph) {
if (error) throw error;
var nodes = json.nodes;
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(graph.links)
.enter().append("line")
//.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
.attr("stroke-width",4)
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(graph.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("title")
.text(function(d) { return d.id; });
simulation
.nodes(graph.nodes)
.on("tick", ticked);
simulation.force("link")
.links(graph.links);
function ticked() {
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; });
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
});
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = null;
d.fy = null;
}
However, in the output, the nodes are so close to each others like in this photo:
My result
I checked that links are correct (basically I have one source and so many target nodes.)
How can I make larger the distance between nodes? Thanks for any help.
Try this
simulation = d3.forceSimulation()
.force("link", d3.forceLink().distance(value).id(function(d) { return d.id; }))
...
Where value is some number

Categories

Resources