vue and d3 force layout, network visualization - d3.js

So I'm new to d3 and very little experimented with vue.
What I want to do is graph the network after the data has been fetched in a vue component.
I tried to recreate some of the older codes about vue and d3 force layout, and tried to adapt this example, but none of them worked out and I don't really know why.
The closest I got to what I want is probably this answer, but I want it to graph after the data has been fetched.
My code looks like this right now :
<script>
import * as d3 from "d3";
export default {
name: "MapComponent",
data() {
return {
mapData: {}
};
},
created() {
this.mapdataget();
},
computed() {
this.data_vis();
},
methods: {
mapdataget: function () {
this.$store
.dispatch("mapData_get")
.then(() => {
this.mapData = this.$store.getters.mapData;
})
.catch();
},
data_vis() {
let nodes = this.mapData.nodes;
let links = this.mapData.links;
let svg = d3.select("svg")
this.simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function (d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
let link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(links) //graph.links)
.enter().append("line")
.attr("stroke-width", function (d) {
return Math.sqrt(d.value);
});
let node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(nodes) //graph.nodes)
.enter().append("circle")
.attr("r", 5)
.call(d3.drag()
.on("start", this.dragstarted)
.on("drag", this.dragged)
.on("end", this.dragended));
node.append("title")
.text(function (d) {
return d.id;
});
this.simulation
.nodes(nodes)
.on("tick", ticked);
this.simulation.force("link")
.links(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;
});
}
}
}
}
</script>
with mapData looking like this :
const mapData = {
'nodes': [{
'id':String
}, and more],
'links': [{
'id':String,
'source': sourceNodeId,
'target': targetNodeId
}, and more]
}
and the vue template is an svg :
<template>
<svg class='svg'></svg>
</template>
And I got an error :
[Vue warn]: Invalid value for option "computed": expected an Object,
but got Function.

In computed() you define variables that update the DOM at runtime, calling a method there without assigning a returned value or object to a variable is wrong. You should try moving this.data_vis() into the mounted() hook instead.

Related

d3 text elements not rendering inside circle

I'm trying to add texts to svg circles. I've read other posts about using the grouping "g" tag, and I've tried that, but my text still isn't rendering. I see the text element exists in the console though.
Here's the relevant code:
var node = svg
.selectAll(".node")
.data(data.nodes)
.enter()
.append("g")
.attr("class", "node")
.call(
d3
.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended)
);
node
.append("circle")
.attr("r", (d) => {
return d.type === "application" ? 50 : 20;
})
.attr("fill", (d) => {
return d.type === "application" ? "red" : "blue";
});
node
.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function (d) {
return d.name;
});
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;
});
node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
function dragstarted(d) {
if (!d3.event.active) force.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) force.alphaTarget(0);
d.fx = null;
d.fy = null;
}
Added correct code based on accepted answer.
Why is this still not rendering?
One way you can solve this is to bind your data to the group elements first and then append circles and text individually. So your code becomes:
var node = svg
.selectAll("g")
.data(data.nodes)
.enter().append("g")
.attr("class", "nodes");
node
.append("circle")
// all circle-related code
node
.append("text")
// all text related code, including x and y
Only two (AFAIK) svg elements allow other elements to be nested within them: g and text. When you call append on the selection of circles, you're appending a text element as a child of circle - which it doesn't support.

Tooltip using ng-bootstrap on SVG elements not showing

I created circle using D3.js. I used ng-bootstrap for Tooltip but It is not showing on svg element,
g.append("g").selectAll("circle").data(this.RecordDaily).enter().append("circle")
.attr("r", 5).attr("fill", "purple")
.attr("cx", function (d) { return x(parseTime(d.moy)); })
.attr("cy", function (d, i) { return y(d.session); })
.attr("data-toggle", "tooltip").attr("title", function (d) { return d.session })
.attr("ngbTooltip", function (d) { return d.session })
.attr("placement", "top")

Missing links in force directed graph

