How to display label for bi-direct link/path/line in D3js force directed layout - d3.js

I'm trying to draw a network topology using D3sj force directed layout. I did a basic topology with link and node. However, now i want to show the interface name for each link on each node as the following picture. Could you guys guide me how i can do that?
Thank you in advance!
P/S I attached my topology here!1

Ideally you should present the javascript that you have already written, and explained what it does do, and what is missing. Having said that, I recently finished work on a similar project to what you describe, so had the following results handy.
Does this jsfiddle do what you are attempting?
There are two key components. The first is in defining the text elements (here I append them to an SVG element):
var text = svg.selectAll('text')
.data(force.nodes())
.enter().append('text')
.text(function (d) { return d.name });
Here I'm assuming that the nodes[] array contains objects with a .name property that is to be displayed.
The second component is to translate the text elements to their appropriate positions, inside the tick handler:
function tick () {
text.attr('transform', function (d) {
return 'translate(' + d.x + ',' + d.y + ')';
});
// Other code for nodes and links
}
See the jsfiddle for a complete working example, including commented code that should allow you to add images at the nodes if you want to try to reproduce your sample image more closely.

Related

d3.js Set d3-tip background to element colour

I've created a scatter graph using the following code as a basis:
http://bl.ocks.org/peterssonjonas/4a0e7cb8d23231243e0e
However I'd like to change the background of the tooltip either based upon the colour of the selected element, or by adding a data column related to colour (i.e. d.colour).
The code currently generates tooltip text based upon the selected element via the following lines:
var tip = d3.tip()
.attr("class", "d3-tip")
.offset([-10, 0])
.html(function(d) {
return xCat + ": " + d[xCat] + "<br>" + yCat + ": " + d[yCat];
});
I was hoping that by adding something like:
.style("background", function(d) { return d.colour; })
I'd be able to achieve this. However when I do this I find that d is undefined (by adding a console.log before returning).
I'm a super novice when it comes to this kind of thing, so any advice anyone could give me would be super helpful.
Thanks!
Here is a positive criticism: don't use d3-tip or any other plugin to create your tooltips. Create them yourself. That way, you can have better control over them and customise them the way you want.
Back to the question: without even looking at that plugin's documentation, you can select the element (in this case, a <div>) by class:
d3.select(".d3-tip").style("background-color", color(d[colorCat]));
Here is the updated bl.ocks: http://bl.ocks.org/GerardoFurtado/70f2608e455b61514cc96dff6fe41ea6/65c940cb987ae1cbda5dc352cda54a382a945ae8
Regarding the undefined: the tip.style is not receiving the datum when the event is fired, apparently only tip.html does. To be sure about that you have to check their source code.

D3 - reordering elements based on their data

I have made a compound bar chart representing footballers within football teams. The chart is here: http://andybarefoot.com/football/path.html
I used d3 and built the page to work in two stages. Firstly I load the data and create a rectangle for each player. I then update the parameters of the rectangles based on the data assigned to each element depending on which view is chosen. This means that the different navigation options resize and rearrange the rectangles based on existing data mapped to the elements but no additional data is loaded in.
Whilst the resizing of the rectangles works correctly I am unable to reorder the rectangles based on the data.
The vertical position of each rectangle is set simply by "i" multiplied by a set spacing variable. To change the order I thought I could selectAll all elements, sort based on the relevant data, and then set the new vertical position in the same way. (i.e. the value of "i" would have changed). However I can't get this to work.
Here is my (unsuccessful) attempt:
// select all elements and then reorder
svg
.selectAll(".team")
.sort(function(a, b) {
return b.totalClubContractDistance - a.totalClubContractDistance;
})
;
// select all elements and reposition according to new order
svg
.selectAll(".team")
.duration(750)
.attr("transform", function(d,i) {
return "translate(0,"+teamSpacing*i+")";
})
;
In d3 there are 4 core concepts. Join, Update, Enter, Exit. You can read more here: https://bost.ocks.org/mike/join/
Basically, every time you want to update the position of an element, you should change the data, then do a join followed by an update.
So the code would look like this:
function render (data) {
// join
// this joins the new data to the existing data
var teams = svg.selectAll('.team')
.data(data);
// update
// this will update existing teams that have a different location
teams.attr('transform', function (d, i) {
return 'translate(0, ' + teamSpacing * i + ')';
});
// enter
// this will add new teams that were added to the data set
teams.enter()
.attr('transform', function (d, i) {
return 'translate(0, ' + teamSpacing * i + ')';
});
// exit
// this will remove all the teams that are no longer part of the data set
teams.exit()
.remove();
}
Hope this helps

d3 checking for types and setting a type accordingly

