d3.js Tree Layout not expanded on creation - d3.js

I've been digging d3.js for a few days by now and so far I love it.
I got into it while searching for a tree layout since I need one for my project. I came accross this example
I've accomplished the data-fetching and the stylish aspects to fit my site.
My question, since I wasn't able to get away with it is: How can I tell the tree not to be expanded at the beggining? I want the user to go clicking on the nodes as desired, because I always find myself un-clicking at the very start just to get the "flare" node alone.
Thanks in advance, if you know how to do it you can just add the code or re-work the one in the sample, I'll then apply it to the project I'm working on.

That example toggles children open or closed with this code:
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update(d);
}
So you can initialize the tree to be closed at a particular level by changing the original dataset from children to _children on any level you wish to start out as collapsed.
For example (from flare.json):
{
"name": "flare",
"_children": [
{
"name": "analytics",
"_children": [
{
"name": "cluster",
"_children": [
{"name": "AgglomerativeCluster", "size": 3938},
{"name": "CommunityStructure", "size": 3812},
{"name": "HierarchicalCluster", "size": 6714},
{"name": "MergeEdge", "size": 743}
]
}
...
...

Related

graphQL filter array containing ALL

I am quite new to graphQL, and after searching the whole afternoon, i didn't found my answer to a relative quite simple problem.
I have two objects in my strapi backend :
"travels": [
{
"id": "1",
"title": "Bolivia: La Paz y Salar de Uyuni",
"travel_types": [
{
"name": "Culturales"
},
{
"name": "Aventura"
},
{
"name": "Ecoturismo"
}
]
},
{
"id": "2",
"title": "Europa clásica 2020",
"travel_types": [
{
"name": "Clasicas"
},
{
"name": "Culturales"
}
]
}
]
I am trying to get a filter where I search for travels containing ALL the user-selected travel_types.
I then wrote a query like that :
query($where: JSON){
travels (where:$where) {
id # Or _id if you are using MongoDB
title
travel_types {name}
}
And the parameter i try to input for testing :
{
"where":{
"travel_types.name_contains": ["Aventura"],
"travel_types.name_contains": ["Clasicas"]
}
}
This should return an empty array, because none of the travels have both Aventura and Clasicas travel-types.
But instead it returns the travel with id=2. It seems that only the second filter is taken.
I searched for a query which would be like Array.every() in javascript, but i wasn't able to find.
Does someone has an idea how to achieve this type of filtering ?
Thank you very much,

D3 Tree Graph Modified Data Model

I'm hoping to get opinions on what would be a good data model to drawing something like d3 tree graph. Tree graph won't work for me as is because I have scenarios where a child node could be linked with two parent nodes, sometimes, the parent nodes from different levels of hierarchy. I'm planning to modify d3 tree graph to use a different data model, and here's where I'd really appreciate expert opinions. Following is a simple representation of what I think the data model could be. One is hierarchical model and the other is flat. Has anyone actually meddled with d3 tree data model? Any help/opinions are greatly appreciated! Thanks in advance for the help!
var hierarchicalData = [
{
"id": "n1",
"children": [
{
"id": "n1-a",
"children":[
{
"id": "n1-a-1"
}
]
},
{
"id": "n1-b",
"children":[
{
"id": "n1-b-1"
}
]
}
]
},
{
"id": "n2",
"children": [
{
"id": "n2-a",
"children":[
{
"id": "n2-a-1"
}
]
}
]
}
];
The following is a flat representation of the exact same hierarchical model but contains "level" that represents hierarchy.
{
"n1":{
"level": 0,
"children": ["n1-a", "n1-b"],
},
"n1-a":{
"level": 1,
"children":["n1-a-1"]
},
"n1-a-1":{
"level": 2,
"children":[]
},
"n1-b":{
"level": 1,
"children":["n1-b-1"]
},
"n1-b-1":{
"level": 2,
"children":[]
},
"n2":{
"level": 0,
"children": ["n2-a"]
},
"n2-a":{
"level": 1,
"children": ["n2-a-1"]
},
"n2-a-1":{
"level": 2,
"children":[]
}
}
If a child node can have more than one parent, then it's not a tree graph by definition.
There are several UI approaches you may take if you want to have a tree but it really depends on what you're trying to accomplish.
I worked with d3 tree to present a company Org-chart.
Several companies have employees with a direct manager and secondary manager.
What we did is showing the connection only to the direct manager.
But we presented the link to the other manager on mouse-over on the employee node.
This is more of a UI solution than a data model solution and there are many other possibilities.
Another option is to do what My-heritage did with family tree. They're showing both parents of each node, but only one of them is connected to the rest of the tree presented.

How do you convert TopoJSON linestring to polygon?

I have a TopoJSON file containing the boundaries of various districts in Uttar Pradesh, India. When you load the data on a map, you see only the outlines of the districts; the districts themselves are not filled.
I believe the problem is that each district is of type GeometryCollection that has its own geometries made up of a series of LineStrings.
Instead, I want each district to be of type Polygon that just has arcs.
For example, the first object is:
{
"type": "GeometryCollection",
"geometries": [{
"type": "GeometryCollection",
"properties": {
"district_number": 1,
"district_name": "Ghaziabad"
},
"geometries": [{
"type": "LineString",
"arcs": [0]
}, {
"type": "LineString",
"arcs": [1]
}, {
"type": "LineString",
"arcs": [2]
}, {
"type": "LineString",
"arcs": [3]
}, {
"type": "LineString",
"arcs": [4]
}, {
"type": "LineString",
"arcs": [5]
}]
}
I think I want to convert it, and every other object, to:
{
"type": "Polygon",
"properties": {
"district_number": 1,
"district_name": "Ghaziabad"
},
"arcs": [[0,1,2,3,4,5]]
}
I could fix it manually, but that seems insane. Is there a better way?
Update
So I figured out how to convert the object into the result I thought I wanted, but I got some very wacky polygons. Here is my (very clunky) code. Thanks to Saeed Adel Mehraban for some guidance with this.
d3.json('map.topojson',function(error,data){ // get my json that needs to be converted
var arr = data.objects.collection.geometries; // this is the relevant array
var newArr = []; // in order to map each object, i need to put each one into a new array as a single-item array
arr.forEach(function(d,i){
var curr = [d];
newArr.push(curr);
})
newArr.forEach(function(e,i){ // now that i have my new array, i want to convert each object that contains a LineString into a Polygon
var output = e.map(function(d){
var arcsArr = []; // an empty array to push each single value of the LineString arcs into
return {
"type": "Polygon", // change the type to polygon
"properties": d.properties, // keep the properties
"arcs": d.geometries.map(function(g) { // a single key-value pair for arcs, made up of the individual arcs from the LineString
arcsArr.push(g.arcs[0]);
return [arcsArr]; // the array of arcs must be in another array
})
};
});
var output = output[0]; // get only the first item in the output array, which is the object i have modified
output.arcs = output.arcs[0]; // and change the arcs so we're only taking the first array (as we've duplicated the arrays)
$('body').append(JSON.stringify(output)+','); // append the whole thing to the body so I can copy it and paste it into the appropriate part of the JSON
});
});
This "worked" in the sense that my LineStrings were indeed converted to Polygons, retaining the original border. But the polygons themselves are a nightmare, with straight lines crisscrossing the map at all kinds of angles.
Is there something like a command line tool that can convert boundaries made of LineStrings into Polygons?
I believe I ran into the same problem being described.
This is Zambia drawn as a svg polyline foreach arc (red being the first arc listed, and magenta being the last):
However when attempting to create a polygon by concatenating the arcs:
What happened was the arcs for every object were listed clockwise, but the points in every individual arc were listed counterclockwise. Without seeing the topojson that OP is using I cannot 100% confirm this, but I suspect that this was the case.
I solved this by reversing the points in an arc before pushing them to the array of points to draw the polygon and now all is well:
Maybe a map function like below? (I write that with simplistic assumption about data schema. I can't guarantee that it works for complex linestrings since I'm not familiar with topojson format. But it works with your provided data)
var foo = [
{
"type": "GeometryCollection",
"geometries": [{
"type": "GeometryCollection",
"properties": {
"district_number": 1,
"district_name": "Ghaziabad"
},
"geometries": [{
"type": "LineString",
"arcs": [0]
}, {
"type": "LineString",
"arcs": [1]
}, {
"type": "LineString",
"arcs": [2]
}, {
"type": "LineString",
"arcs": [3]
}, {
"type": "LineString",
"arcs": [4]
}, {
"type": "LineString",
"arcs": [5]
}]
}]
}
];
var bar = foo.map(function(d) {
return {
"type": "Polygon",
"properties": d.geometries[0].properties,
"arc": d.geometries.map(function(g1) {
return g1.geometries.map(function(g) {
return g.arcs[0];
});
})
};
});
console.log(bar);

Collapsible/hierarchical AND force-directed graph in d3.js

There are numerous examples of force-directed graphs (i.e. nodes and links) and collapsible trees (i.e. parent-child nodes) but I cant find an example of the combination of these - other than some 1-level clustered networks like this - http://static.cybercommons.org/js/d3/examples/force/force-cluster.html.
That is I need a full hierarchy of nodes (with any number of levels) with links between various nodes across the hierarchy.
Has anyone got an example of this?
And if so I'd ultimately like to see the hierarchies be collapsible and any of the links from the children are 'elevated' up to the parent when it is collapsed.
Cheers,
Tim
This is similar to what I'd expect the jsonData to look like ...
{
"nodes": [
{
"name": "Parent 1",
"children": [
{
"name": "Child 1",
},
},
{
"name": "Parent 2",
"children": [
{
"name": "Child 2",
},
.
.
.
"links": [
{
source: "Child 1",
target: "Child 2"
},
.
.
i try to merge both examples here my fiddle
// Toggle children on click.
function click(d) {
if (d.children) {
d._children = d.children;
d.children = null;
} else {
d.children = d._children;
d._children = null;
}
update();
}
Here is a nice example that exhibits both properties http://bl.ocks.org/mbostock/1093130
I'm also interested in this.
I have found two examples, that I'd like to combine.
http://bl.ocks.org/mbostock/1062288
http://graus.nu/d3/

Hyperlinks in d3.js objects

I am a complete novice at d3.js or java in general. I am using the indented tree example from http://bl.ocks.org/1093025. It took me two hours to get this to work on my local computer, so that should give you an idea of my skill level.
I opened the flare.json file and started messing with it and was able to manipulate it successfully. It looks like this
{
"name": "Test D3",
"children": [
{
"name": "News",
"children": [
{
"name": "CNN",
"size": 1000
},
{
"name": "BBC",
"size": 3812
}
]
},
{
"name": "Blogs",
"children": [
{
"name": "Engaget",
"size": 3938
}
]
},
{
"name": "Search",
"children": [
{
"name": "Google",
"size": 3938
},
{
"name": "Bing",
"size": 3938
}
]
}
]
}
What I want to do now, is to try to add hyperlinks. For example, I want to be able to click on "CNN" and go to CNN.com. Is there a modification I can make to flare.json that will do that?
It is quite easy, just add some more "key" : "value" pairs. Example:
"children": [
{
"name": "Google",
"size": 3938,
"url": "https://www.google.com"
},
{
"name": "Bing",
"size": 3938,
"url": "http://www.bing.com"
}
]
Of course, in your d3 code you then need to append <svg:a> tags and set their xlink:href attribute.
Here is some html and d3-code that might be of help to you. First you need to import the xlink namespace in your html file:
<html xmlns:xlink="http://www.w3.org/1999/xlink">
...
</html>
Then in the d3 drawing code where you append nodes for each data element you wrap the element you want to be clickable links with an svg:a tag:
nodeEnter.append("svg:a")
.attr("xlink:href", function(d){return d.url;}) // <-- reading the new "url" property
.append("svg:rect")
.attr("y", -barHeight / 2)
.attr("height", barHeight)
.attr("width", barWidth)
.style("fill", color)
.on("click", click); // <- remove this if you like
You might want to remove the click handler (which is present in the original example) by deleting the .on("click", click) as it might interfere with the default behavior of SVG links.
Clicking on your rects should now lead you to the appropriate url.
SVG links might not be fully implemented in all browsers.
Alternatively you could modify the click handler to read the URL from d.url and use that one to manually redirect the browser to that URL via JavaScript: window.location = d.url;. Then you do not need the svg:a tag and the xlink code. Though adding a real link (not a scripted one) has the benefit that the user/browser can decide what to do (e.g., open in new tab/page). It also helps if some of your users have JavaScript disabled.

Resources