I would like to remove nodes that have 0 as "cardinalita".
For now I have hidden nodes but I would like remove empty link.
Here the situation:
https://jsfiddle.net/d65k3zzy/
var treeData = [
{
"name": "First",
"parent": "null",
"cardinalita": "9",
"children": [
{
"name": "Second",
"parent": "First",
"cardinalita": "1",
"children": [
{
"name": "Third",
"parent": "Second",
"cardinalita": "63",
"children": [
{
"name": "fourth",
"parent": "Third",
"cardinalita": "39",
"children": [
{
"name": "last",
"parent": "fourth",
"cardinalita": "70",
"children": [
{
"name": "special",
"parent": "last",
"cardinalita": "11"
}
]
},
{
"name": "special",
"parent": "fourth",
"cardinalita": "0"
}
]
},
{
"name": "null",
"parent": "Third",
"cardinalita": "0",
"children": [
{
"name": "special",
"parent": "null",
"cardinalita": "10"
}
]
}
]
},
{
"name": "Third",
"parent": "Second",
"cardinalita": "528"
}
]
},
{
"name": "Second",
"parent": "First",
"cardinalita": "33",
"children": [
{
"name": "Third",
"parent": "Second",
"cardinalita": "63"
}
]
}
]
}
];
// ************** Generate the tree diagram *****************
var margin = {top: 0, right: 120, bottom: 40, left: 60},
width = 1000 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
// distanza fra i nodi figli
.separation(function separation(a, b) { return a.parent == b.parent ? 1.5 : 1; })
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("#diagramma").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData[0];
root.x0 = height / 2;
root.y0 = 0;
update(root);
d3.select(self.frameElement).style("height", "500px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 160; });
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
//.on("click", click);
// contenitore testo
nodeEnter.append("svg:rect")
.attr("width", function(d) { if(d.name != 'null' || (d.cardinalita < 1)){return "70";} })
.attr("height", function (d) {
return 21;
})
.attr("r", 1e-6)
.attr("y", -10)
.attr("x", -50)
.attr("rx", 0)
.attr("ry", 0)
.attr("stroke", function(d) { if(d.name != 'null' && d.cardinalita >= 1){return "#23527c";} })
.attr("stroke-width", "2")
.style("fill", function (d) {
return d._children ? "#ccc" : "#fff";
})
.style("fill-opacity", function(d) { if(d.name == 'null' || (d.cardinalita < 1)){return "0";} })
//contenitore cardinalità
nodeEnter.append("svg:rect")
.attr("width", 25)
.attr("height", function (d) {
return 21;
})
.attr("r", 1e-6)
.attr("y", 11)
.attr("x", -5)
.attr("rx", 0)
.attr("ry", 0)
.attr("stroke", function(d) { if(d.name != 'null' && d.cardinalita >= 1){return "#23527c";} })
.attr("stroke-width", "2")
.style("fill", "#fff")
.style("fill-opacity", function(d) { if(d.name == 'null' || (d.cardinalita < 1)){return "0";} })
// nome del nodo
nodeEnter.append("text")
.attr("x", function (d) {
return d._children ? -8 : 8;
})
.attr("y", -4)
.attr("x", -40)
.attr("dy", "0.7em")
.style("font-size", "14")
.style("font-weight", "bold")
.text(function (d) {
if(d.name != 'null' && d.cardinalita != 0)
return d.name;
});
// cardinalità
nodeEnter.append("text")
.attr("x", function (d) {
return d._children ? -8 : 8;
})
.attr("y", 18)
.attr("x", -2)
.attr("dy", "0.7em")
.style("font-size", "14")
.text(function (d) {
if(d.name != 'null' && d.cardinalita >= 1)
return d.cardinalita;
});
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("rect")
.attr("r", 10)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("rect")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: source.x0, y: source.y0};
return diagonal({source: o, target: o});
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// 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);
}
header { padding: 20px; }
section { overflow: hidden; width: 1000px; margin: 0 auto;}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
#diagramma { display: inline; float: left; }
.map { display: inline; float: right; }
.p {
font-family: Arial, sans-serif;
text-align: center;
}
.map svg {
height: auto;
width: auto;
min-width: 300px;
max-width: 400px;
margin: 0 auto;
display: block;
}
.map g {
fill: #ccc;
stroke: #fff;
stroke-width: 2;
}
.map g:hover, g.active, .st0:hover, .active .st0 {
fill: #23527c !important;
cursor: help;
cursor: pointer;
}
.info_panel {
background-color: rgba(255,255,255, .8);
padding: 5px;
font-size: 12px;
font-family: Helvetica, Arial, sans-serif;
position: absolute;
border: 1px solid #333;
color: #333;
white-space: nowrap;
}
.info_panel::first-line {
font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
<section>
<div id="diagramma">
</div>
</section>
</body>
I think you really want to remove links that have cardinalita === "0" and have no children:
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", function(d){
if (!d.target.children && d.target.cardinalita === "0")
return "M0,0"; //<-- draw nothing
else
return diagonal(d);
});
Full code:
var treeData = [
{
"name": "First",
"parent": "null",
"cardinalita": "9",
"children": [
{
"name": "Second",
"parent": "First",
"cardinalita": "1",
"children": [
{
"name": "Third",
"parent": "Second",
"cardinalita": "63",
"children": [
{
"name": "fourth",
"parent": "Third",
"cardinalita": "39",
"children": [
{
"name": "last",
"parent": "fourth",
"cardinalita": "70",
"children": [
{
"name": "special",
"parent": "last",
"cardinalita": "11"
}
]
},
{
"name": "special",
"parent": "fourth",
"cardinalita": "0"
}
]
},
{
"name": "null",
"parent": "Third",
"cardinalita": "0",
"children": [
{
"name": "special",
"parent": "null",
"cardinalita": "10"
}
]
}
]
},
{
"name": "Third",
"parent": "Second",
"cardinalita": "528"
}
]
},
{
"name": "Second",
"parent": "First",
"cardinalita": "33",
"children": [
{
"name": "Third",
"parent": "Second",
"cardinalita": "63"
}
]
}
]
}
];
// ************** Generate the tree diagram *****************
var margin = {top: 0, right: 120, bottom: 40, left: 60},
width = 1000 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
// distanza fra i nodi figli
.separation(function separation(a, b) { return a.parent == b.parent ? 1.5 : 1; })
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) { return [d.y, d.x]; });
var svg = d3.select("#diagramma").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData[0];
root.x0 = height / 2;
root.y0 = 0;
update(root);
d3.select(self.frameElement).style("height", "500px");
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) { d.y = d.depth * 160; });
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) { return d.id || (d.id = ++i); });
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) { return "translate(" + source.y0 + "," + source.x0 + ")"; })
//.on("click", click);
// contenitore testo
nodeEnter.append("svg:rect")
.attr("width", function(d) { if(d.name != 'null' || (d.cardinalita < 1)){return "70";} })
.attr("height", function (d) {
return 21;
})
.attr("r", 1e-6)
.attr("y", -10)
.attr("x", -50)
.attr("rx", 0)
.attr("ry", 0)
.attr("stroke", function(d) { if(d.name != 'null' && d.cardinalita >= 1){return "#23527c";} })
.attr("stroke-width", "2")
.style("fill", function (d) {
return d._children ? "#ccc" : "#fff";
})
.style("fill-opacity", function(d) { if(d.name == 'null' || (d.cardinalita < 1)){return "0";} })
//contenitore cardinalità
nodeEnter.append("svg:rect")
.attr("width", 25)
.attr("height", function (d) {
return 21;
})
.attr("r", 1e-6)
.attr("y", 11)
.attr("x", -5)
.attr("rx", 0)
.attr("ry", 0)
.attr("stroke", function(d) { if(d.name != 'null' && d.cardinalita >= 1){return "#23527c";} })
.attr("stroke-width", "2")
.style("fill", "#fff")
.style("fill-opacity", function(d) { if(d.name == 'null' || (d.cardinalita < 1)){return "0";} })
// nome del nodo
nodeEnter.append("text")
.attr("x", function (d) {
return d._children ? -8 : 8;
})
.attr("y", -4)
.attr("x", -40)
.attr("dy", "0.7em")
.style("font-size", "14")
.style("font-weight", "bold")
.text(function (d) {
if(d.name != 'null' && d.cardinalita != 0)
return d.name;
});
// cardinalità
nodeEnter.append("text")
.attr("x", function (d) {
return d._children ? -8 : 8;
})
.attr("y", 18)
.attr("x", -2)
.attr("dy", "0.7em")
.style("font-size", "14")
.text(function (d) {
if(d.name != 'null' && d.cardinalita >= 1)
return d.cardinalita;
});
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + d.y + "," + d.x + ")"; });
nodeUpdate.select("rect")
.attr("r", 10)
.style("fill", function(d) { return d._children ? "lightsteelblue" : "#fff"; });
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) { return "translate(" + source.y + "," + source.x + ")"; })
.remove();
nodeExit.select("rect")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) { return d.target.id; });
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link");
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", function(d){
if (!d.target.children && d.target.cardinalita === "0")
return "M0,0";
else
return diagonal(d);
});
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {x: source.x, y: source.y};
return diagonal({source: o, target: o});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
// 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);
}
header { padding: 20px; }
section { overflow: hidden; width: 1000px; margin: 0 auto;}
.node text {
font: 12px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
#diagramma { display: inline; float: left; }
.map { display: inline; float: right; }
.p {
font-family: Arial, sans-serif;
text-align: center;
}
.map svg {
height: auto;
width: auto;
min-width: 300px;
max-width: 400px;
margin: 0 auto;
display: block;
}
.map g {
fill: #ccc;
stroke: #fff;
stroke-width: 2;
}
.map g:hover, g.active, .st0:hover, .active .st0 {
fill: #23527c !important;
cursor: help;
cursor: pointer;
}
.info_panel {
background-color: rgba(255,255,255, .8);
padding: 5px;
font-size: 12px;
font-family: Helvetica, Arial, sans-serif;
position: absolute;
border: 1px solid #333;
color: #333;
white-space: nowrap;
}
.info_panel::first-line {
font-weight: bold;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<!DOCTYPE html>
<body>
<section>
<div id="diagramma">
</div>
</section>
</body>
Related
I am working on an angular application with d3. My code is as follows.
var treeData = [{
"name": "MD",
"children": [{
"name": "Professional",
"children": [{
"name": "Third A",
"children": [{
"name": "Fourth A",
"children": [{
"name": "Fifth A"
}, {
"name": "Fifth B"
}, {
"name": "Fifth C"
}, {
"name": "Fifth D"
}]
}, {
"name": "Fourth B"
}, {
"name": "Fourth C"
}, {
"name": "Fourth D"
}]
}, {
"name": "Third B"
}]
}, {
"name": "Leader",
"children": [{
"name": "Third C"
}, {
"name": "Third D"
}]
}, {
"name": "Advocate",
"children": [{
"name": "Third E"
}, {
"name": "Third F"
}]
}, {
"name": "Clinician",
"children": [{
"name": "Third G"
}, {
"name": "Third H"
}, ]
}, ]
}];
var colourScale = d3.scale.ordinal()
.domain(["MD", "Professional", "Leader", "Advocate", "Clinician"])
.range(["#6695c8", "#cd3838", "#d48440", "#a8ba5f", "#63b7c0"]);
// ************** Generate the tree diagram *****************
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 1200 - margin.right - margin.left,
height = 650 - margin.top - margin.bottom;
var i = 0,
duration = 750,
root;
var tree = d3.layout.tree()
.size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData[0];
root.x0 = height / 2;
root.y0 = 0;
update(root);
d3.select(self.frameElement).style("height", "500px");
// Collapse after the second level
root.children.forEach(collapse);
update(root);
// Collapse the node and all it's children
function collapse(d) {
if (d.children) {
d._children = d.children
d._children.forEach(collapse)
d.children = null
}
}
function update(source) {
console.log('UPDATE')
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 200;
});
// Update the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + source.y0 + "," + source.x0 + ")";
})
.on("click", click);
nodeEnter.append("circle")
.attr("r", 1e-6)
.style("fill", function(d) {
return d._children ? "#C0C0C0" : "#fff";
});
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -13 : 13;
})
.attr("dy", ".35em")
.attr("text-anchor", function(d) {
return d.children || d._children ? "end" : "start";
})
.text(function(d) {
return d.name;
})
.style("fill-opacity", 1e-6);
nodeEnter.append('foreignObject').attr('width', '20')
.attr("x", 10)
.attr("y", 1)
.attr('height', '20').append('xhtml:input')
.attr('type', 'checkbox')
.attr("id", d => `checkbox-${d.id}`)
//.attr("fill","none")
//.style("opacity","1")
// An on click function for the checkboxes
.on("click", d => {
if (d.children) {
d.children.forEach(child => {
const cb = d3.select(`#checkbox-${child.id}`);
//console.log('CB: ', cb.node());
cb.node().checked = d3.event.target.checked;
cb.attr('disabled', d3.event.target.checked ? true : null);
})
}
else {
if (d3.event.target.checked) {
d.parent.children.forEach(child => {
console.log('CID: ', child.id, d.id);
if (child.id !== d.id) {
const cb = d3.select(`#checkbox-${child.id}`);
console.log('CB: ', cb.node())
cb.node().checked = false;
}
});
}
}
d3.event.stopPropagation();
//console.log(d);
//console.log(d3.event.target.checked);
})
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeUpdate.select("circle")
.attr("r", 10)
.attr("fill-opacity", "0.7")
.attr("stroke-opacity", "1")
.style("fill", function(d) {
return (typeof d._children !== 'undefined') ? (colourScale(findParent(d))) : '#FFF';
})
.style("stroke", function(d) {
return colourScale(findParent(d));
});
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 1e-6);
nodeExit.select("text")
.style("fill-opacity", 1e-6);
// Update the links…
var link = svg.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("stroke-width", function(d) {
return 1;
})
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
})
.attr("opacity", "0.3")
.style("stroke", function(d) {
return colourScale(findParentLinks(d));
});
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
function findParent(datum) {
if (datum.depth < 2) {
return datum.name
} else {
return findParent(datum.parent)
}
}
function findParentLinks(datum) {
if (datum.target.depth < 2) {
return datum.target.name
} else {
return findParent(datum.target.parent)
}
}
// 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);
}
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: #C0C0C0;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #C0C0C0;
stroke-width: 1.5px;
}
I am facing following problem:
If a collapsed node has children, and its parent is getting checked, the node itself is checked as well, but its children cannot be checked because they are not visible.
The checking should work recursively: if a node is checked, all its descendants should be checked and disabled as well regardless their state (expanded or collapsed).
How can I do this?
Here is a proposed solution:
const treeData = {
"id": 1,
"name": "Root",
"checked": false,
"color": "white",
"children": [
{
"id": 2,
"name": "Leaf A",
"checked": false,
"color": "red",
"children": [
{
"id": 3,
"name": "A - 1",
"checked": false,
"color": "brown",
},
{
"id": 4,
"name": "A - 2",
"checked": false,
"color": "orange",
},
{
"id": 5,
"name": "A - 3",
"checked": false,
"color": "yellow",
},
]
},
{
"id": 6,
"name": "Leaf B",
"checked": false,
"color": "green",
"children": [
{
"id": 7,
"name": "B - 1",
"checked": false,
"color": "#00ff40",
},
{
"id": 8,
"name": "B - 2",
"checked": false,
"color": "#00ff80",
}
]
}
]
};
const margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
};
const width = 600 - margin.right - margin.left;
const height = 400 - margin.top - margin.bottom;
var i = 0,duration = 750;
const tree = d3.layout.tree()
.size([height, width]);
const diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
const svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom);
const container = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
const root = treeData;
root.x0 = height / 2;
root.y0 = 0;
// Collapse after the second level
root.children.forEach(collapse);
update(root);
// Collapse the node and all it's children
function collapse(d) {
if (d.children) {
d._children = d.children
d._children.forEach(collapse)
d.children = null
}
}
function update(source) {
// Compute the new tree layout.
const nodes = tree.nodes(root).reverse();
const links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 200;
});
// Update the nodes…
const node = container.selectAll("g.node")
.data(nodes, d => d.id);
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter()
.append("g")
.attr("class", "node")
.attr("transform", d => `translate(${source.y0},${source.x0})`)
.on("click", onClickNode);
nodeEnter.append("circle")
.attr("r", 10)
.style("fill", d => d.color);
nodeEnter.append("text")
.attr("x", 20)
.attr("dy", 4)
.attr("text-anchor", "start")
.text(d => d.name);
nodeEnter.append('foreignObject')
.attr('width', '20')
.attr('height', '20')
.attr("x", -30)
.attr("y", -8)
.append('xhtml:input')
.attr('type', 'checkbox')
.attr("id", d => `checkbox-${d.id}`)
.on("click", onClickCheckbox)
// Transition nodes to their new position.
var nodeUpdate = node.transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
// ???
nodeUpdate.select("circle")
.style("stroke", 'black');
nodeUpdate.each(function(d) {
const cb = d3.select(this).select('[type="checkbox"]').node();
cb.checked = d.checked;
cb.disabled = isParentChecked(d);
});
nodeUpdate.select("text")
.style("fill-opacity", 1);
// Transition exiting nodes to the parent's new position.
const nodeExit = node.exit().transition()
.duration(duration)
.attr("transform", function(d) {
return "translate(" + source.y + "," + source.x + ")";
})
.remove();
nodeExit.select("circle")
.attr("r", 0);
nodeExit.select("text")
.style("fill-opacity", 0);
// Update the links…
var link = container.selectAll("path.link")
.data(links, d => d.target.id);
// Enter any new links at the parent's previous position.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("stroke-width", 1)
.attr("d", function(d) {
var o = {
x: source.x0,
y: source.y0
};
return diagonal({
source: o,
target: o
});
})
.attr("opacity", "0.3")
.style("stroke", 'black');
// Transition links to their new position.
link.transition()
.duration(duration)
.attr("d", diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition()
.duration(duration)
.attr("d", function(d) {
var o = {
x: source.x,
y: source.y
};
return diagonal({
source: o,
target: o
});
})
.remove();
// Stash the old positions for transition.
nodes.forEach(function(d) {
d.x0 = d.x;
d.y0 = d.y;
});
}
function findParent(datum) {
if (datum.depth < 2) {
return datum.name
} else {
return findParent(datum.parent)
}
}
function findParentLinks(datum) {
if (datum.target.depth < 2) {
return datum.target.name
} else {
return findParent(datum.target.parent)
}
}
const checkNode = (d, checked, byParent) => {
if (d.id === 2)
console.log('CHECK TO: ', checked);
d.checked = checked;
const children = d.children || d._children;
if (children)
children.forEach(child => checkNode(child, checked, true));
if (!byParent && checked && d.parent) {
console.log('UNCHECK SIBLINGS');
const siblings = d.parent.children || d.parent._children;
siblings.forEach(sibling => {
if (sibling.id !== d.id) {
console.log('UNCHECK: ', sibling)
checkNode(sibling, false, true);
}
});
}
}
function isParentChecked (d) {
if (!d.parent) {
return false;
}
if (d.parent.checked) {
return true;
}
return isParentChecked(d.parent);
}
function onClickCheckbox(d) {
d3.event.stopPropagation();
checkNode(d, d3.event.target.checked, false);
console.log('ROOT: ', root);
update(root);
}
// Toggle children on click.
function onClickNode(d) {
if (d.children) {
d._children = d.children;
d.children = null;
}
else {
d.children = d._children;
d._children = null;
}
update(d);
}
.node {
cursor: pointer;
}
.node circle {
fill: #fff;
stroke: #C0C0C0;
stroke-width: 1.5px;
}
.node text {
font: 10px sans-serif;
}
.link {
fill: none;
stroke: #C0C0C0;
stroke-width: 1.5px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.17/d3.min.js"></script>
I have created a horizontal tree diagram as shown in below image. I want straight lines between nodes. The curved lines between nodes are default in d3 js. I saw some answers on google for this but did not found any satisfactory result. So is it possible to draw straight lines between nodes in d3 js? If yes then how can I do that?
enter image description here
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
.node circle {
fill: #ff9900;
stroke: #ff9900;
stroke-width: 1px;
}
.node text {
font: 16px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
</style>
</head>
<body>
<script language="javascript">
var treeData = [{
"name": "1",
"parent": "null",
"children": [{
"name": "2",
"parent": "Persons",
"children": [{
"name": "3",
"parent": "Country of residence"
}, {
"name": "4",
"parent": "Country of residence"
}, {
"name": "5",
"parent": "Country of residence"
}, {
"name": "6",
"parent": "Country of residence"
}]
}]
}];
// ************** Generate the tree diagram *****************
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 960 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var i = 0;
var tree = d3.layout.tree().size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var line = d3.svg.line()
.x(function(d) {
return d.lx;
})
.y(function(d) {
return d.ly;
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData[0];
update(root);
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 180;
});
// Declare the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter the nodes.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeEnter.append("circle")
.attr("r", 40)
.style("fill", "#ff9900");
// append icon inside circle
nodeEnter.append("image")
.attr("xlink:href", "http://localhost/d3/user2.jpg")
.attr("x", "-18px")
.attr("y", "-18px")
.attr("width", "35px")
.attr("height", "35px");
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -40 : -50;
})
.attr("y", function(d) {
return d.children || d._children ? 55 : 55;
})
.attr("dy", ".35em")
.attr("text-anchor", function(d) {
return d.children || d._children ? "start" : "start";
})
.text(function(d) {
return d.name;
})
.style("fill-opacity", 1);
// Declare the links…
var link = svg.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter the links.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", diagonal);
}
</script>
</body>
</body>
</html>
Define your line :
var line = d3.svg.line()
.x(function(d) {
return d.y; // because tree is horizontal
})
.y(function(d) {
return d.x; // because tree is horizontal
});
Change your links function to this because d3.svg.line() takes array of points as argument
Hope this helps
// Enter the links.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) {
return line([d.source, d.target]);
});
<!DOCTYPE html>
<html lang="en">
<head>
<link rel="stylesheet" href="https://use.fontawesome.com/releases/v5.3.1/css/all.css" integrity="sha384-mzrmE5qonljUremFsqc01SB46JvROS7bZs3IO2EmfFsd15uHvIt+Y8vEf7N7fWAU" crossorigin="anonymous">
<script src="https://d3js.org/d3.v3.min.js"></script>
<style>
.node circle {
fill: #ff9900;
stroke: #ff9900;
stroke-width: 1px;
}
.node text {
font: 16px sans-serif;
}
.link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
}
</style>
</head>
<body>
<script language="javascript">
var treeData = [{
"name": "1",
"parent": "null",
"children": [{
"name": "2",
"parent": "Persons",
"children": [{
"name": "3",
"parent": "Country of residence"
}, {
"name": "4",
"parent": "Country of residence"
}, {
"name": "5",
"parent": "Country of residence"
}, {
"name": "6",
"parent": "Country of residence"
}]
}]
}];
// ************** Generate the tree diagram *****************
var margin = {
top: 20,
right: 120,
bottom: 20,
left: 120
},
width = 960 - margin.right - margin.left,
height = 500 - margin.top - margin.bottom;
var i = 0;
var tree = d3.layout.tree().size([height, width]);
var diagonal = d3.svg.diagonal()
.projection(function(d) {
return [d.y, d.x];
});
var line = d3.svg.line()
.x(function(d) {
return d.y;
})
.y(function(d) {
return d.x;
});
var svg = d3.select("body").append("svg")
.attr("width", width + margin.right + margin.left)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
root = treeData[0];
update(root);
function update(source) {
// Compute the new tree layout.
var nodes = tree.nodes(root).reverse(),
links = tree.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function(d) {
d.y = d.depth * 180;
});
// Declare the nodes…
var node = svg.selectAll("g.node")
.data(nodes, function(d) {
return d.id || (d.id = ++i);
});
// Enter the nodes.
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.y + "," + d.x + ")";
});
nodeEnter.append("circle")
.attr("r", 40)
.style("fill", "#ff9900");
// append icon inside circle
nodeEnter.append("image")
.attr("xlink:href", "http://localhost/d3/user2.jpg")
.attr("x", "-18px")
.attr("y", "-18px")
.attr("width", "35px")
.attr("height", "35px");
nodeEnter.append("text")
.attr("x", function(d) {
return d.children || d._children ? -40 : -50;
})
.attr("y", function(d) {
return d.children || d._children ? 55 : 55;
})
.attr("dy", ".35em")
.attr("text-anchor", function(d) {
return d.children || d._children ? "start" : "start";
})
.text(function(d) {
return d.name;
})
.style("fill-opacity", 1);
// Declare the links…
var link = svg.selectAll("path.link")
.data(links, function(d) {
return d.target.id;
});
// Enter the links.
link.enter().insert("path", "g")
.attr("class", "link")
.attr("d", function(d) { return line([d.source, d.target])});
}
</script>
</body>
</body>
</html>
I have created a forced layout graph. it is working fine.
Now I need to make the blocks draggable such that, even after dragging the block it should not affect the other blocks as shown in this example.
Here's what I tried:
d3.json("links.json", function(error, links) {
var nodes = {};
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {name: link.source});
link.target = nodes[link.target] || (nodes[link.target] = {name: link.target});
});
var width = 1000,
height = 700;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.gravity(0.01)
.size([width, height])
.linkDistance(200)
.charge(-600)
.on("tick", tick)
.start();
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// Per-type markers, as they don't inherit styles.
svg.append("defs").selectAll("marker")
.data(["forward","back"]) //use .data(["forward","back","front"]) for different types of data
.enter().append("marker")
.attr("id", function(d) { return d; })
.attr("viewBox", "0 -5 10 10")
.attr("refX", 100)
.attr("refY", 1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-4L10,0L0,5");
var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("path")
.attr("class", function(d) { return "link " + d.type; })
.attr("marker-end", function(d) { return "url(#" + d.type + ")"; });
var rect = svg.append("g").selectAll("rect")
.data(force.nodes())
.enter().append("rect")
.attr("x", -20)
.attr("y", -20)
.attr("width", function(d) { return (d.weight*40); })
.attr("height", function(d) { return (d.weight*20); })
.call(force.drag).on("mouseover", fade(.1))
.on("mouseout", fade(1));;
force.drag().on('drag', fade(.1));
var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("x", 0)
.attr("y", ".41em")
.text(function(d) { return d.name; });
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
rect.attr("transform", transform);
path.attr("d", linkLine);
text.attr("transform", transform);
}
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
});
</script>
</body>
</html>
And data in links.json file is
[{"source": "Delhi", "target": "Mangalore", "type": "forward"},
{"source": "Mangalore", "target": "Delhi", "type": "back"},
{"source": "Delhi", "target": "Yashvanthpur", "type": "back"},
{"source": "Mangalore", "target": "Rome", "type": "forward"},
{"source": "Delhi", "target": "Mysore", "type": "forward"},
{"source": "Chandigarh", "target": "Vellore", "type": "forward"},
{"source": "Chandigarh", "target": "Mangalore", "type": "forward"},
{"source": "Delhi", "target": "Nagpur", "type": "forward"}
]
Are there any methods so that, I will not get any movements in the other nodes, even though I drag some node in the graph?
Is there any separate method to accomplish that?
Set property fixed to true for each node when force simulations are stopped.
force.on('end', function(d) {
links.forEach(function(l) {
l.source.fixed = true;
l.target.fixed = true;
})
});
var links = [{
"source": "Delhi",
"target": "Mangalore",
"type": "forward"
},
{
"source": "Mangalore",
"target": "Delhi",
"type": "back"
},
{
"source": "Delhi",
"target": "Yashvanthpur",
"type": "back"
},
{
"source": "Mangalore",
"target": "Rome",
"type": "forward"
},
{
"source": "Delhi",
"target": "Mysore",
"type": "forward"
},
{
"source": "Chandigarh",
"target": "Vellore",
"type": "forward"
},
{
"source": "Chandigarh",
"target": "Mangalore",
"type": "forward"
},
{
"source": "Delhi",
"target": "Nagpur",
"type": "forward"
}
];
var nodes = {};
// Compute the distinct nodes from the links.
links.forEach(function(link) {
link.source = nodes[link.source] || (nodes[link.source] = {
name: link.source
});
link.target = nodes[link.target] || (nodes[link.target] = {
name: link.target
});
});
var width = 1000,
height = 700;
var force = d3.layout.force()
.nodes(d3.values(nodes))
.links(links)
.gravity(0.01)
.size([width, height])
.linkDistance(200)
.charge(-600)
.on("tick", tick)
.start()
force.on('end', function(d) {
links.forEach(function(l) {
l.source.fixed = true;
l.target.fixed = true;
})
});
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height);
// Per-type markers, as they don't inherit styles.
svg.append("defs").selectAll("marker")
.data(["forward", "back"]) //use .data(["forward","back","front"]) for different types of data
.enter().append("marker")
.attr("id", function(d) {
return d;
})
.attr("viewBox", "0 -5 10 10")
.attr("refX", 100)
.attr("refY", 1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-4L10,0L0,5");
var path = svg.append("g").selectAll("path")
.data(force.links())
.enter().append("path")
.attr("class", function(d) {
return "link " + d.type;
})
.attr("marker-end", function(d) {
return "url(#" + d.type + ")";
});
var rect = svg.append("g").selectAll("rect")
.data(force.nodes())
.enter().append("rect")
.attr("x", -20)
.attr("y", -20)
.attr("width", function(d) {
return (d.weight * 40);
})
.attr("height", function(d) {
return (d.weight * 20);
})
.call(force.drag).on("mouseover", fade(.1))
.on("mouseout", fade(1));;
force.drag().on('drag', fade(.1));
var text = svg.append("g").selectAll("text")
.data(force.nodes())
.enter().append("text")
.attr("text-anchor", "middle")
.attr("x", function(d){ return -20+(d.weight * 20) })
.attr("y", function(d){ return -20+(d.weight * 20)/2 })
.text(function(d) {
return d.name;
});
// Use elliptical arc path segments to doubly-encode directionality.
function tick() {
rect.attr("transform", transform);
path.attr("d", linkLine);
text.attr("transform", transform);
}
var linkedByIndex = {};
links.forEach(function(d) {
linkedByIndex[d.source.index + "," + d.target.index] = 1;
});
function isConnected(a, b) {
return linkedByIndex[a.index + "," + b.index] ||
linkedByIndex[b.index + "," + a.index] ||
a.index == b.index;
};
function linkLine(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y;
return "M" + d.source.x + "," + d.source.y + "L" + d.target.x + "," + d.target.y;
}
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
// For fade-in and fade-out effect
function fade(opacity) {
return function(d) {
rect.style("stroke-opacity", function(o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
rect.style("stroke-opacity", opacity)
.style("stroke-opacity", function(o) {
return o.source === d || o.target === d ? 1 : opacity;
});
};
};
.link {
fill: none;
stroke: #666;
stroke-width: 2px;
}
#forward {
fill: green;
}
#back {
fill: red;
}
<!-- #back {
fill: green;
}
-->.link.forward {
stroke: green;
}
.link.back {
stroke: green;
}
rect {
fill: #a5b0ed;
stroke: #333;
stroke-width: 2px;
}
text {
font: 10px sans-serif;
pointer-events: none;
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
I want to link circles and rects by the shapes' name, it's all right.
I also remove the force layout, cause I want to use the static position.
but the drag function doesn't work, only the link can be dragged, not the Shapes. I dont konw the reason.
does someon help me review the code?
Thanks you very much.
var graph = {
"nodes":[
{
"appId":"AP110358",
"name":"Customer Account Profile",
"type":"CIRCLE",
"x":50,
"y":50
},
{
"appId":"NB",
"name":"NB",
"type":"CIRCLE",
"x":500,
"y":500
},
{
"appId":"AP114737",
"name":"RBG",
"type":"CIRCLE",
"x":300,
"y":600
},
{
"appId":"NULL",
"name":"Account",
"type":"RECT",
"x":400,
"y":700
}
],
"links":[
{
"source":"Customer Account Profile",
"target":"NB",
"value":1,
"label":null
},
{
"source":"NB",
"target":"RBG",
"value":1,
"label":null
},
{
"source":"RBG",
"target":"Customer Account Profile",
"value":1,
"label":null
},
{
"source":"NB",
"target":"Customer Account Profile",
"value":1,
"label":null
},
{
"source":"Customer Account Profile",
"target":"Account",
"value":1,
"label":null
},
{
"source":"NB",
"target":"Account",
"value":1,
"label":null
},
{
"source":"RBG",
"target":"Account",
"value":1,
"label":null
}
]
};
var width = window.innerWidth;
var height = window.innerHeight;
var center;
if(width > 1200){
center = [(width-1200) / 2, 0]
}else{
center = [0, 0]
}
var edges = [];
graph.links.forEach(function(e) {
var sourceNode = graph.nodes.filter(function(n) {
return n.name === e.source;
})[0],
targetNode = graph.nodes.filter(function(n) {
return n.name === e.target;
})[0];
edges.push({
source: sourceNode,
target: targetNode,
value: e.value
});
});
var color = d3.scale.category20();
var svg = d3.select("body")
.append("svg")
.attr("width", "1200")
.attr("height", "1000")
.attr("transform", "translate(" + center + ")")
.append("g");
var container = svg.append("g");
var link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(edges)
.enter().append("line")
.attr("class", "link")
.attr("x1", function(l) {
var sourceNode = graph.nodes.filter(function(d) {
return d == l.source
})[0];
d3.select(this).attr("y1", sourceNode.y);
return sourceNode.x
})
.attr("x2", function(l) {
var targetNode = graph.nodes.filter(function(d) {
return d == l.target
})[0];
d3.select(this).attr("y2", targetNode.y);
return targetNode.x
})
.style("stroke-width", function(d) {
return d.value;
});
link.append("title").text(function(d) {
return d.value;
});
var drag = d3.behavior.drag()
.on("drag", function(d, i) {
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("cx", d.x).attr("cy", d.y);
link.each(function(l) {
if (l.source == d) {
d3.select(this).attr("x1", d.x).attr("y1", d.y);
} else if (l.target == d) {
d3.select(this).attr("x2", d.x).attr("y2", d.y);
}
});
});
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.call(drag);
var i = 0;
node.each(function(d) {
if (d.type == "CIRCLE") {
d3.select(this).append("circle")
.attr("r", 50)
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.style("fill", function(d) { return color(i & 3);});
d3.select(this).append("text")
.text(function(d) {
return d.name;
})
.attr("transform", function(d) {
return "translate(" + d.x + "," + (d.y+5) + ")";
})
.style("text-anchor","middle");
} else {
d3.select(this).append("rect")
.attr("height", 40)
.attr("width", 140)
.attr("x", -10)
.attr("y", -(40 / 2))
.attr("transform", function(d) {
return "translate(" + d.x + "," + (d.y+5) + ")";
})
.style("fill", "green");
d3.select(this).append("text")
.text(function(d) {
return d.name;
})
.style("text-anchor","start")
.attr("transform", function(d) {
return "translate(" + (d.x - 5) + "," + (d.y+10) + ")";
});
}
i++;
});
node.on("click", function(d){
console.log(d.x + "|--|" + d.y);
});
<style type="text/css">
.node {
stroke: #fff;
stroke-width: 1.5px;
cursor: move;
}
.node-active {
stroke: #555;
stroke-width: 1.5px;
}
.link {
stroke: #555;
stroke-opacity: .3;
}
.link-active {
stroke-opacity: 1;
}
.overlay {
fill: none;
pointer-events: all;
}
</style>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body style="background-color: #42f4e5">
<!-- main -->
</body>
I know the reason
I call the drag method in the node, but the node doesn't append the shape, so the drag doesn't work.
so I change the drag invoke position as snippet
var graph = {
"nodes": [{
"appId": "AP110358",
"name": "Customer Account Profile",
"type": "CIRCLE",
"x": 50,
"y": 50
},
{
"appId": "NB",
"name": "NB",
"type": "CIRCLE",
"x": 500,
"y": 500
},
{
"appId": "AP114737",
"name": "RBG",
"type": "CIRCLE",
"x": 300,
"y": 600
},
{
"appId": "NULL",
"name": "Account",
"type": "RECT",
"x": 400,
"y": 700
}
],
"links": [{
"source": "Customer Account Profile",
"target": "NB",
"value": 1,
"label": null
},
{
"source": "NB",
"target": "RBG",
"value": 1,
"label": null
},
{
"source": "RBG",
"target": "Customer Account Profile",
"value": 1,
"label": null
},
{
"source": "NB",
"target": "Customer Account Profile",
"value": 1,
"label": null
},
{
"source": "Customer Account Profile",
"target": "Account",
"value": 1,
"label": null
},
{
"source": "NB",
"target": "Account",
"value": 1,
"label": null
},
{
"source": "RBG",
"target": "Account",
"value": 1,
"label": null
}
]
};
var width = window.innerWidth;
var height = window.innerHeight;
var center;
if (width > 1200) {
center = [(width - 1200) / 2, 0]
} else {
center = [0, 0]
}
var edges = [];
graph.links.forEach(function(e) {
var sourceNode = graph.nodes.filter(function(n) {
return n.name === e.source;
})[0],
targetNode = graph.nodes.filter(function(n) {
return n.name === e.target;
})[0];
edges.push({
source: sourceNode,
target: targetNode,
value: e.value
});
});
var color = d3.scale.category20();
var svg = d3.select("body")
.append("svg")
.attr("width", "1200")
.attr("height", "1000")
.attr("transform", "translate(" + center + ")")
.append("g");
var container = svg.append("g");
var link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(edges)
.enter().append("line")
.attr("class", "link")
.attr("x1", function(l) {
var sourceNode = graph.nodes.filter(function(d) {
return d == l.source
})[0];
d3.select(this).attr("y1", sourceNode.y);
return sourceNode.x
})
.attr("x2", function(l) {
var targetNode = graph.nodes.filter(function(d) {
return d == l.target
})[0];
d3.select(this).attr("y2", targetNode.y);
return targetNode.x
})
.style("stroke-width", function(d) {
return d.value;
});
link.append("title").text(function(d) {
return d.value;
});
var drag = d3.behavior.drag()
.on("drag", function(d) {
d.x += d3.event.dx;
d.y += d3.event.dy;
d3.select(this).attr("cx", d.x).attr("cy", d.y);
link.each(function(l) {
if (l.source == d) {
d3.select(this).attr("x1", d.x).attr("y1", d.y);
} else if (l.target == d) {
d3.select(this).attr("x2", d.x).attr("y2", d.y);
}
});
});
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node");
var i = 0;
node.each(function(d) {
if (d.type == "CIRCLE") {
d3.select(this).append("circle")
.attr("r", 50)
.attr("cx", function(d) {
return d.x
})
.attr("cy", function(d) {
return d.y
})
.call(drag)
.style("fill", function(d) {
return color(i & 3);
});
d3.select(this).append("text")
.text(function(d) {
return d.name;
})
.attr("transform", function(d) {
return "translate(" + d.x + "," + (d.y + 5) + ")";
})
.style("text-anchor", "middle");
} else {
d3.select(this).append("rect")
.attr("height", 40)
.attr("width", 140)
.attr("x", -10)
.attr("y", -(40 / 2))
.attr("transform", function(d) {
return "translate(" + d.x + "," + (d.y + 5) + ")";
})
.style("fill", "green");
d3.select(this).append("text")
.text(function(d) {
return d.name;
})
.style("text-anchor", "start")
.attr("transform", function(d) {
return "translate(" + (d.x - 5) + "," + (d.y + 10) + ")";
});
}
i++;
});
node.on("click", function(d) {
console.log(d.x + "|--|" + d.y);
});
<style type="text/css">.node {
stroke: #fff;
stroke-width: 1.5px;
cursor: move;
}
.node-active {
stroke: #555;
stroke-width: 1.5px;
}
.link {
stroke: #555;
stroke-opacity: .3;
}
.link-active {
stroke-opacity: 1;
}
.overlay {
fill: none;
pointer-events: all;
}
</style>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.1/d3.min.js"></script>
<body style="background-color: #42f4e5">
<!-- main -->
</body>
Following the example on this page: Parallel Coordinates
I'm trying to add the data inside the html page. I'm using the following code:
var cars = [
['AMC Ambassador Brougham',13,8,360,175,3821,11,73],
['AMC Ambassador DPL',15,8,390,190,3850,8.5,70]
];
var dimensions = ['name','economy (mpg)','cylinders','displacement (cc)','power (hp)','weight (lb)','0-60 mph (s)','year'];
But I'm getting a blank screen.
I'm guessing the format of the data is not right.
Could someone advice on how can I format the data inside the html file so that I can display the data correctly.
Thanks
d3.csv will return an array of objects where each row in the file is an object in the array with properties of the CSV header and values of the row values. So to translate that directly to JSON would look like:
var cars = [{
"name": "AMC Ambassador Brougham",
"economy (mpg)": "13",
"cylinders": "8",
"displacement (cc)": "360",
"power (hp)": "175",
"weight (lb)": "3821",
"0-60 mph (s)": "11",
"year": "73"
}, {
"name": "AMC Ambassador DPL",
"economy (mpg)": "15",
"cylinders": "8",
"displacement (cc)": "390",
"power (hp)": "190",
"weight (lb)": "3850",
"0-60 mph (s)": "8.5",
"year": "70"
}];
Running code:
<!DOCTYPE html>
<meta charset="utf-8">
<style>
svg {
font: 10px sans-serif;
}
.background path {
fill: none;
stroke: #ddd;
shape-rendering: crispEdges;
}
.foreground path {
fill: none;
stroke: steelblue;
}
.brush .extent {
fill-opacity: .3;
stroke: #fff;
shape-rendering: crispEdges;
}
.axis line,
.axis path {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.axis text {
text-shadow: 0 1px 0 #fff, 1px 0 0 #fff, 0 -1px 0 #fff, -1px 0 0 #fff;
cursor: move;
}
</style>
<body>
<script src="//d3js.org/d3.v3.min.js"></script>
<script>
var margin = {
top: 30,
right: 10,
bottom: 10,
left: 10
},
width = 960 - margin.left - margin.right,
height = 500 - margin.top - margin.bottom;
var x = d3.scale.ordinal().rangePoints([0, width], 1),
y = {},
dragging = {};
var line = d3.svg.line(),
axis = d3.svg.axis().orient("left"),
background,
foreground;
var svg = d3.select("body").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var cars = [{
"name": "AMC Ambassador Brougham",
"economy (mpg)": "13",
"cylinders": "8",
"displacement (cc)": "360",
"power (hp)": "175",
"weight (lb)": "3821",
"0-60 mph (s)": "11",
"year": "73"
}, {
"name": "AMC Ambassador DPL",
"economy (mpg)": "15",
"cylinders": "8",
"displacement (cc)": "390",
"power (hp)": "190",
"weight (lb)": "3850",
"0-60 mph (s)": "8.5",
"year": "70"
}];
// Extract the list of dimensions and create a scale for each.
x.domain(dimensions = d3.keys(cars[0]).filter(function(d) {
return d != "name" && (y[d] = d3.scale.linear()
.domain(d3.extent(cars, function(p) {
return +p[d];
}))
.range([height, 0]));
}));
// Add grey background lines for context.
background = svg.append("g")
.attr("class", "background")
.selectAll("path")
.data(cars)
.enter().append("path")
.attr("d", path);
// Add blue foreground lines for focus.
foreground = svg.append("g")
.attr("class", "foreground")
.selectAll("path")
.data(cars)
.enter().append("path")
.attr("d", path);
// Add a group element for each dimension.
var g = svg.selectAll(".dimension")
.data(dimensions)
.enter().append("g")
.attr("class", "dimension")
.attr("transform", function(d) {
return "translate(" + x(d) + ")";
})
.call(d3.behavior.drag()
.origin(function(d) {
return {
x: x(d)
};
})
.on("dragstart", function(d) {
dragging[d] = x(d);
background.attr("visibility", "hidden");
})
.on("drag", function(d) {
dragging[d] = Math.min(width, Math.max(0, d3.event.x));
foreground.attr("d", path);
dimensions.sort(function(a, b) {
return position(a) - position(b);
});
x.domain(dimensions);
g.attr("transform", function(d) {
return "translate(" + position(d) + ")";
})
})
.on("dragend", function(d) {
delete dragging[d];
transition(d3.select(this)).attr("transform", "translate(" + x(d) + ")");
transition(foreground).attr("d", path);
background
.attr("d", path)
.transition()
.delay(500)
.duration(0)
.attr("visibility", null);
}));
// Add an axis and title.
g.append("g")
.attr("class", "axis")
.each(function(d) {
d3.select(this).call(axis.scale(y[d]));
})
.append("text")
.style("text-anchor", "middle")
.attr("y", -9)
.text(function(d) {
return d;
});
// Add and store a brush for each axis.
g.append("g")
.attr("class", "brush")
.each(function(d) {
d3.select(this).call(y[d].brush = d3.svg.brush().y(y[d]).on("brushstart", brushstart).on("brush", brush));
})
.selectAll("rect")
.attr("x", -8)
.attr("width", 16);
function position(d) {
var v = dragging[d];
return v == null ? x(d) : v;
}
function transition(g) {
return g.transition().duration(500);
}
// Returns the path for a given data point.
function path(d) {
return line(dimensions.map(function(p) {
return [position(p), y[p](d[p])];
}));
}
function brushstart() {
d3.event.sourceEvent.stopPropagation();
}
// Handles a brush event, toggling the display of foreground lines.
function brush() {
var actives = dimensions.filter(function(p) {
return !y[p].brush.empty();
}),
extents = actives.map(function(p) {
return y[p].brush.extent();
});
foreground.style("display", function(d) {
return actives.every(function(p, i) {
return extents[i][0] <= d[p] && d[p] <= extents[i][1];
}) ? null : "none";
});
}
</script>