Hi is there any package there for display Tree diagram at dashboard
some thing like this
each node get : color, name, parents
thanks
Hi is there any package there for display Tree diagram at dashboard
some thing like this
each node get : color, name, parents
thanks
Answer in stackoverflow: javascript - How to recursively toggle nodes in a d3.js tree diagram - Stack Overflow
so, I got the tree as node template (will modify it's input to be dynamic later)
tree.txt (6.7 KB)
but it is out of my "map" group
how do I change it?
Alos, how can I modify the layout so I can do map under discovery,
centered and not the auto way it is done?
thakns
Modify the line that appends the SVG element to use the id of your TAB/group. For instance: tab name = D3 and group name = G1, it will be:
var svg = d3.select("#D3_G1").append("svg")
Result:
Looks like in you case it should be:
var svg = d3.select("#Layout_map").append("svg")
Love to see this when you get dynamic data added!
Very odd side issue. i put your flow on a tab and it showed up fine on the 'Map' tab. I then added a pie chart on the same tab, but pointing to another UI tab and the Tree shows up on both the 'Map' and the tab (Home in this case) the chart is assigned to. Not only that but it has depth to the lines
@Andrel any thoughts?
Hi @zenofmud, did you modify this line of code from the original code ?
var svg = d3.select("body").append("svg")
How it look like now for you ?
Paul, you need to turn off the css "fill" styling for your <path>
elements -- check the Dev Console, and you can see the inherited style that needs to be overridden.
You can see the same effect in the editor links when i turn off the fill: none;
style...
@andrel
when I change that to:
var svg = d3.select("#Layout_map").append("svg")
nothing shows
@shrickus - it looks fine on the original tab, it just when I went to the tab with the pie chart that I saw that image.
All I've dont is used the flow that @natanel provided with no changes other than putting it in a flow-tab that has a pie chart. Here is the flow(s) showing the issue:
[{"id":"8d9e58f5.85f7c","type":"ui_chart","z":"120f82c.f796cfd","name":"","group":"e22ca977.f549b","order":0,"width":0,"height":0,"label":"chart","chartType":"pie","legend":"false","xformat":"HH:mm:ss","interpolate":"linear","nodata":"","dot":false,"ymin":"","ymax":"","removeOlder":1,"removeOlderPoints":"","removeOlderUnit":"3600","cutout":0,"useOneColor":false,"colors":["#1F77B4","#AEC7E8","#FF7F0E","#2CA02C","#98DF8A","#D62728","#FF9896","#9467BD","#C5B0D5"],"useOldStyle":false,"x":550,"y":260,"wires":[[],[]]},{"id":"fa234288.e1773","type":"inject","z":"120f82c.f796cfd","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":100,"y":260,"wires":[["f6913e72.3b51c","d4bb304a.4f50f8","9aefcaac.f2028"]]},{"id":"f6913e72.3b51c","type":"change","z":"120f82c.f796cfd","name":"Series 1 - 15","rules":[{"t":"set","p":"payload","pt":"msg","to":"15","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"series 1","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":200,"wires":[["8d9e58f5.85f7c"]]},{"id":"d4bb304a.4f50f8","type":"change","z":"120f82c.f796cfd","name":"Series 2 - 10","rules":[{"t":"set","p":"payload","pt":"msg","to":"10","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"series 2","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":240,"wires":[["8d9e58f5.85f7c"]]},{"id":"9aefcaac.f2028","type":"change","z":"120f82c.f796cfd","name":"Series 3 - 12","rules":[{"t":"set","p":"payload","pt":"msg","to":"12","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"series 3","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":280,"wires":[["8d9e58f5.85f7c"]]},{"id":"e9fa2773.b23058","type":"change","z":"120f82c.f796cfd","name":"Series 4 - 13","rules":[{"t":"set","p":"payload","pt":"msg","to":"13","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"series 4","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":320,"wires":[["8d9e58f5.85f7c"]]},{"id":"434e660f.1c7248","type":"change","z":"120f82c.f796cfd","name":"Series 5 - 8","rules":[{"t":"set","p":"payload","pt":"msg","to":"8","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"series 4","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":360,"wires":[["8d9e58f5.85f7c"]]},{"id":"2c92a40.d6fb95c","type":"change","z":"120f82c.f796cfd","name":"Series 6 - 17","rules":[{"t":"set","p":"payload","pt":"msg","to":"17","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"series 8","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":400,"wires":[["8d9e58f5.85f7c"]]},{"id":"b291f4d9.9d0738","type":"change","z":"120f82c.f796cfd","name":"Series 7 - 23","rules":[{"t":"set","p":"payload","pt":"msg","to":"23","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"series 7","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":440,"wires":[["8d9e58f5.85f7c"]]},{"id":"906fafb9.5c1c78","type":"change","z":"120f82c.f796cfd","name":"Series 8 - 1","rules":[{"t":"set","p":"payload","pt":"msg","to":"1","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"series 8","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":480,"wires":[["8d9e58f5.85f7c"]]},{"id":"b71e5fbc.ddacd","type":"change","z":"120f82c.f796cfd","name":"Series9 - 1","rules":[{"t":"set","p":"payload","pt":"msg","to":"1","tot":"num"},{"t":"set","p":"topic","pt":"msg","to":"series 9","tot":"str"}],"action":"","property":"","from":"","to":"","reg":false,"x":310,"y":520,"wires":[["8d9e58f5.85f7c"]]},{"id":"5109e224.555dac","type":"ui_template","z":"120f82c.f796cfd","group":"4d52119.21ac4f","name":"map matrix","order":0,"width":"0","height":"0","format":"<style>\t\n\t.node {\n\t\tcursor: pointer;\n\t}\n\n\t.node circle {\n\t fill: #fff;\n\t stroke: steelblue;\n\t stroke-width: 3px;\n\t}\n\n\t.node text {\n\t font: 12px sans-serif;\n\t fill: #fff;\n\t}\n\n\t.link {\n\t fill: none;\n\t stroke: #ccc;\n\t stroke-width: 2px;\n\t}\n</style>\n\t\n<script>\nvar treeData = [\n {\n \"name\": \"Top Level\",\n \"parent\": \"null\",\n \"status\": \"red\",\n \"children\": [\n {\n \"name\": \"Level 2-A\",\n \"parent\": \"Top Level\",\n \"status\": \"red\",\n \"children\": [\n {\n \"name\": \"Legolas\",\n \"status\": \"red\",\n \"parent\": \"Level 2-A\"\n },\n {\n \"name\": \"Finegal\",\n \"status\": \"green\",\n \"parent\": \"Level 2-A\"\n }\n ]\n },\n {\n \"name\": \"Level 2-B\",\n \"status\": \"red\",\n \"parent\": \"Top Level\",\n \"children\": [\n {\n \"name\": \"Durin\",\n \"status\": \"red\",\n \"parent\": \"Level 2-B\",\n\t\t\t\"children\": [\n\t\t\t {\n\t\t\t\t\"name\": \"Frodo\",\n\t\t\t\t\"status\": \"green\",\n\t\t\t\t\"parent\": \"Durin\"\n\t\t\t },\n\t\t\t {\n\t\t\t\t\"name\": \"Bilbo\",\n\t\t\t\t\"status\": \"red\",\n\t\t\t\t\"parent\": \"Durin\"\n\t\t\t }\n\t\t\t] \n },\n {\n \"name\": \"Balin\",\n \"status\": \"green\",\n \"parent\": \"Level 2-B\",\n\t\t\t\"children\": [\n\t\t\t {\n\t\t\t\t\"name\": \"Merry\",\n\t\t\t\t\"status\": \"green\",\n\t\t\t\t\"parent\": \"Balin\"\n\t\t\t },\n\t\t\t {\n\t\t\t\t\"name\": \"Pipen\",\n\t\t\t\t\"status\": \"green\",\n\t\t\t\t\"parent\": \"Balin\"\n\t\t\t }\n\t\t\t] \n }\n ]\n },\n {\n \"name\": \"Level 2-C\",\n \"parent\": \"Top Level\",\n \"status\": \"green\",\n \"children\": [\n {\n \"name\": \"Gandalf\",\n \"status\": \"green\",\n \"parent\": \"Level 2-C\"\n },\n {\n \"name\": \"Sauruman\",\n \"status\": \"green\",\n \"parent\": \"Level 2-C\"\n }\n ]\n },\n ]\n }\n];\n\n\n// ************** Generate the tree diagram\t *****************\nvar margin = {top: 20, right: 120, bottom: 20, left: 120},\n\twidth = 960 - margin.right - margin.left,\n\theight = 500 - margin.top - margin.bottom;\n\t\nvar i = 0,\n\tduration = 750,\n\troot;\n\nvar tree = d3.layout.tree()\n\t.size([height, width]);\n\nvar diagonal = d3.svg.diagonal()\n\t.projection(function(d) { return [d.y, d.x]; });\n\nvar svg = d3.select(\"body\").append(\"svg\")\n\t.attr(\"width\", width + margin.right + margin.left)\n\t.attr(\"height\", height + margin.top + margin.bottom)\n .append(\"g\")\n\t.attr(\"transform\", \"translate(\" + margin.left + \",\" \n\t + margin.top + \")\");\n\nroot = treeData[0];\nroot.x0 = height / 2;\nroot.y0 = 0;\n\nfunction toggleAll(d) {\n if (d.children) {\n if (d.status == \"green\") {\n d._children = d.children;\n d._children.forEach(toggleAll);\n d.children = null;\n }\n }\n}\n\nroot.children.forEach(toggleAll);\n \nupdate(root);\n\nd3.select(self.frameElement).style(\"height\", \"500px\");\n\nfunction update(source) {\n\n // Compute the new tree layout.\n var nodes = tree.nodes(root).reverse(),\n\t links = tree.links(nodes);\n\n // Normalize for fixed-depth.\n nodes.forEach(function(d) { d.y = d.depth * 180; });\n\n // Update the nodesĂ–\n var node = svg.selectAll(\"g.node\")\n\t .data(nodes, function(d) { return d.id || (d.id = ++i); });\n\n // Enter any new nodes at the parent's previous position.\n var nodeEnter = node.enter().append(\"g\")\n\t .attr(\"class\", \"node\")\n\t .attr(\"transform\", function(d) { \n\t\t return \"translate(\" + source.y0 + \",\" + source.x0 + \")\"; })\n\t .on(\"click\", click);\n\n nodeEnter.append(\"circle\")\n\t .attr(\"r\", 1e-6)\n\t .style(\"fill\", function(d) { return d.status; });\n\n nodeEnter.append(\"text\")\n\t .attr(\"x\", function(d) { \n\t\t return d.children || d._children ? -13 : 13; })\n\t .attr(\"dy\", \".35em\")\n\t .attr(\"text-anchor\", function(d) { \n\t\t return d.children || d._children ? \"end\" : \"start\"; })\n\t .text(function(d) { return d.name; })\n\t .style(\"fill-opacity\", 1e-6);\n\n // Transition nodes to their new position.\n var nodeUpdate = node.transition()\n\t .duration(duration)\n\t .attr(\"transform\", function(d) { \n\t\t return \"translate(\" + d.y + \",\" + d.x + \")\"; });\n\n nodeUpdate.select(\"circle\")\n\t .attr(\"r\", 10)\n\t .style(\"fill\", function(d) { return d.status; });\n\n nodeUpdate.select(\"text\")\n\t .style(\"fill-opacity\", 1);\n\n // Transition exiting nodes to the parent's new position.\n var nodeExit = node.exit().transition()\n\t .duration(duration)\n\t .attr(\"transform\", function(d) { \n\t\t return \"translate(\" + source.y + \",\" + source.x + \")\"; })\n\t .remove();\n\n nodeExit.select(\"circle\")\n\t .attr(\"r\", 1e-6);\n\n nodeExit.select(\"text\")\n\t .style(\"fill-opacity\", 1e-6);\n\n // Update the linksĂ–\n var link = svg.selectAll(\"path.link\")\n\t .data(links, function(d) { return d.target.id; });\n\n // Enter any new links at the parent's previous position.\n link.enter().insert(\"path\", \"g\")\n\t .attr(\"class\", \"link\")\n\t .attr(\"d\", function(d) {\n\t\tvar o = {x: source.x0, y: source.y0};\n\t\treturn diagonal({source: o, target: o});\n\t });\n\n // Transition links to their new position.\n link.transition()\n\t .duration(duration)\n\t .attr(\"d\", diagonal);\n\n // Transition exiting nodes to the parent's new position.\n link.exit().transition()\n\t .duration(duration)\n\t .attr(\"d\", function(d) {\n\t\tvar o = {x: source.x, y: source.y};\n\t\treturn diagonal({source: o, target: o});\n\t })\n\t .remove();\n\n // Stash the old positions for transition.\n nodes.forEach(function(d) {\n\td.x0 = d.x;\n\td.y0 = d.y;\n });\n}\n\n// Toggle children on click.\nfunction click(d) {\n console.log(d);\n if (d.children) {\n\td._children = d.children;\n\td.children = null;\n } else {\n\td.children = d._children;\n\td._children = null;\n }\n update(d);\n}\n</script>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":430,"y":80,"wires":[[]]},{"id":"e22ca977.f549b","type":"ui_group","z":"","name":"Josh plants gauges","tab":"2a9bbfc9.fe1488","order":1,"disp":true,"width":"12","collapse":false},{"id":"4d52119.21ac4f","type":"ui_group","z":"","name":"map","tab":"a95a2ef.0cc6a5","order":2,"disp":true,"width":"6","collapse":false},{"id":"2a9bbfc9.fe1488","type":"ui_tab","z":"","name":"Home","icon":"dashboard","order":1},{"id":"a95a2ef.0cc6a5","type":"ui_tab","z":"","name":"Map","icon":"dashboard","order":2}]
Import it and deploy then go to the UI and look at the MAPS tab - the 'tree' will be there and look fine. Now go to the 'Home' tab and you will see a pie chart and the tree with the expanded curve/lines.
Some more weirdness.My ui has three tabs Home, Maps and Hal. Wen I first of in this is what Home looks like:
Next I go to the Maps tab and it looks like this:
Me thinks that something is bumping into something else...
Hi zenofmud
I did the send data to map
as being new , this is probably some syntax error
can you take a look and suggest
tree.txt (9.9 KB)
Hi Paul,
Your code needs to be patched to reflect the tab and group name you have in place.
Instead of
var svg = d3.select("body").append("svg")
Use line below:
var svg = d3.select("#Map_map").append("svg")
Ahhh, much better!! I see now, the "body" caused it to be displayed on any tab! I will file this away for future reference!
This is not the issue that is preventing you from displaying the tree in the dashboard but anyway it is better to change your code.
In some places you declare and use a variable named "node". It happens that "node" is already a declared object in Node-RED so better to not use it.
// arrange tree
while( treeData[treeData.length-1].zone !== 0 )
{
var node = treeData.pop();
// find and add as children to parent
var i = FindParent(treeData,node);
if( i != -1)
treeData[i].children.push(node);
}
Apparently your function node is generating a valid data structure (msg.payload.treeData).
However, I was not able to use it inside the ui_template node. I need to revise how to use the msg object properties inside the ui_template.
When using a variable declared directly inside the ui_template it works perfectly.
Flow:
[{"id":"a9a2ce4f.e328f","type":"tab","label":"Flow 9","disabled":false,"info":""},{"id":"d211e6ca.04c818","type":"function","z":"a9a2ce4f.e328f","name":"prepare map data","func":"// data to read from cordinator eeprom\nvar numBonded = 4;\nvar numDiscovered = 3;\n//\t\t\t\t 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\nvar nodesBonded = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0 ];\nvar nodesDiscovered = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0 ];\nvar vrnArray = \t\t [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 2, 0, 0 ];\nvar nodesZone = [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0 ];\nvar nodesParent = \t [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0 ];\n\nvar treeData = [\n\t{\n\t\t\"name\": \"Cordinator\",\n\t\t\"parent\": \"null\",\n\t\t\"status\": \"orange\",\n\t\t\"zone\": 0,\n\t\t\"children\": []\n\t}\n];\n\nvar index = 0;\nwhile( numBonded )\n{\n index++;\n\tif( nodesBonded[index] == 1)\n\t{\n\t\tvar parent = \"\";\n\t\tvar status = \"\";\n\t\t\n\t\t// orphan\n\t\tif( nodesDiscovered[index] === 0 )\n\t\t{\n\t\t\tparent = \"null\";\n\t\t\tstatus = \"red\";\n\t\t}\n\t\t// first children\n\t\telse if( nodesParent[index] === 0 )\n\t\t{\n\t\t\tparent = \"Cordinator\";\n\t\t\tstatus = \"green\";\n\t\t}\n\t\t// other children\n\t\telse\n\t\t{\n\t\t\tparent = \"ID \" + vrnArray.indexOf(nodesParent[index]).toString() + \", VRN \" + nodesParent[index].toString();\n\t\t\tstatus = \"green\";\n\t\t}\n\t\t\n\t\ttreeData.push( \n\t\t{\n\t\t\t\"name\": \"ID \" + index.toString() + \", VRN \" + vrnArray[index].toString(),\n\t\t\t\"parent\": parent,\n\t\t\t\"status\": status,\n\t\t\t\"zone\": nodesZone[index],\n\t\t\t\"children\": []\n\t\t} );\n\t\tnumBonded--;\n\t}\n}\n\n// sort array by zone\ntreeData.sort(function(a,b) {return (a.zone > b.zone) ? 1 : ((b.zone > a.zone) ? -1 : 0);} );\n\n// arrange tree\nwhile( treeData[treeData.length-1].zone !== 0 )\n{\n var node = treeData.pop();\n \n // find and add as children to parent\n var i = FindParent(treeData,node);\n\n if( i != -1)\n treeData[i].children.push(node);\n}\n\nglobal.set(\"treeData\", treeData);\nmsg.payload={\"treeData\": treeData };\n\nreturn msg;\n\nfunction FindParent(treeData, node) {\n for( var i = treeData.length-1 ; i >= 0 ; i--)\n {\n var n = node.parent.localeCompare(treeData[i].name);\n if((treeData[i].zone === (node.zone)-1) && (n===0))\n return i;\n }\n return -1;\n}","outputs":1,"noerr":0,"x":330,"y":220,"wires":[["2c76fb3c.525134","bdcefd26.67fc7"]]},{"id":"4e9c142a.63189c","type":"inject","z":"a9a2ce4f.e328f","name":"","topic":"","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":140,"y":220,"wires":[["d211e6ca.04c818"]]},{"id":"7b976e82.e2bb7","type":"debug","z":"a9a2ce4f.e328f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":690,"y":220,"wires":[]},{"id":"2c76fb3c.525134","type":"debug","z":"a9a2ce4f.e328f","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":530,"y":160,"wires":[]},{"id":"bdcefd26.67fc7","type":"ui_template","z":"a9a2ce4f.e328f","group":"6a8123c4.16a83c","name":"","order":0,"width":"0","height":"0","format":"<!DOCTYPE html>\n\n<html>\n\n<head>\n<style>\t\n\t.node {\n\t\tcursor: pointer;\n\t}\n\n\t.node circle {\n\t fill: #fff;\n\t stroke: steelblue;\n\t stroke-width: 3px;\n\t}\n\n\t.node text {\n\t font: 12px sans-serif;\n\t fill: #fff;\n\t}\n\n\t.link {\n\t fill: none;\n\t stroke: #ccc;\n\t stroke-width: 2px;\n\t}\n</style>\n</head>\n\n<body>\n\n\n<script src=\"//d3js.org/d3.v3.js\"></script>\t\n<script>\nvar treeData = [\n {\n \"name\": \"Top Level\",\n \"parent\": \"null\",\n \"status\": \"red\",\n \"children\": [\n {\n \"name\": \"Level 2-A\",\n \"parent\": \"Top Level\",\n \"status\": \"red\",\n \"children\": [\n {\n \"name\": \"Legolas\",\n \"status\": \"red\",\n \"parent\": \"Level 2-A\"\n },\n {\n \"name\": \"Finegal\",\n \"status\": \"green\",\n \"parent\": \"Level 2-A\"\n }\n ]\n },\n {\n \"name\": \"Level 2-B\",\n \"status\": \"red\",\n \"parent\": \"Top Level\",\n \"children\": [\n {\n \"name\": \"Durin\",\n \"status\": \"red\",\n \"parent\": \"Level 2-B\",\n \"children\": [\n {\n \"name\": \"Frodo\",\n \"status\": \"green\",\n \"parent\": \"Durin\"\n },\n {\n \"name\": \"Bilbo\",\n \"status\": \"red\",\n \"parent\": \"Durin\"\n }\n ]\n },\n {\n \"name\": \"Balin\",\n \"status\": \"green\",\n \"parent\": \"Level 2-B\",\n \"children\": [\n {\n \"name\": \"Merry\",\n \"status\": \"green\",\n \"parent\": \"Balin\"\n },\n {\n \"name\": \"Pipen\",\n \"status\": \"green\",\n \"parent\": \"Balin\"\n }\n ]\n }\n ]\n },\n {\n \"name\": \"Level 2-C\",\n \"parent\": \"Top Level\",\n \"status\": \"green\",\n \"children\": [\n {\n \"name\": \"Gandalf\",\n \"status\": \"green\",\n \"parent\": \"Level 2-C\"\n },\n {\n \"name\": \"Sauruman\",\n \"status\": \"green\",\n \"parent\": \"Level 2-C\"\n }\n ]\n }\n ]\n }\n ];\n\n\n// ************** Generate the tree diagram\t *****************\nvar margin = {top: 20, right: 120, bottom: 20, left: 120},\n\twidth = 960 - margin.right - margin.left,\n\theight = 500 - margin.top - margin.bottom;\n\t\nvar i = 0,\n\tduration = 750,\n\troot;\n\nvar tree = d3.layout.tree()\n\t.size([height, width]);\n\nvar diagonal = d3.svg.diagonal()\n\t.projection(function(d) { return [d.y, d.x]; });\n\nvar svg = d3.select(\"#Layout_map\").append(\"svg\")\n\t.attr(\"width\", width + margin.right + margin.left)\n\t.attr(\"height\", height + margin.top + margin.bottom)\n .append(\"g\")\n\t.attr(\"transform\", \"translate(\" + margin.left + \",\" \n\t + margin.top + \")\");\n\nroot = treeData[0];\nroot.x0 = height / 2;\nroot.y0 = 0;\n\nfunction toggleAll(d) {\n if (d.children) {\n if (d.status == \"green\") {\n d._children = d.children;\n d._children.forEach(toggleAll);\n d.children = null;\n }\n }\n}\n\nroot.children.forEach(toggleAll);\n \nupdate(root);\n\nd3.select(self.frameElement).style(\"height\", \"500px\");\n\nfunction update(source) {\n\n // Compute the new tree layout.\n var nodes = tree.nodes(root).reverse(),\n\t links = tree.links(nodes);\n\n // Normalize for fixed-depth.\n nodes.forEach(function(d) { d.y = d.depth * 180; });\n\n // Update the nodes…\n var node2 = svg.selectAll(\"g.node\")\n\t .data(nodes, function(d) { return d.id || (d.id = ++i); });\n\n // Enter any new nodes at the parent's previous position.\n var nodeEnter = node2.enter().append(\"g\")\n\t .attr(\"class\", \"node\")\n\t .attr(\"transform\", function(d) { \n\t\t return \"translate(\" + source.y0 + \",\" + source.x0 + \")\"; })\n\t .on(\"click\", click);\n\n nodeEnter.append(\"circle\")\n\t .attr(\"r\", 1e-6)\n\t .style(\"fill\", function(d) { return d.status; });\n\n nodeEnter.append(\"text\")\n\t .attr(\"x\", function(d) { \n\t\t return d.children || d._children ? -13 : 13; })\n\t .attr(\"dy\", \".35em\")\n\t .attr(\"text-anchor\", function(d) { \n\t\t return d.children || d._children ? \"end\" : \"start\"; })\n\t .text(function(d) { return d.name; })\n\t .style(\"fill-opacity\", 1e-6);\n\n // Transition nodes to their new position.\n var nodeUpdate = node2.transition()\n\t .duration(duration)\n\t .attr(\"transform\", function(d) { \n\t\t return \"translate(\" + d.y + \",\" + d.x + \")\"; });\n\n nodeUpdate.select(\"circle\")\n\t .attr(\"r\", 10)\n\t .style(\"fill\", function(d) { return d.status; });\n\n nodeUpdate.select(\"text\")\n\t .style(\"fill-opacity\", 1);\n\n // Transition exiting nodes to the parent's new position.\n var nodeExit = node2.exit().transition()\n\t .duration(duration)\n\t .attr(\"transform\", function(d) { \n\t\t return \"translate(\" + source.y + \",\" + source.x + \")\"; })\n\t .remove();\n\n nodeExit.select(\"circle\")\n\t .attr(\"r\", 1e-6);\n\n nodeExit.select(\"text\")\n\t .style(\"fill-opacity\", 1e-6);\n\n // Update the links…\n var link = svg.selectAll(\"path.link\")\n\t .data(links, function(d) { return d.target.id; });\n\n // Enter any new links at the parent's previous position.\n link.enter().insert(\"path\", \"g\")\n\t .attr(\"class\", \"link\")\n\t .attr(\"d\", function(d) {\n\t\tvar o = {x: source.x0, y: source.y0};\n\t\treturn diagonal({source: o, target: o});\n\t });\n\n // Transition links to their new position.\n link.transition()\n\t .duration(duration)\n\t .attr(\"d\", diagonal);\n\n // Transition exiting nodes to the parent's new position.\n link.exit().transition()\n\t .duration(duration)\n\t .attr(\"d\", function(d) {\n\t\tvar o = {x: source.x, y: source.y};\n\t\treturn diagonal({source: o, target: o});\n\t })\n\t .remove();\n\n // Stash the old positions for transition.\n nodes.forEach(function(d) {\n\td.x0 = d.x;\n\td.y0 = d.y;\n });\n}\n\n// Toggle children on click.\nfunction click(d) {\n console.log(d);\n if (d.children) {\n\td._children = d.children;\n\td.children = null;\n } else {\n\td.children = d._children;\n\td._children = null;\n }\n update(d);\n}\n</script>\n\n</body>\n\n</html>","storeOutMessages":false,"fwdInMessages":true,"templateScope":"local","x":520,"y":220,"wires":[["7b976e82.e2bb7"]]},{"id":"6a8123c4.16a83c","type":"ui_group","z":"","name":"map","tab":"4361b344.61bc2c","order":2,"disp":false,"width":"16","collapse":false},{"id":"4361b344.61bc2c","type":"ui_tab","z":"","name":"Layout","icon":"dashboard","order":2}]
almost there
able to update by msg.payload
but
Flow:
tree.txt (8.5 KB)
Hi guys,
Very interesting stuff !
@natanel: will you make this afterwards available on flows.nodered.org, so it could be used as a generic usable component?
Kind regards,
Bart
Hi Bart
first thanks
few points
any way my suggestion
this tree is very specific to the example data (found in link)
just send me a link so I can benefit from any improvement
Hi
finally got something
that I can do and I am pleased
tree.txt (13.2 KB)
not sure my clear method is correct, but it's working
// clear
d3.select("#Layout_map_SVG").remove();
still will be nice to have
Andrei - as before, your help is gold
zenofmud - you can see the code you asked in the attachment
Bart - fill free to progress this node, I can use the community help
BR
Natanel
Hello @natanel,
I am lagging behind as only now I could have a look on this post.
Looks like you already solved both issues but let me provide my thoughts
I would rather find a way to add the SVG element only once, at the startup, so there will be no need to add a statement to remove the SVG element later on. This can be achieved by moving a few lines of code outside the function(scope) block. I did it and, apparently, it is working all right.
In regards to expand all the tree nodes at the startup. As far as I could understand the function toggleAll(d) will check the color of the node and expand or not. In order to always expand at the startup, I modified the color to one not used (black). It worked.
Here is the modified flow in case you want to use some of these ideas:
[{"id":"f889d991.03a9d8","type":"tab","label":"Flow 3","disabled":false,"info":""},{"id":"dd4f4542.e90b08","type":"ui_template","z":"f889d991.03a9d8","group":"4ccda03f.4a457","name":"map matrix","order":0,"width":"6","height":"1","format":"<style>\t\n\t.node {\n\t\tcursor: pointer;\n\t}\n\n\t.node circle {\n\t fill: #fff;\n\t stroke: steelblue;\n\t stroke-width: 3px;\n\t}\n\n\t.node text {\n\t font: 12px sans-serif;\n\t fill: #fff;\n\t}\n\n\t.link {\n\t fill: none;\n\t stroke: #ccc;\n\t stroke-width: 2px;\n\t}\n</style>\n\n<script>\n // ************** Generate the tree diagram\t *****************\n var margin = { top: 20, right: 120, bottom: 20, left: 120 },\n width = 960 - margin.right - margin.left,\n height = 500 - margin.top - margin.bottom;\n \n var svg = d3.select(\"#Layout_map\").append(\"svg\")\n .attr(\"width\", width + margin.right + margin.left)\n .attr(\"height\", height + margin.top + margin.bottom)\n .append(\"g\")\n .attr(\"transform\", \"translate(\" + margin.left + \",\"\n + margin.top + \")\");\n \n</script>\n\n\n<script>\n(function (scope) {\n //console.dir(scope);\n scope.$watch('msg', function (msg) {\n //console.dir(msg);\n\n var treeData = msg.payload.treeData;\n\n\n var i = 0,\n duration = 750,\n root;\n\n var tree = d3.layout.tree()\n .size([height, width]);\n\n var diagonal = d3.svg.diagonal()\n .projection(function (d) { return [d.y, d.x]; });\n\n\n\n root = treeData[0];\n root.x0 = height / 2;\n root.y0 = 0;\n\n function toggleAll(d) {\n if (d.children) {\n if (d.status == \"black\") {\n d._children = d.children;\n d._children.forEach(toggleAll);\n d.children = null;\n }\n }\n }\n\n root.children.forEach(toggleAll);\n\n update(root);\n\n //root = treeData[1];\n //root.x0 = 2* ( height / 3 );\n //root.y0 = 0;\n //root.children.forEach(toggleAll);\n //update(root);\n\n d3.select(self.frameElement).style(\"height\", \"500px\");\n\n function update(source) {\n\n // Compute the new tree layout.\n var nodes = tree.nodes(root).reverse(),\n links = tree.links(nodes);\n\n // Normalize for fixed-depth.\n nodes.forEach(function (d) { d.y = d.depth * 180; });\n\n // Update the nodes…\n var node2 = svg.selectAll(\"g.node\")\n .data(nodes, function (d) { return d.id || (d.id = ++i); });\n\n // Enter any new nodes at the parent's previous position.\n var nodeEnter = node2.enter().append(\"g\")\n .attr(\"class\", \"node\")\n .attr(\"transform\", function (d) {\n return \"translate(\" + source.y0 + \",\" + source.x0 + \")\";\n })\n .on(\"click\", click);\n\n nodeEnter.append(\"circle\")\n .attr(\"r\", 1e-6)\n .style(\"fill\", function (d) { return d.status; });\n\n nodeEnter.append(\"text\")\n .attr(\"x\", function (d) {\n return d.children || d._children ? -13 : 13;\n })\n .attr(\"dy\", \".35em\")\n .attr(\"text-anchor\", function (d) {\n return d.children || d._children ? \"end\" : \"start\";\n })\n .text(function (d) { return d.name; })\n .style(\"fill-opacity\", 1e-6);\n\n // Transition nodes to their new position.\n var nodeUpdate = node2.transition()\n .duration(duration)\n .attr(\"transform\", function (d) {\n return \"translate(\" + d.y + \",\" + d.x + \")\";\n });\n\n nodeUpdate.select(\"circle\")\n .attr(\"r\", 10)\n .style(\"fill\", function (d) { return d.status; });\n\n nodeUpdate.select(\"text\")\n .style(\"fill-opacity\", 1);\n\n // Transition exiting nodes to the parent's new position.\n var nodeExit = node2.exit().transition()\n .duration(duration)\n .attr(\"transform\", function (d) {\n return \"translate(\" + source.y + \",\" + source.x + \")\";\n })\n .remove();\n\n nodeExit.select(\"circle\")\n .attr(\"r\", 1e-6);\n\n nodeExit.select(\"text\")\n .style(\"fill-opacity\", 1e-6);\n\n // Update the links…\n var link = svg.selectAll(\"path.link\")\n .data(links, function (d) { return d.target.id; });\n\n // Enter any new links at the parent's previous position.\n link.enter().insert(\"path\", \"g\")\n .attr(\"class\", \"link\")\n .attr(\"d\", function (d) {\n var o = { x: source.x0, y: source.y0 };\n return diagonal({ source: o, target: o });\n });\n\n // Transition links to their new position.\n link.transition()\n .duration(duration)\n .attr(\"d\", diagonal);\n\n // Transition exiting nodes to the parent's new position.\n link.exit().transition()\n .duration(duration)\n .attr(\"d\", function (d) {\n var o = { x: source.x, y: source.y };\n return diagonal({ source: o, target: o });\n })\n .remove();\n\n // Stash the old positions for transition.\n nodes.forEach(function (d) {\n d.x0 = d.x;\n d.y0 = d.y;\n });\n }\n\n // Toggle children on click.\n function click(d) {\n //console.log(d);\n if (d.children) {\n d._children = d.children;\n d.children = null;\n } else {\n d.children = d._children;\n d._children = null;\n }\n update(d);\n }\n });\n})(scope);\n</script>","storeOutMessages":true,"fwdInMessages":true,"templateScope":"local","x":790,"y":240,"wires":[[]]},{"id":"2b1738d4.41a168","type":"function","z":"f889d991.03a9d8","name":"prepare map data","func":"// data to read from cordinator eeprom\nvar numBonded = 4;\nvar numDiscovered = 3;\n//\t\t\t\t 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15\nvar nodesBonded = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 0];\nvar nodesDiscovered = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 0];\nvar vrnArray = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 3, 2, 0, 0];\nvar nodesZone = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 1, 0, 0];\nvar nodesParent = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0];\n\nvar treeData = [\n {\n \"name\": \"Cordinator\",\n \"parent\": \"null\",\n \"status\": \"orange\",\n \"children\": [],\n \"zone\": 0\n }\n];\n\nvar index = 0;\nwhile (numBonded) {\n index++;\n if (nodesBonded[index] == 1) {\n var parent = \"\";\n var status = \"\";\n\n // orphan\n if (nodesDiscovered[index] === 0) {\n parent = \"null\";\n status = \"red\";\n }\n // first children\n else if (nodesParent[index] === 0) {\n parent = \"Cordinator\";\n status = \"green\";\n }\n // other children\n else {\n parent = \"ID \" + vrnArray.indexOf(nodesParent[index]).toString() + \", VRN \" + nodesParent[index].toString();\n status = \"green\";\n }\n\n treeData.push(\n {\n \"name\": \"ID \" + index.toString() + \", VRN \" + vrnArray[index].toString(),\n \"parent\": parent,\n \"status\": status,\n \"children\": [],\n \"zone\": nodesZone[index],\n });\n numBonded--;\n }\n}\n\n// sort array by zone\ntreeData.sort(function (a, b) { return (a.zone > b.zone) ? 1 : ((b.zone > a.zone) ? -1 : 0); });\n\n// arrange tree\nwhile (treeData[treeData.length - 1].zone !== 0) {\n var nodeChild = treeData.pop();\n\n // find and add as children to parent\n var i = FindParent(treeData, nodeChild);\n\n if (i != -1)\n treeData[i].children.push(nodeChild);\n}\n\nglobal.set(\"treeData\", treeData);\nmsg.payload = { \"treeData\": treeData };\n\nreturn msg;\n\nfunction FindParent(treeData, nodeChild) {\n for (var i = treeData.length - 1; i >= 0; i--) {\n var n = nodeChild.parent.localeCompare(treeData[i].name);\n if ((treeData[i].zone === (nodeChild.zone) - 1) && (n === 0))\n return i;\n }\n return -1;\n}","outputs":1,"noerr":0,"x":550,"y":180,"wires":[["527fa260.f0c70c","dd4f4542.e90b08"]]},{"id":"cb1c32ef.a7d4a","type":"inject","z":"f889d991.03a9d8","name":"","topic":"prepare map","payload":"","payloadType":"date","repeat":"","crontab":"","once":false,"onceDelay":0.1,"x":320,"y":180,"wires":[["2b1738d4.41a168"]]},{"id":"527fa260.f0c70c","type":"debug","z":"f889d991.03a9d8","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":790,"y":180,"wires":[]},{"id":"4ccda03f.4a457","type":"ui_group","z":"","name":"map","tab":"e870aec0.5ba89","order":2,"disp":true,"width":"6","collapse":false},{"id":"e870aec0.5ba89","type":"ui_tab","z":"","name":"Layout","icon":"dashboard","order":2}]