I'm trying to implement the Sankey plugin with d3. I'm not getting the output I expected though. Hopefully someone can help.
I've reduced down to simplest possible dataset.
There are 3 nodes. I would expect one link from Entry to Exit, and one going from Entry to Zone1, then Exit. Here is the set:
const graph = {
nodes: [
{'node': 0, 'name': 'Entry'},
{'node': 1, 'name': 'Zone 1'},
{'node': 2, 'name': 'Exit'}
],
links: [
{"source": 0, "target": 1, "value": 2},
{"source": 1, "target": 2, "value": 2},
{"source": 0, "target": 2, "value": 4}
]
}
However I'm just getting one link pathway, and also it looks kind of mashed up
Here is the link code:
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;
});
link.append("title")
.text(function (d) {
return d.source.name + " → " +
d.target.name + "\n" + format(d.value);
});
There was no explicit stroke color in the link code. I just needed to add:
.attr("stroke", "#CDCDCD")
Related
I have a data array that looks like this:
var data = [
{
"key": "user",
"values": [
"roles",
"manager",
"otherRoles",
"reports",
"devices",
"coffees"
]
},
{
"key": "role",
"values": [
"assignments",
"members",
"otherMembers"
]
},
{
"key": "assignment",
"values": [
"roles"
]
},
{
"key": "Device",
"values": [
"owners",
"roles"
]
},
{
"key": "Coffee",
"values": [
"drinkers"
]
}
];
I am attempting to render tables from SVG rectangles where the table header is the "key" and the table rows are the "values". The only way I was able to make this work was to give a unique incremental class to each table (table0, table1 etc.) I know this is terrible and prevents me from easily accessing all tables in the future. Here is the pertinent code:
parentBox = svgContainer.selectAll('.table')
.data(data, function(d) {return d.key;})
.enter()
.append('g')
.attr('class', function(d, i) {
return "table" + i;
})
.attr("transform", function(d, i) {
return "translate(" + boxWidth*i + "," + rowHeight*i + ")";
});
I'd like to figure out the right way to access nested data in D3. Here is the code in its entirety: D3 Example
Turns out the solution just requires understanding selections better. I first created the parentBox container:
parentBox = svgContainer.selectAll('.table')
.data(data, function(d) {return d.key;})
.enter()
.append('g')
.attr('class', 'table') (etc.)
Then, when populating the table with rows I first selected the created tables, used the each method to loop through each table and create each row. The one trick was to use d3.select(this) to make sure the rows were created properly.
svgContainer.selectAll('.table')
.each(function (d, i) {
tableRows = d3.select(this).selectAll('.row')
.data(d.values)
.enter()
.append(g) (etc.)
Say I have an Array of these objects:
[
{name: "P1", Subj1: 9.7, Subj2: 10, Subj3: 9.5, Subj4: 8.2, Subj5:9.3, Subj6: 8.9},
{name: "P2", Subj1: 9.7, Subj2: 10, Subj3: 9.5, Subj4: 8.2, Subj5:9.3, Subj6: 8.9},
{name: "P3", Subj1: 9.7, Subj2: 10, Subj3: 9.5, Subj4: 8.2, Subj5:9.3, Subj6: 8.9} ]
I would like to plot a bar chart for one person. I currently have this code:
d3.select("#chart").selectAll("div.h-bar")
.data(getObj(name))
.enter()
.append("div")
.attr("class", "h-bar")
.append("span");
//delete elements not associated with data element
d3.select("#chart").selectAll("div.h-bar")
.data(getObj(name))
.exit().remove();
d3.select("#chart").selectAll("div.h-bar")
.data(getObj(name))
.attr("class", "h-bar")
.style("width", function (d) {
return ( d.Subj1* 10) + "px";
}
)
.select("span")
.text(function (d) {
return d.name;
});
with the getObj(name) function:
function getObj(Name){
for(var i = 0; i <studentList.length; i++){
if(studentList[i].Naam == Name){
console.log(studentList[i]);
return [studentList[i]];
}
}
}
I cant figure out how to get a bar chart for person 1 where every bar has a width of achieved note, and the span text the name of the subject.
Any hints?
I'm new to d3 and haven't much web frontend development experience. For a web application I have I'm trying to draw a force directed graph. I've been trying the last few hours to get it to work. I've been looking at lots of different code example and what I'm doing looks very similar. I eventually got nodes to draw but the links between the nodes don't show up and I was trying different things and nothing seems to work. I don't know why my code wouldn't draw the edges.
From printing the nodes and links to the console I saw that the nodes got extra attributes like the d3 docs had mentioned but the links never seem to get these attributes. Below is my javascript file and the JSON file. I reduced the JSON file to only 3 entries to try and make it easier to solve the problem.
var height = 1080;
var width = 1920;
var color = d3.scale.category20();
var force = d3.layout.force()
.linkDistance(-120)
.linkStrength(30)
.size([width, height]);
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
d3.json("/static/javascript/language_data.json", function(data){
force
.nodes(data.languages)
.links(data.language_pairs)
.start();
var link = svg.selectAll(".link")
.data(data.language_pairs)
.enter().append("line")
.attr("class", "link");
var node = svg.selectAll(".node")
.data(data.languages)
.enter().append("circle")
.attr("class", "node")
.attr("r", 5)
.style("fill", function(d) { return color(d.group); })
.call(force.drag);
node.append("title")
.text(function(d) { return d.language; });
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("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
});
});
Here is the JSON file:
From looking at few examples my understanding is that the source and target are index positions from the list of nodes.
{
"languages":[
{"language": "TypeScript", "group": 1},
{"language": "Java", "group": 2},
{"language": "VHDL", "group": 3}
],
"language_pairs":[
{"source": "0", "target": "1", "value": 5},
{"source": "1", "target": "2", "value": 5},
{"source": "2", "target": "0", "value": 5}
]
}
Sorry if I left out anything! Thanks for any help!
Two issues:
1.) Your "language_pairs" source/target indexes are strings and not numbers. Get rid of the quotes:
"language_pairs":[
{"source": 0, "target": 1, "value": 5},
{"source": 1, "target": 2, "value": 5},
{"source": 2, "target": 0, "value": 5}
]
2.) Your linkDistance and linkStrength parameters don't make sense:
var force = d3.layout.force()
.linkDistance(-120) // negative distance?
.linkStrength(30) // according to the docs, this must be between 0 and 1?
.size([width, height]);
Here's an example that fixes these problems.
I'm very new to doing anything with d3 and jSon. Here is a pice of data I'm trying to get out from json and I would just like to know if I'm even on the right path.
Basically each status group would have more servers inside than just one like at the moment and the idea would be to get rectangle graph for one server and list these nicely next to each other.
I've been reading a lot of tutorials and trying to browse for similiar kind of issues other people might've had, but so far had really no luck...
jSon data I'm trying to pull out
[
{
"status": "ok",
"servers":
[
{
"id": "VR01",
"servername": "Server_1",
"cpu": 45, "mem": 25,
"diskIO": 0, "bandwith": 200
}
]
},
{
"status": "attention",
"servers":
[
{
"id": "VR10",
"servername": "Server_10",
"cpu": 55, "mem": 35,
"diskIO": 1, "bandwith": 2000
}
]
},
{
"status": "warning",
"servers":
[
{
"id": "VR02",
"servername": "Server_02",
"cpu": 98, "mem": 85,
"diskIO": 1,
"bandwith": 2000
}
]
},
{
"status": "dead",
"servers":
[
{
"id": "VR20",
"servername": "Server_20",
"cpu": 0, "mem": 0,
"diskIO": 0,
"bandwith": 0
}
]
}
]
the D3 bit
<script>
var width = ("width", 1000);
var height = ("height", 800);
d3.json("mydata.json", function(data) {
var canvas = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
var status = function sortData(data){
for (i = 0; i < d.length; i++) {
if(d.status ==="ok")
canvas.selectAll("rect")
.data(d.server)
.enter()
.append("rect")
.attr("x", 25)
.attr("y", function(d, i){return 25 * i;})
.attr("fill", "purple")
}
}
})
</script>
Really appreciate any suggestions you might have!
I think that it would be better to use nested selections to create your dashboard.
// Create one group for each server group
var serverGroup = svg.selectAll('g')
.data(data)
.enter()
.append('g')
.attr('transform', function(d, i) { return 'translate(0, ' + 50 * i + ')');
// Create the inner elements for each group
var servers = serverGroup.selectAll('rect')
.data(function(d) { return d.servers; })
.enter()
.append('rect')
// ... more settings here ...
This will create three groups, one for each group of servers and translate each one vertically. Each group contains the group data, so we can use the group data to create elements inside each group. Also, you can add a title, background color and other settings for each group using this structure. This article contains the concepts that you need to work on your problem: How Selections Work. Regards,
I have a working code example (only the <script type="text/javascript"> part) of a static graph using d3.js as below:
/* Create graph data */
var nodes = [];
for (var i = 0; i < 13; i++)
{
var datum = {
"value": i
};
nodes.push(datum);
}
var links = [{"source": 0, "target": 1},
{"source": 1, "target": 2},
{"source": 2, "target": 0},
{"source": 1, "target": 3},
{"source": 3, "target": 2},
{"source": 3, "target": 4},
{"source": 4, "target": 5},
{"source": 5, "target": 6},
{"source": 5, "target": 7},
{"source": 6, "target": 7},
{"source": 6, "target": 8},
{"source": 7, "target": 8},
{"source": 9, "target": 4},
{"source": 9, "target": 11},
{"source": 9, "target": 10},
{"source": 10, "target": 11},
{"source": 11, "target": 12},
{"source": 12, "target": 10}];
/* Create force graph */
var w = 800;
var h = 500;
var size = nodes.length;
nodes.forEach(function(d, i) { d.x = d.y = w / size * i});
var svg = d3.select("body").append("svg")
.attr("width", w)
.attr("weight", h);
var force = d3.layout.force()
.nodes(nodes)
.links(links)
.linkDistance(200)
.size([w, h]);
setTimeout(function() {
var n = 400
force.start();
for (var i = n * n; i > 0; --i) force.tick();
force.stop();
svg.selectAll("line")
.data(links)
.enter().append("line")
.attr("class", "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.append("svg:g")
.selectAll("circle")
.data(nodes)
.enter().append("svg:circle")
.attr("class", "node")
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("r", 15);
svg.append("svg:g")
.selectAll("text")
.data(nodes)
.enter().append("svg:text")
.attr("class", "label")
.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; })
.attr("text-anchor", "middle")
.attr("y", ".3em")
.text(function(d) { return d.value; });
}, 10);
and it produces this rather scrambled layout:
While it is technically the correct graph, the ideal layout should be something like this (ignoring the different visual graphics):
Note that the layout should be fixed so that reloading the page does not change the positioning of each node; the layout should also be static, in that there is no animation effect and the nodes are not draggable. Both requirements are already achieved in the script above.
So how should I further configure this d3 script to produce a layout shown in the second image?
First, increase the charge strength and reduce the link distance. Doing so places a greater emphasis on global structure rather than local connections. Also, if you increase the charge strength enough, the repulsive charge will push even directly-connected nodes farther apart, thus effectively increasing the link distance while giving better overall structure. (The downside of a stronger charge force is that graph initialization is more chaotic, but this shouldn’t be a problem for static layouts.)
Second, you may need to increase the number of iterations or add custom forces to get better results. Force layouts often work well on arbitrary graphs, but there’s no guarantee that they will produce an optimal (or even good) result. For any graph where you can make simplifying assumptions (for example, trees), there may be additional forces or constraints that you can apply to encourage the simulation to converge onto a better solution.