I previously asked a similar question before, however it seemed that I wasn't being precise enough about my question - hopefully this is.
Many examples of the Tree Layout format their hierarchy data set as this: https://gist.github.com/tchaymore/1249394#file-math_map_compact-json
However I wanted to change the "children" node in the json to "children1" for example
var flare = {
"Name": "Example",
"children1": [
{
"BName":"Ja",
"Email":"",
"children1":[
{
"NickName":"NC",
"id":2
}
],
}
]};
When I had changed the name of that node, I then used the children function:
var tree = d3.layout.tree()
.size([height, width])
.children(function(d) { return d.children1; });
After that I changed all of the "children" references to "children1" & so on.
However, now I have the problem of not being able to collapse the tree back into its parent. I've tried to use this example as a reference, but no luck: http://blog.pixelingene.com/2011/07/building-a-tree-diagram-in-d3-js/
Here's a example of my current problem: http://jsfiddle.net/mEyQW/1/
.. I apologize for the previous confusion
I don't really get what you wanted to do with your collapse() function and the _children1 but if you remove it, then everything works fine:
jsFiddle: http://jsfiddle.net/chrisJamesC/mEyQW/2/
Related
I have created a tree control using kendo TreeView.it has more than 10,000 nodes and i have used loadOnDemand false when creating Tree.
I am providing a feature to expand the tree by its level, for this i have created a method which takes the parameter "level" as number and expand it accordingly and user can enter 15 (max level) into the method, it works fine with 500 to 600 nodes for all the levels but when tree has more than 5000 nodes than if user is trying to expand above the 2nd level nodes then browser hangs and shows not responding error.
Method which i have created to expand the tree is :-
function ExapandByLevel(level, currentLevel) {
if (!currentLevel) {
currentLevel = 0;
}
if (level != currentLevel) {
var collapsedItems = $("#treeView").find(".k-plus:visible");
if (collapsedItems.length > 0) {
setTimeout(function () {
currentLevel++;
var $tree = $("#treeView");
var treeView = $tree.data("kendoTreeView");
var collapsedItemsLength = collapsedItems.length;
for (var i = 0; i < collapsedItemsLength; i++) {
treeView.expand($(collapsedItems[i]).closest(".k-item"));
}
ExapandByLevel(level, currentLevel);
}, 100);
}
else {
//console.timeEnd("ExapandByLevel");
hideLoading();
}
}
if (level == currentLevel) {
hideLoading();
}
}
call above given method like this:-
ExapandByLevel(15);
here 15 is level to expand in tree.
when tree has more than 5000 nodes than if user is trying to expand above the 2nd level nodes then browser hangs and shows not responding error.
please suggest any way to do this,what i want is expand the tree which can contains more than 5000 nodes.
I had a similar problem with kendo TreeView, when I wanted to load a tree with 30,000 nodes. The browser would freeze for a long time to load this number of nodes even when loadOnDemand was set to true.
So we decided to implement the server-side functionality for expanding nodes, and that's what you should do. You need to have 2 changes in your existing code.
Change your tree use server side Expand method.
When you call expand, you should make sure the node is expanded.
These two steps will be explained below. The thing you should know is, this way your browser doesn't hang at all, but it may take some time to complete the operation, because there will be so many webservice calls to the server.
Change your tree to use server side Expand method:
Please see Kendo UI's demos for Binding to Remote Data in this
link. Note that loadOnDemand should be set to true. In addition the server side Expand web service should be implemented too.
When you call expand, you should make sure the node is expanded:
In order to do this, there should be an event like Expanded defined in Kendo UI TreeView, but unfortunately there is none, except Expanding event. Using setTimeout in this case is not reliable, because the network is not reliable. So we ended up using a while statement to check that the node's children are created or not. There might be a better solution for this, however this satisfies our current requirement. Here's the change you should make when expanding nodes:
if (collapsedItems.length > 0) {
currentLevel++;
var $tree = $("#treeView");
var treeView = $tree.data("kendoTreeView");
var collapsedItemsLength = collapsedItems.length;
for (var i = 0; i < collapsedItemsLength; i++) {
var node = $(collapsedItems[i]).closest(".k-item")
if (!node.hasChildren)
continue; // do not expand if the node does not have children
treeView.expand(node);
// wait until the node is expanded
while (!node.Children || node.Children.length == 0);
}
ExapandByLevel(level, currentLevel);
}
You can also do the expand calls in a parallel way in order to decrease the loading time, but then you should change the way you check if all the nodes are expanded or not. I just wrote a sample code here that should work fine.
Hope this helps.
The solution to your problem is pretty simple: Update the version of Kendo UI that you are using since they have optimized (a loooooooooot) the code for HierarchicalDataSource and for TreeView.
Check this: http://jsfiddle.net/OnaBai/GHdwR/135/
This is your code where I've change the version of kendoui.all.min.js to v2014.1.318. I didn't even changed the CSS (despite you should). You will see that opening those 5000 nodes is pretty fast.
Nevertheless, if you go to 10000 elements you will very likely consider it slow but sorry for challenging you: do you really think that 10000 nodes tree is User Friendly? Is a Tree the correct way of presenting such a huge amount of data?
what I'm trying to do is d3.nest() one csv into another.
The node list looks like this:
id,movie,year,genre
85442,Hamlet,1907,Drama
57421,Don Quijote,1908,Drama
13146,Amleto [I],1908,Drama
85443,Hamlet,1908,Drama
160468,Othello,1909,Drama
160468,Othello,1909,Romance
And the edges or links look like this:
sourceid,targetid
234,99455
234,125817
234,201681
476,72885
476,188536
476,246634
1028,14948
1028,60050
So I would like to nest the edges into the node list by the given IDs.
I'm doing some unclever nesting that doesn't work that looks like this right now:
d3.csv("edges.csv", function(edges){
d3.csv("nodes.csv", function(nodes){
var nested_data = d3.nest()
.key(function(d) { return d.sourceid; })
.key(function(d) { return d.id; })
.entries(edges)
.entries(nodes);
console.debug(nested_data);
});
});
I searched for help and found this: D3 change elements based on two different datasets?
But I can't get my head around it. Is it really so difficult? Or might there be another way of doing this? I found this visualization (http://mbostock.github.io/d3/talk/20111116/airports.html) which also deals with two csv files and network structures without using nest().
Thanks,
Kim
Because many people looked at this question, I thought of writing a short follow-up on how this can be solved, to my understanding now.
Lars already gave the right hints. So here just a coded version.
d3.csv("edges.csv", function(edges){
d3.csv("nodes.csv", function(nodes){
var newlist = [];
var i = edges.length;
while(i--){
var edge = edges[i];
newlist.push(
nodes[edge.sourceid].x),
nodes[edge.targetid].y)
);
}
var nested_data = d3.nest()
.key(function(d) { return ... })
.entries(newlist);
});
});
It's not fitting the question perfectly because it assumes that the nodes list is ordered by the ids. The two lists above could be itterated over with something like http://underscorejs.org/#find
Hope this helps.
I am basically worse then a noob on Raphael. I was wondering how to set the path ID for the following code. I did some googling but couldn't find any way to set ID to the following code, since it uses an array.
var mySVG=['exampleDOMid',400,400,
{
"type":"path","fill":"#C4C5C7","path":"path code here","stroke":"none"
},{
"type":"path","fill":"#EDEDEE","path":"path code here","stroke":"none"
},{
"type":"path","fill":"#5A5B5D","path":"path code here","stroke":"none"
},{
"type":"path","fill":"#231F20","path":"path code here","stroke":"none"
}];
var r = Raphael(mySVG);
I generated the code from http://toki-woki.net/p/SVG2RaphaelJS/ by submitting a SVG file
To elaborate on my comment, it depends how you want to use that id.
1. If you need the ID just for some precedence of events, then just use the position in the array
2. To have something specific, just add this to your path:
path.data("id", id);
Then you can retrieve the id by doing the following:
var index = path.data("id");
Also look into Raphael documentation for data() function for more details.
I've searched through the myriad parent/child array/object/whatever questions here and elsewhere and haven't been able to solve my issue. I know this is a bit long, but I wanted to ensure I'm providing enough info to you guys.
Here's what I want to do:
I have a number of <div>-delineated items on the page with parent/child relationships, generated via php from my database
I want to use these items as the data source for a D3.js Dendrogram (a node-link tree diagram http://mbostock.github.com/d3/ex/cluster.html)
I'm storing them with left/right nested set values but also parentID values, so I can add ID, parentID, rgt, lft and depth attributes to the <div> elements, so I should have available whatever's needed to generate the parent/child relationships on the client side
For various reasons, instead of creating a JSON file on the server side to use as the data source, I need to create it on the client side based on the attributes in #3
I've had difficulty getting various suggested javascript functions to work and all the D3 examples I've found use either a preexisting JSON file or generated math-based file, not attributes of elements already on the page
Here is an example of what already works for me with the D3 Dendrogram, but it's not generated dynamically:
var tree3 =
{"sid": "1", "children": [
{"sid": "2", "children": [
{"sid": "5", "children": [
{"sid": "75"},
{"sid": "85", "children": [
{"sid": "87"}, ...
To give you an idea of where these attributes are in the DOM, I originally tried the below, but of course it doesn't generate any hierarchy:
function tree() {
var tree=[];
$("article").each(function(){
tree.push({
sid:$(this).attr("sid"),
l:$(this).attr("l"),
r:$(this).attr("r"),
pid:$(this).attr("pid")
});
});
return tree;
}
I've been messing around unsuccessfully with variants of the below to get a nested array:
function tree2() {
$("article").(function(d) {
return d.parent().attr("pid") === 0;
}, function(parent, child) {
return parent.attr("pid") === child.parent().attr("sid");
}).toArray();
}
So, I'm driving myself crazy trying to create the javascript array nested correctly, but it's dawned on me that I may not need to and that D3's data selectors and methods could be sufficient. Could you please help me with the code to:
Pull the needed attributes to generate the parent/child relationship within a D3 function ("sid" is the identifier) or, if this isn't possible,
Create the needed array or array-like object in javascript for use by D3 (still with "sid" as the identifier).
Thanks in advance.
You need to get recursive! Basically the trick is to pass the current parent in as you go, which changes the context and allows you to walk down the tree.
Update: Working fiddle.
Assuming your HTML structure is something like this:
<div sid="1" pid="">
<div sid="1.1" pid="1">
<div sid="1.1.1" pid="1.1">
</div>
</div>
</div>
You could do something like this:
var _json = {};
function addTreeNode(div, parentObj) {
var childObj = {
sid: $(div).attr("sid"),
pid: $(div).attr("pid")
}
// add this to it's parent in the JSON hierarchy
if (!parentObj.children) parentObj.children = [];
parentObj.children.push(childObj);
// keep adding for all children div's
$(div).find("div").each(function() {
addTreeNode(this, childObj);
});
}
// start at the roots, it will magically work it's way out to the leaves
$("body > div").each(function(){
addTreeNode(this, _json);
});
console.log(_json);
Note that if your tree is big enough, you will cause stack overflows, especially in IE. In that case, you'll need to switch this over from recursion to iteration. It's not as pretty that way, though.
My company is building a graph-view editor for chatbots. We are using Cytoscape along with the cytoscape-cola extension to accomplish this. One of the issues we are facing is dynamically adding new nodes to the graph without them overlapping with existing nodes on the graph.
I have looked through previous similar questions(listed below) but to no avail:
Cytoscape - handle locked nodes
I tried the solution in there i.e. applying the layout only on the newly added nodes but they keep getting stacked in the centre of the screen.
Cytoscape - ignore locked nodes
I tried the solution mentioned in here but regardless of locking the nodes in one go i.e. cy.nodes().lock() or individually i.e. cy.nodes().forEach(node => node.lock()), the locked nodes still keep moving. Also interesting thing to note here is that when locking nodes individually, the newly added node(s) are also locked regardless of whether or not I am locking in the call above or not.
Cytoscape - dynamically add nodes without moving others
I tried this solution as well but the locked nodes still move and the complete layout is changed - sometimes altogether, sometimes just a little.
Code
This is currently what I am using to construct the graph:
const layoutConfig = {
name: "cola",
handleDisconnected: true,
animate: true,
avoidOverlap: true,
infinite: false,
unconstrIter: 1,
userConstIter: 0,
allConstIter: 1,
ready: e => {
e.cy.fit()
e.cy.center()
}
}
this.graph = Cytoscape({ ... })
this.layout = this.grapg.makeLayout(layoutConfig)
this.layout.run();
This is what I am using to add new node(s) to the graph:
const addElements = (elements: ElementSingular | ElementMultiple) => {
this.graph.nodes().forEach(node => {
node.lock();
})
this.graph.add(elements)
this.layout = this.graph.makeLayout(layoutConfig)
this.layout.on("layoutready", () => {
this.nodes().forEach(node => {
node.unlock();
})
})
this.layout.run()
this.graph.nodes().forEach(node => {
node.unlock();
})
}
I'd like to do one of the following:
Understand what I am doing wrong if what I am trying to accomplish is possible but my code doesn't achieve it
Understand why I would not be able to accomplish it i.e. the limitations that govern it
Edit: Is this what you were looking for? https://output.jsbin.com/hokineluwo
Edit: I didn't saw before, but you are also unlocking the nodes right after the layout call, Cola is async, the run only kicks-off the process. Remove that code and only use the layoutstop method.
I don't remember correctly, but i think that cola keeps moving the elements after the layoutready. From their code:
// trigger layoutready when each node has had its position set at least once
There is a layoutstop event that you could use (and cytoscape-cola uses too).
From the docs (emphasis is mine):
layoutready : when a layout has set initial positions for all the
nodes (but perhaps not final positions)
layoutstop : when a layout has
finished running completely or otherwise stopped running
I would try to remove that callback and inspect if it gives good results.
Also, you should try to reproduce it here: http://jsbin.com/fiqugiq
It makes it easier for others (even the authors) to play with it and see if you have found a bug or not.