I am working with a scatterplot in d3 and dots on the graph represent different papers. Papers on the graph can have different types ie. In_library, cites, cited_by- and each type is given a different color. On click of a paper with type In_library papers that the clicked paper cites and papers that cites the clicked paper are retrieved from the database and displayed on the graph as different colours depending on its type.
The issue I am having is that for example, there are initially 4 papers on display on the graph and they are all of type In_library. When I click on one of these, papers linked to it will appear. But what I want is if a paper cites or is cited by a paper that is ALREADY of type In_library (is already on the graph), I want to recognise it as In_library and change the type to lets say
type "combo", instead of changing it to type "cites" or "cited by", so that visually I still know that it is in the library but it is also a paper linked to the selected paper. This is the relevent code I have so far to check if the retrieved paper is of type "In_library" and if so, change the type to "combo", else give it the type "cites" or "cited by" (depending on which connection it is in).
function clickHandler (d, i) {
d3.json("connection2.php?paperID="+d.ID, function(error, dataJson) {
dataJson.forEach(function(d) {
d.YEAR = +d.YEAR;
d.counter = +d.counter;
if (d.type === "In_library") {
d.type = "combo";
}
else d.type = "cited by";
allData.push(d);
}
})
}
Any help is appreciated I am new to d3, but also if what I am trying to do isnt feasible but if theres another way of achieving this I would appreciate that also. Thanks in advance!
Sounds to me like you are trying too hard here and not leveraging d3 capabilities. You have a bunch of uniquely identified circles; when you draw your plot, you want those that are new to the plot to be a certain color. When updating, you want those that were already there to be a new color. This sounds tailor made for the the enter/update/exit pattern.
function update(data){
// this is our overall update selection
var sel = svg.selectAll('circle')
.data(data, function(d,i){
return d.id;
});
// those that are updated
// mark with combo class
sel.attr("class", "combo");
// these are the newly arriving guys
sel.enter()
.append("circle")
.attr('cx', function(d){
return s(d.x);
})
.attr('cy', function(d){
return s(d.y);
})
.attr('r', 25)
.attr("class", "in_library");
}
Here's a quick example.

d3.js Family Tree Spouse Highlight

Code link: http://jsfiddle.net/mj58659094/yKUsQ/;
When clicked on a person (node), it selects the spouse also. I only want to select (highlight) the person I clicked on (husband or wife or child). (When I inspect the html in FireBug, spouse nodes (g transform="translate(0,70)") are inside the person nodes. I think they should be outside, but within g class="node" group). I don't know how to fix this. Anyone, please help. Thanks.
Updated: Edit below
I think you are right in that the best way to solve your onclick problem is to keep a person's spouses in the same group as the person (instead of in a nested group). In addition to that, I think you are applying the onclick in the wrong place. My advice is to
Change lines 325-330 to the following
var node = g.append("svg:g").attr("class", "node")
.selectAll("g")
.data(function (d) { return d.nodes; })
.enter().append("svg:g");
node.append("svg:rect")
.on("click", clickedNode);
Currently, you were applying the onclick to the group containing both the person and his/her spouses, while instead you want to use the onclick on each person/spouse separately. Note that once you make this change, you will need to update your code to test each node's rect (instead of the node's group g) as to whether it is selected (lines 355-365). You will also have to update your CSS to style .node rect.normal and .node rect.selected on lines 25 and 29 of your CSS file.
The second issue is that you are only drawing the first spouse for each person. Currently the updateSpouses function is iterating over each person, and then adding a group with a rectangle for just the first spouse. What you need to do is first add a group for each person with spouses, and then over each person's spouses. Here's a rough draft of how to modify updateSpouses to get you started:
var node = svgTree.selectAll(".node g")
.append("svg:g").attr("transform", function (d, i) {
if (i == d3.familyTree.rootGeneration)
return "translate(" + (d.spouseX) + "," + (d3.familyTree.gapBetweenSpouses) + ")";
else
return "translate(" + 0 + "," + (d3.familyTree.gapBetweenSpouses) + ")";
})
.filter(function (d, i) { return personSpouses" in d });
var spouses = node.selectAll(".spouse")
.data(function(d){ return d.personSpouses }).enter()
.append("rect")
.on("click", clickedNode);
Edit
In response to your comment, I went ahead and modified your original code http://jsfiddle.net/mdml/xJBXm/. Here's a quick summary of the changes I made:
I put each person in a group, which then has its own onclick attribute (lines 141-146), so that when you click on the rectangle/text clickedNode is called.
I also put each set of spouses in a group (as described above) so that each of them are individually clickable as well (lines 229-232).
I modified the resetNodePersonSelected and setNodePersonSelected functions so that they search/update spouses in addition to children.
I've described the changes in high-level above but let me know if you have any more questions on the implementation.

Update multi-line graph D3.js

I'm attempting to update a d3 multi-line graph by pulling data at 5 second intervals from a mySQL database using PHP. At the moment the graph is displaying but not updating - I know that there is something not right with my updateData function but have tried everything can think of and nothing seems to work. Can anyone please help?
https://gist.github.com/Majella/ab32fe0151fd487da3f6
UPDATE:
As you can see the x-axis line is only showing sporadically and some of the lines aren't lined up with the y-axis.
Updated gist:
https://gist.github.com/Majella/ab32fe0151fd487da3f6
UPDATE 2: For some bizarre reason the lines are changing colour - or moving completely not exactly sure. So while on graph above the lines are from top - blue, orange then white - when graph updating the blue might move to bottom with orange on top and white in middle etc - but happening randomly?
In your original drawing of the graph, you correctly use:
var parameter = svg.selectAll(".parameter")
.data(data, function(d) { return d.key; })
.enter().append("g")
.attr("class", "parameter");
which joins the data (data) to the elements (g.parameter)
During your update function, you will need to join the data again in order to perform updates, deletes, and adds of elements. The 3 little circles tutorial is an excellent place to learn more about this.
Anyway, in your update function, you may want something like this (untested):
// re-acquire joined data
var containers = svg.selectAll("g.parameter")
.data( data );
// update existing elements for this data
containers
.select( "path.line" )
.attr( "d", function(d) { return line(d.values); })

Resources