I'm using Modifying a Force Directed Graph II as a base for my graph. I thought I'd coped with the different ways we're handling how links are formatted (Bostock's uses references, mine uses strings), by changing
simulation.force("link", d3.forceLink(links).distance(200))
to
simulation.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
but no dice. The links still aren't being drawn. What else am I missing?
My jsfiddle is here.
There are two errors in the code you posted in your question:
When modifying the link force in your code you are no longer providing the links to the force since you are not providing links as an argument. It has to be d3.forceLink(links).
The function provided to .id() is the accessor function to the nodes' ids. In your case the id of a node is defined by its property name. Thus, you need to change the accessor to .id(function(d) { return d.name; }).
Change the definition of the link force to the following to make it work:
.force("link", d3.forceLink(links).id(function(d) {
return d.name;
}))
Have a look at the following snippet for a working demo:
nodes = [{
name: "test",
type: "test"
}, {
name: "test2",
type: "test2"
}];
links = [{
source: "test",
target: "test2"
}];
var width = 500,
height = 500;
var svg = d3.select("svg"),
color = "blue";
var simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceManyBody().strength(-1000))
.force("link", d3.forceLink(links).id(function(d) {
return d.name;
}))
.force("x", d3.forceX())
.force("y", d3.forceY());
var g = svg.append("g").attr("transform", "translate(" + width / 2 + "," + height / 2 + ")"),
link = g.append("g").attr("stroke", "#000").attr("stroke-width", 1.5).selectAll(".link"),
node = g.append("g").attr("stroke", "#fff").attr("stroke-width", 1.5).selectAll(".node");
forceGraphUpdate = function() {
node = node.data(nodes);
node.exit().transition().attr("r", 0)
.remove();
node = node.enter().append("circle")
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended))
.attr("fill", function(d) {
return color;
})
.call(function(node) {
node.transition().attr("r", 8)
})
.merge(node);
link = link.data(links);
link.exit().transition()
.attr("stroke-opacity", 0)
.attrTween("x1", function(d) {
return function() {
return d.source.x;
};
})
.attrTween("x2", function(d) {
return function() {
return d.target.x;
};
})
.attrTween("y1", function(d) {
return function() {
return d.source.y;
};
})
.attrTween("y2", function(d) {
return function() {
return d.target.y;
};
})
.remove();
link = link.enter().append("line")
.call(function(link) {
link.transition().attr("stroke-opacity", 1);
})
.merge(link);
simulation
.nodes(nodes)
.on("tick", ticked);
}
function ticked() {
node.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return 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;
});
}
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;
}
forceGraphUpdate();
function addNode() {
nodes.push({
name: "test",
type: "testing"
});
forceGraphUpdate();
}
addNode();
<script src="https://d3js.org/d3.v4.js"></script>
<svg width="500" height="500"></svg>

Create points on a D3 multiline graph

I'm trying to add points to a line graph using d3 in this example:
http://bl.ocks.org/mbostock/3884955
I was also trying to follow this post
How do you get the points to look like this picture from the documentation?
http://github.com/mbostock/d3/wiki/line.png
The stroke of the circle should match the line color.
var color = d3.scale.category10();
d3.csv("data.csv", function(error, data) {
color.domain(d3.keys(data[0]).filter(function(key) { return key !== "date"; }));
data.forEach(function(d) {
d.date = parseDate(d.date);
});
var ranks = color.domain().map(function(name) {
return {
name: name,
values: data.map(function(d) {
return {date: d.date, ranking: +d[name]};
})
};
});
var rank = svg.selectAll(".rank")
.data(ranks)
.enter().append("g")
.attr("class", "rank");
rank.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); })
.style("stroke", function(d) { return color(d.name); });
var point = rank.append("g")
.attr("class", "line-point");
point.selectAll('circle')
.data(function(d){ return d.values})
.enter().append('circle')
.attr("cx", function(d) { return x(d.date) })
.attr("cy", function(d) { return y(d.ranking) })
.attr("r", 3.5)
.style("fill", "white")
.style("stroke", function(d) { return color(d.name); });
.style("stroke", function(d) { return color(this.parentNode.__data__.name); })
See JSBin code
Found answer here

change the style with 'selectAll' and 'select' problems

I am trying to change the style of some svg element.
when I do this:
var circleSelected = d3.select("#circleid_2");
circleSelected.style("fill", "purple");
circleSelected.style("stroke-width", 5);
circleSelected.style("stroke", "red");
the circle is changing its style.
BUT, when i do this:
var allCircles = d3.selectAll(".circle");
allCircles.forEach(function (circle) {
circle.style("fill", "green"); //function(d) { return getNodeColor(d); }
});
it does not work with the error: Object [object SVGCircleElement] has no method 'style'
and here is my 'circle' declaration (note: it has both class and id):
node.append("circle")
.attr("id", function (d) { return "circleid_" + d.id; })
.attr("class", "circle")
.attr("cx", function (d) { return 0; })
.attr("cy", function (d) { return 0; })
.attr("r", function (d) { return getNodeSize(d); })
.style("fill", function (d) { return getNodeColor(d); })
.style("stroke", function (d) { return getNodeStrokeColor(d); })
.style("stroke-width", function (d) { return getNodeStrokeWidth(d); });
What am I doing wrong here? Thanks for the help!
Try:
d3.selectAll("circle").style("fill", "green");
Or:
allCircles.style("fill", "PaleGoldenRod");
Explanation: d3.selectAll will return a selection, which can be acted upon using the functions described in this API: https://github.com/d3/d3/blob/master/API.md#selections-d3-selection
However, as soon as you do forEach, the internal variable returned each time as circle will be an actual DOM element - no longer a selection, and therefore no style function attached.

Resources