Unable to extract graph data (nodes,links) from json in d3.js - javascript

After days of effort, I have finally created my first d3.js layout. But now I want to segregate data and script. I was trying to use JSON to keep data and extract from it. But it didn't work. Please help.
I tried this, but then it started failing:
d3.json("sample.json", function(data) {
var link = svg.append("g")
.attr("class", "links")
.selectAll("line")
.data(data.links)
.enter().append("line")
.attr("stroke-width", function(d) { return Math.sqrt(d.value); });
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("circle")
.data(data.nodes)
.enter().append("circle")
.attr("r", 5)
.attr("fill", function(d) { return color(d.group); })
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
...
)};
Here is the complete code I am working on.
JSFIDDLE : d3_graph_labelled_edge.js

Invalid JSON. After formatting (removing last line commas and replacing single quotes with double):
{
"nodes": [{
"name": "NE1",
"full_name": "NE1",
"type": 2,
"slug": "12.3",
"entity": "company",
"img_hrefD": "",
"img_hrefL": ""
},
{
"name": "NE2",
"full_name": "NE2",
"type": 2,
"slug": "12.4",
"entity": "company",
"img_hrefD": "",
"img_hrefL": ""
},
{
"name": "NE3",
"full_name": "NE3",
"type": 2,
"slug": "12.3",
"entity": "company",
"img_hrefD": "",
"img_hrefL": ""
},
{
"name": "NE4",
"full_name": "NE4",
"type": 2,
"slug": "12.1",
"entity": "company",
"img_hrefD": "",
"img_hrefL": ""
},
{
"name": "NE5",
"full_name": "NE5",
"type": 2,
"slug": "12.0",
"entity": "company",
"img_hrefD": "",
"img_hrefL": ""
}
],
"links": [{
"source": 0,
"target": 1,
"value": 1,
"distance": 15,
"a": "123",
"z": "111"
},
{
"source": 0,
"target": 2,
"value": 1,
"distance": 15,
"a": "123",
"z": "111"
},
{
"source": 0,
"target": 3,
"value": 1,
"distance": 15,
"a": "123",
"z": "111"
},
{
"source": 1,
"target": 2,
"value": 1,
"distance": 15,
"a": "123",
"z": "111"
},
{
"source": 1,
"target": 3,
"value": 1,
"distance": 15,
"a": "123",
"z": "111"
},
{
"source": 2,
"target": 3,
"value": 1,
"distance": 15,
"a": "123",
"z": "111"
},
{
"source": 1,
"target": 4,
"value": 1,
"distance": 15,
"a": "123",
"z": "111"
}
]
}
Here's a plunkr using the above JSON: http://plnkr.co/edit/vRrYyjH3mtPz8vEuTZKx?p=preview
Recommendation: Use JSON Lint for validation.
Hope this helps.

Related

Zooming in a d3js force simulation on a canvas

I've set up a force directed graph with d3.js using svg, but eventually the graph became to big and it's having performance issues. I decided to try to do it in a canvas, because I read it renders stuff muck better and faster. But now I'm having an issue with zooming. I've implemented the zoom behavior correctly (i guess), but i can only zoom when the graph is at rest. Before the simulation find an equilibrium point zoom behavior does not work. Any idea why? Or any tips on what should i do?
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d, i) { return i; }))
.force("charge", d3.forceManyBody().strength( -5 ))
.force("center", d3.forceCenter(width / 2, height / 2));
force.nodes(data.nodes)
.on("tick", ticked)
force.force("link")
.links(data.links);
function ticked(){
context.clearRect(0, 0, width, height);
// Draw the links
data.links.forEach(function(d) {
// Draw a line from source to target.
context.beginPath();
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.stroke();
});
// Draw the nodes
data.nodes.forEach(function(d, i) {
// Draws a complete arc for each node.
context.beginPath();
context.arc(d.x, d.y, d.radius, 0, 2 * Math.PI, true);
context.fill();
});
};
// now the zooming part
canvas.call( d3.zoom().scaleExtent([0.2, 10]).on("zoom", zoomed) )
function zoomed(d) {
context.save();
context.clearRect(0, 0, width, height);
context.translate(d3.event.transform.x, d3.event.transform.y);
context.scale(d3.event.transform.k, d3.event.transform.k);
// Draw the links ...
data.links.forEach(function(d) {
context.beginPath();
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.stroke();
});
// Draw the nodes ...
data.nodes.forEach(function(d, i) {
context.beginPath();
context.arc(d.x, d.y, d.radius, 0, 2 * Math.PI, true);
context.fill();
});
context.restore();
}
You tick function is not aware of any transforms created by the zoom. Best way to do this is to always apply a transform in the tick (which before any zooming is the identity transform). This allows you to reuse the tick method to do all drawing.
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d, i) {
return d.id;
}))
.force("charge", d3.forceManyBody().strength(-5))
.force("center", d3.forceCenter(width / 2, height / 2));
force.nodes(data.nodes)
.on("tick", ticked);
force.force("link")
.links(data.links)
var trans = d3.zoomIdentity; //<-- identity transform
function ticked() {
context.save();
context.clearRect(0, 0, width, height);
context.translate(trans.x, trans.y); //<-- this always applies a transform
context.scale(trans.k, trans.k);
// Draw the links
data.links.forEach(function(d) {
// Draw a line from source to target.
context.beginPath();
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.stroke();
});
// Draw the nodes
data.nodes.forEach(function(d, i) {
// Draws a complete arc for each node.
context.beginPath();
context.arc(d.x, d.y, 5, 0, 2 * Math.PI, true);
context.fill();
});
context.restore();
};
// now the zooming part
canvas.call(d3.zoom().scaleExtent([0.2, 10]).on("zoom", zoomed))
function zoomed(d) {
trans = d3.event.transform; //<-- set to current transform
ticked(); //<-- use tick to redraw regardless of event
}
Full running code:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#4.0.0" data-semver="4.0.0" src="https://d3js.org/d3.v4.min.js"></script>
</head>
<body>
<canvas width="500" height="500"></canvas>
<script>
var width = 500,
height = 500
canvas = document.querySelector("canvas"),
context = canvas.getContext("2d");
canvas = d3.select(canvas);
var data = {
"nodes": [{
"id": "Myriel",
"group": 1
}, {
"id": "Napoleon",
"group": 1
}, {
"id": "Mlle.Baptistine",
"group": 1
}, {
"id": "Mme.Magloire",
"group": 1
}, {
"id": "CountessdeLo",
"group": 1
}, {
"id": "Geborand",
"group": 1
}, {
"id": "Champtercier",
"group": 1
}, {
"id": "Cravatte",
"group": 1
}, {
"id": "Count",
"group": 1
}, {
"id": "OldMan",
"group": 1
}, {
"id": "Labarre",
"group": 2
}, {
"id": "Valjean",
"group": 2
}, {
"id": "Marguerite",
"group": 3
}, {
"id": "Mme.deR",
"group": 2
}, {
"id": "Isabeau",
"group": 2
}, {
"id": "Gervais",
"group": 2
}, {
"id": "Tholomyes",
"group": 3
}, {
"id": "Listolier",
"group": 3
}, {
"id": "Fameuil",
"group": 3
}, {
"id": "Blacheville",
"group": 3
}, {
"id": "Favourite",
"group": 3
}, {
"id": "Dahlia",
"group": 3
}, {
"id": "Zephine",
"group": 3
}, {
"id": "Fantine",
"group": 3
}, {
"id": "Mme.Thenardier",
"group": 4
}, {
"id": "Thenardier",
"group": 4
}, {
"id": "Cosette",
"group": 5
}, {
"id": "Javert",
"group": 4
}, {
"id": "Fauchelevent",
"group": 0
}],
"links": [{
"source": "Napoleon",
"target": "Myriel",
"value": 1
}, {
"source": "Mlle.Baptistine",
"target": "Myriel",
"value": 8
}, {
"source": "Mme.Magloire",
"target": "Myriel",
"value": 10
}, {
"source": "Mme.Magloire",
"target": "Mlle.Baptistine",
"value": 6
}, {
"source": "CountessdeLo",
"target": "Myriel",
"value": 1
}, {
"source": "Geborand",
"target": "Myriel",
"value": 1
}, {
"source": "Champtercier",
"target": "Myriel",
"value": 1
}, {
"source": "Cravatte",
"target": "Myriel",
"value": 1
}, {
"source": "Count",
"target": "Myriel",
"value": 2
}, {
"source": "OldMan",
"target": "Myriel",
"value": 1
}, {
"source": "Valjean",
"target": "Labarre",
"value": 1
}, {
"source": "Valjean",
"target": "Mme.Magloire",
"value": 3
}, {
"source": "Valjean",
"target": "Mlle.Baptistine",
"value": 3
}, {
"source": "Valjean",
"target": "Myriel",
"value": 5
}, {
"source": "Marguerite",
"target": "Valjean",
"value": 1
}, {
"source": "Mme.deR",
"target": "Valjean",
"value": 1
}, {
"source": "Isabeau",
"target": "Valjean",
"value": 1
}, {
"source": "Gervais",
"target": "Valjean",
"value": 1
}, {
"source": "Listolier",
"target": "Tholomyes",
"value": 4
}, {
"source": "Fameuil",
"target": "Tholomyes",
"value": 4
}, {
"source": "Fameuil",
"target": "Listolier",
"value": 4
}, {
"source": "Blacheville",
"target": "Tholomyes",
"value": 4
}, {
"source": "Blacheville",
"target": "Listolier",
"value": 4
}, {
"source": "Blacheville",
"target": "Fameuil",
"value": 4
}, {
"source": "Favourite",
"target": "Tholomyes",
"value": 3
}, {
"source": "Favourite",
"target": "Listolier",
"value": 3
}, {
"source": "Favourite",
"target": "Fameuil",
"value": 3
}, {
"source": "Favourite",
"target": "Blacheville",
"value": 4
}, {
"source": "Dahlia",
"target": "Tholomyes",
"value": 3
}, {
"source": "Dahlia",
"target": "Listolier",
"value": 3
}, {
"source": "Dahlia",
"target": "Fameuil",
"value": 3
}, {
"source": "Dahlia",
"target": "Blacheville",
"value": 3
}, {
"source": "Dahlia",
"target": "Favourite",
"value": 5
}, {
"source": "Zephine",
"target": "Tholomyes",
"value": 3
}, {
"source": "Zephine",
"target": "Listolier",
"value": 3
}, {
"source": "Zephine",
"target": "Fameuil",
"value": 3
}, {
"source": "Zephine",
"target": "Blacheville",
"value": 3
}, {
"source": "Zephine",
"target": "Favourite",
"value": 4
}, {
"source": "Zephine",
"target": "Dahlia",
"value": 4
}, {
"source": "Fantine",
"target": "Tholomyes",
"value": 3
}, {
"source": "Fantine",
"target": "Listolier",
"value": 3
}, {
"source": "Fantine",
"target": "Fameuil",
"value": 3
}, {
"source": "Fantine",
"target": "Blacheville",
"value": 3
}, {
"source": "Fantine",
"target": "Favourite",
"value": 4
}, {
"source": "Fantine",
"target": "Dahlia",
"value": 4
}, {
"source": "Fantine",
"target": "Zephine",
"value": 4
}, {
"source": "Fantine",
"target": "Marguerite",
"value": 2
}]
}
var force = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d, i) {
return d.id;
}))
.force("charge", d3.forceManyBody().strength(-5))
.force("center", d3.forceCenter(width / 2, height / 2));
force.nodes(data.nodes)
.on("tick", ticked);
force.force("link")
.links(data.links)
var trans = d3.zoomIdentity;
function ticked() {
context.save();
context.clearRect(0, 0, width, height);
context.translate(trans.x, trans.y);
context.scale(trans.k, trans.k);
// Draw the links
data.links.forEach(function(d) {
// Draw a line from source to target.
context.beginPath();
context.moveTo(d.source.x, d.source.y);
context.lineTo(d.target.x, d.target.y);
context.stroke();
});
// Draw the nodes
data.nodes.forEach(function(d, i) {
// Draws a complete arc for each node.
context.beginPath();
context.arc(d.x, d.y, 5, 0, 2 * Math.PI, true);
context.fill();
});
context.restore();
};
// now the zooming part
canvas.call(d3.zoom().scaleExtent([0.2, 10]).on("zoom", zoomed))
function zoomed(d) {
trans = d3.event.transform;
ticked();
}
</script>
</body>
</html>

d3.js: How to change a nodes' representation in a force-layout graph

I am trying to expand this force layout example by changing a nodes' shape from circle to rectangle when it is clicked. So I don't want to change any data but just want to replace the corresponding SVG element.
One of my approaches looked like this:
node.on("click", function() {
this.remove();
svg.selectAll(".node").data(graph.nodes).enter().append("rect")
.attr("class", "node")
.attr("width", 5).attr("height", 5)
.style("fill", function(d) { return color(d.group); });
});
So I removed the SVG element from the DOM and rebound the data, adding a rectangle for the now missing node.
Unfortunately this does not work (the force layout does not set any properties on the new element) and I have no idea if this a reasonable approach at all.
Any ideas how to do this properly?
Try this way.
node.on("click", function(d) {
var size = d.weight * 2 + 12;
d3.select(this).select("circle").remove();
d3.select(this).append("rect")
.attr("x", -(size / 2))
.attr("y", -(size / 2))
.attr("height", size)
.attr("width", size)
.style("fill", function(d) {
return color(1 / d.rating);
});
});
Working code snippet -
var graph = {
"nodes": [{
"name": "1",
"rating": 90,
"id": 2951
}, {
"name": "2",
"rating": 80,
"id": 654654
}, {
"name": "3",
"rating": 80,
"id": 6546544
}, {
"name": "4",
"rating": 1,
"id": 68987978
}, {
"name": "5",
"rating": 1,
"id": 9878933
}, {
"name": "6",
"rating": 1,
"id": 6161
}, {
"name": "7",
"rating": 1,
"id": 64654
}, {
"name": "8",
"rating": 20,
"id": 354654
}, {
"name": "9",
"rating": 50,
"id": 8494
}, {
"name": "10",
"rating": 1,
"id": 6846874
}, {
"name": "11",
"rating": 1,
"id": 5487
}, {
"name": "12",
"rating": 80,
"id": "parfum_kenzo"
}, {
"name": "13",
"rating": 1,
"id": 65465465
}, {
"name": "14",
"rating": 90,
"id": "jungle_de_kenzo"
}, {
"name": "15",
"rating": 20,
"id": 313514
}, {
"name": "16",
"rating": 40,
"id": 36543614
}, {
"name": "17",
"rating": 100,
"id": "Yann_YA645"
}, {
"name": "18",
"rating": 1,
"id": 97413
}, {
"name": "19",
"rating": 1,
"id": 97414
}, {
"name": "20",
"rating": 100,
"id": 976431231
}, {
"name": "21",
"rating": 1,
"id": 9416
}, {
"name": "22",
"rating": 1,
"id": 998949
}, {
"name": "23",
"rating": 100,
"id": 984941
}, {
"name": "24",
"rating": 100,
"id": "99843"
}, {
"name": "25",
"rating": 1,
"id": 94915
}, {
"name": "26",
"rating": 1,
"id": 913134
}, {
"name": "27",
"rating": 1,
"id": 9134371
}],
"links": [{
"source": 6,
"target": 5,
"value": 6,
"label": "publishedOn"
}, {
"source": 8,
"target": 5,
"value": 6,
"label": "publishedOn"
}, {
"source": 7,
"target": 1,
"value": 4,
"label": "containsKeyword"
}, {
"source": 8,
"target": 10,
"value": 3,
"label": "containsKeyword"
}, {
"source": 7,
"target": 14,
"value": 4,
"label": "publishedBy"
}, {
"source": 8,
"target": 15,
"value": 6,
"label": "publishedBy"
}, {
"source": 9,
"target": 1,
"value": 6,
"label": "depicts"
}, {
"source": 10,
"target": 1,
"value": 6,
"label": "depicts"
}, {
"source": 16,
"target": 1,
"value": 6,
"label": "manageWebsite"
}, {
"source": 16,
"target": 2,
"value": 5,
"label": "manageWebsite"
}, {
"source": 16,
"target": 3,
"value": 6,
"label": "manageWebsite"
}, {
"source": 16,
"target": 4,
"value": 6,
"label": "manageWebsite"
}, {
"source": 19,
"target": 18,
"value": 2,
"label": "postedOn"
}, {
"source": 18,
"target": 1,
"value": 6,
"label": "childOf"
}, {
"source": 17,
"target": 19,
"value": 8,
"label": "describes"
}, {
"source": 18,
"target": 11,
"value": 6,
"label": "containsKeyword"
}, {
"source": 17,
"target": 13,
"value": 3,
"label": "containsKeyword"
}, {
"source": 20,
"target": 13,
"value": 3,
"label": "containsKeyword"
}, {
"source": 20,
"target": 21,
"value": 3,
"label": "postedOn"
}, {
"source": 22,
"target": 20,
"value": 3,
"label": "postedOn"
}, {
"source": 23,
"target": 21,
"value": 3,
"label": "manageWebsite"
}, {
"source": 23,
"target": 24,
"value": 3,
"label": "manageWebsite"
}, {
"source": 23,
"target": 25,
"value": 3,
"label": "manageWebsite"
}, {
"source": 23,
"target": 26,
"value": 3,
"label": "manageWebsite"
}]
}
var margin = {
top: -5,
right: -5,
bottom: -5,
left: -5
};
var width = 500 - margin.left - margin.right,
height = 400 - margin.top - margin.bottom;
var color = d3.scale.category20();
var force = d3.layout.force()
.charge(-200)
.linkDistance(50)
.size([width + margin.left + margin.right, height + margin.top + margin.bottom]);
var zoom = d3.behavior.zoom()
.scaleExtent([1, 10])
.on("zoom", zoomed);
var drag = d3.behavior.drag()
.origin(function(d) {
return d;
})
.on("dragstart", dragstarted)
.on("drag", dragged)
.on("dragend", dragended);
var svg = d3.select("#map").append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom)
.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.right + ")")
.call(zoom);
var rect = svg.append("rect")
.attr("width", width)
.attr("height", height)
.style("fill", "none")
.style("pointer-events", "all");
var container = svg.append("g");
//d3.json('http://blt909.free.fr/wd/map2.json', function(error, graph) {
force
.nodes(graph.nodes)
.links(graph.links)
.start();
var link = container.append("g")
.attr("class", "links")
.selectAll(".link")
.data(graph.links)
.enter().append("line")
.attr("class", "link")
.style("stroke-width", function(d) {
return Math.sqrt(d.value);
});
var node = container.append("g")
.attr("class", "nodes")
.selectAll(".node")
.data(graph.nodes)
.enter().append("g")
.attr("class", "node")
.attr("cx", function(d) {
return d.x;
})
.attr("cy", function(d) {
return d.y;
})
.call(drag);
node.append("circle")
.attr("r", function(d) {
return d.weight * 2 + 12;
})
.style("fill", function(d) {
return color(1 / d.rating);
});
force.on("tick", function() {
link.attr("x1", function(d) {
return d.source.x;
})
.attr("y1", function(d) {
return d.source.y;
})
.attr("x2", function(d) {
return d.target.x;
})
.attr("y2", function(d) {
return d.target.y;
});
node.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
});
var linkedByIndex = {};
graph.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];
}
node.on("click", function(d) {
var size = d.weight * 2 + 12;
d3.select(this).select("circle").remove();
d3.select(this).append("rect")
.attr("x", -(size / 2))
.attr("y", -(size / 2))
.attr("height", size)
.attr("width", size)
.style("fill", function(d) {
return color(1 / d.rating);
});
});
function dottype(d) {
d.x = +d.x;
d.y = +d.y;
return d;
}
function zoomed() {
container.attr("transform", "translate(" + d3.event.translate + ")scale(" + d3.event.scale + ")");
}
function dragstarted(d) {
d3.event.sourceEvent.stopPropagation();
d3.select(this).classed("dragging", true);
force.start();
}
function dragged(d) {
d3.select(this).attr("cx", d.x = d3.event.x).attr("cy", d.y = d3.event.y);
}
function dragended(d) {
d3.select(this).classed("dragging", false);
}
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.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;
}
#map {
border: 2px #555 dashed;
width: 500px;
height: 400px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<body>
<div id="map"></div>
</body>
You've updated the dom elements, but not the array used by the force layout algorithm, so do this as the last line in the on click function:
graph.nodes = svg.selectAll(".node");
(You might also find without a data key function that random nodes get changed to rectangles rather than the one you clicked)

Angular D3 not Displaying

I am trying to create an Angular D3 force directed graph but for some reason it is not displaying, and there are no errors in the console. Now it currently loads a JSON however I am also trying to figure out how to load in a variable instead.... any help will be appreciated
index.html
<head>
<!--<script src="css/angular.css"></script>-->
</head>
<style>
.node {
stroke: #fff;
stroke-width: 1.5px;
}
.link {
stroke: #999;
stroke-opacity: .6;
}
</style>
<body>
<div ng-controller="MainCtrl">
<d3-bars data="d3Data"></d3-bars>
</div>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.1.4/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.5.12/d3.js"></script>
<script src="js/index.js"></script>
</body>
app.js
app = angular.module("triForce", [])
app.controller("MainCtrl", function($scope, $http) {
$scope.width = 900;
$scope.height = 900;
var color = d3.scale.category20()
var force = d3.layout.force()
.charge(-120)
.linkDistance(30)
.size([$scope.width, $scope.height]);
$scope.data=[{
"nodes": [
{
"name": "hblodget",
"group": 1,
"size": 1,
"image": null
},
{
"name": "DowntownDonna69",
"group": 1,
"size": 20,
"image": "http://pbs.twimg.com/profile_images/636139174672732160/L5cd008s_normal.jpg"
},
{
"name": "PupsherLive",
"group": 1,
"size": 19,
"image": "http://pbs.twimg.com/profile_images/378800000210840839/93a8ba3852a8e20364957eb8b907b6b3_normal.jpeg"
},
{
"name": "PupsherLive",
"group": 1,
"size": 19,
"image": "http://pbs.twimg.com/profile_images/378800000210840839/93a8ba3852a8e20364957eb8b907b6b3_normal.jpeg"
},
{
"name": "PupsherLive",
"group": 1,
"size": 19,
"image": "http://pbs.twimg.com/profile_images/378800000210840839/93a8ba3852a8e20364957eb8b907b6b3_normal.jpeg"
},
{
"name": "PupsherLive",
"group": 1,
"size": 19,
"image": "http://pbs.twimg.com/profile_images/378800000210840839/93a8ba3852a8e20364957eb8b907b6b3_normal.jpeg"
},
{
"name": "PupsherLive",
"group": 1,
"size": 19,
"image": "http://pbs.twimg.com/profile_images/378800000210840839/93a8ba3852a8e20364957eb8b907b6b3_normal.jpeg"
},
{
"name": "PupsherLive",
"group": 1,
"size": 19,
"image": "http://pbs.twimg.com/profile_images/378800000210840839/93a8ba3852a8e20364957eb8b907b6b3_normal.jpeg"
}
],
"links": [
{
"source": 1,
"target": 0,
"value": 1
},
{
"source": 2,
"target": 0,
"value": 1
},
{
"source": 2,
"target": 0,
"value": 1
},
{
"source": 2,
"target": 0,
"value": 1
},
{
"source": 2,
"target": 0,
"value": 1
},
{
"source": 2,
"target": 0,
"value": 1
},
{
"source": 1,
"target": 0,
"value": 1
}
]
}
];
$scope.nodes = graph.nodes;
$scope.links = graph.links;
for(var i=0; i < $scope.links.length ; i++){
$scope.links[i].strokeWidth = Math.round(Math.sqrt($scope.links[i].value))
}
for(var i=0; i < $scope.nodes.length ; i++){
$scope.nodes[i].color = color($scope.nodes[i].group)
}
force
.nodes($scope.nodes)
.links($scope.links)
.on("tick", function(){$scope.$apply()})
.start();
})
I have not dug to far into your code, but at first glance it looks like you are not binding the <d3-bars data="d3Data"></d3-bars> directive to the right variable. It looks like your controller is setting the property $scope.data not $scope.d3Data

D3.js append circle to the GROUP based on the JSON value

Here I'm getting json data as
{
"nodes": [{
"name": "Tomcat",
"comp_type": "tomcat_155:7077",
"id": "tomcat_155:7077",
"pie": true,
"url": "../images/component_icons/1424962275_f-server_128.svg",
"group": 1,
"fixed": true
}, {
"name": "lraj_155_Nov_3(MS SQL)",
"comp_type": "192.168.11.212:1433_Ba",
"id": "lraj_155_Nov_3(MS SQL)",
"pie": false,
"url": "../images/component_icons/1424962160_19.svg",
"group": 2,
"fixed": true
}, {
"name": "rajesh_window",
"comp_type": "192.234.11.116:1433_window",
"id": "rajesh_window",
"pie": false,
"url": "../images/component_icons/1424882359_database.svg",
"group": 3,
"fixed": true
}, {
"name": "shanker_ux_win_3(PS)",
"comp_type": "192.168.11.116:1433_window",
"pie": true,
"id": "shanker_ux_win_3(PS)",
"url": "../images/component_icons/1424882359_database.svg",
"group": 4,
"fixed": true
}],
"links": [{
"source": 1,
"target": 0,
"description": "windows flows",
"value": 1
}, {
"source": 2,
"description": "SQLMS(36.67%)",
"target": 0,
"value": 8
}, {
"source": 1,
"description": "",
"target": 0,
"value": 8
}, {
"source": 3,
"target": 2,
"description": "ctrix 6765",
"value": 1
}]
}
Each node contains PIE which is true or false.
So when i render d3 force layout, If PIE is true a circle have to append to group else no circle have to append.
Please help me out. Thanks in advance.
You can do this using a filter. For example, assuming that you append a g element for each datum and circles only for those for which pie == true:
d3.selectAll("g").data(json.nodes)
.enter().append("g")
.filter(function(d) { return d.pie; })
.append("circle");

D3JS more inteactive network chart node menu

I want to do netwok graph using D3JS, i have prepared force directed graph on jsFiddle.
But when is network more complex, graph become very messy. So I look around and I found somethink what I want implement by d3js. ZoomCharts in this chart on homepage is context menu on every node, child nodes are loading on demand and is possible display another chart on every node (tooltip).
I´m beginer in d3js, so I will be grateful for some advice how to achieve it.
add context menu to nodes
when select some context menu item show another graph (like pie chart)
if it is possible load daata on demand (when user click on parent node)..
If you have ever seen somethink like that (some example code) please share!
Thanks for advice!
Edit1:
I prepare base donut chart for menu. I need add some hover actions and loading icons insted of text labels by icon name in data set. I will be glad if someon help me with this. This menu may helps many people.
Edit2: I updated menu chart, now is supported hover effect. Now it by usefull combine with force directed graph. When someone click on node pie chart menu show.
var dataset = [
{
size: 2,
label: "Item 1"
},
{
size: 1,
label: "Item 2"
},
{
size: 65,
label: "Item 3"
},
{
size: 45,
label: "Item 4"
},
{
size: 50,
label: "Item 5"
}
];
var width = 460,
height = 300,
radius = Math.min(width, height) / 2;
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null)
.value(function(d) { return Object.keys(dataset).length; }); // zde je nutné zadat celkovou populaci - početz prvků v
// Menu
// Arc setting
var arc = d3.svg.arc()
.innerRadius(radius - 100)
.outerRadius(radius - 50);
// Graph space
var svg = d3.select("body").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + width / 2 + "," + height / 2 + ")");
// Prepare graph and load data
var g = svg.selectAll(".arc")
.data(pie(dataset))
.enter().append("g")
.attr("class", "arc");
// Add colors
var path = g.append("path")
.attr("d", arc)
.attr("fill", function (d) { return color(d.data.size);})
// Add labels
var asdfd = g.append("text")
.attr("transform", function(d) { return "translate(" + arc.centroid(d) + ")"; })
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function(d) { return d.data.label; });
var oldColor;
// Add hover action
path.on("mouseenter", function(d,i) {
console.log("mousein"+ d.data.label)
var thisPath = d3.select(this);
oldColor = thisPath.attr("fill"); // save old color
thisPath
.attr("fill", "blue")
.attr("cursor", "pointer")
.attr("class", "on");
})
path.on("mouseout", function(d) {
d3.select(this)
.attr("fill", oldColor)
.attr("class", "off");
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
It will be awsome if combined result will look like this:
**I solved most of this, here is code, but I have performace problem. If someone could help i will be glad. Graph is lagging and i have problem with arrows ind end of the line. **
var json = {
"nodes": [{
"id": -1146034065,
"name": "/",
"group": 0
}, {
"id": -990073683,
"name": "/blog/",
"group": 0
}, {
"id": -1724280020,
"name": "/menu/",
"group": 0
}, {
"id": 1176095248,
"name": "/napojovy-listek/",
"group": 0
}, {
"id": -2085082741,
"name": "/fotogalerie/",
"group": 0
}, {
"id": 883542796,
"name": "/rezervace/",
"group": 0
}, {
"id": 369131020,
"name": "/kontakt/",
"group": 0
}, {
"id": -1276353015,
"name": "/en/",
"group": 0
}, {
"id": -1557747058,
"name": "/o-nas/",
"group": 404
}, {
"id": 890427810,
"name": "/en/about-us/",
"group": 0
}, {
"id": -978700858,
"name": "/en/menu-2/",
"group": 0
}, {
"id": 1436673749,
"name": "/en/napojovy-listek/",
"group": 0
}, {
"id": -489730654,
"name": "/en/photograph/",
"group": 0
}, {
"id": -1461616187,
"name": "/en/reservation/",
"group": 0
}, {
"id": 1520755615,
"name": "/en/contact/",
"group": 0
}, {
"id": 37644686,
"name": "/en//kontakt/",
"group": 0
}, {
"id": 1131720527,
"name": "/en//o-nas/",
"group": 404
}],
"links": [{
"source": -990073683,
"target": -1146034065,
"value": 1
}, {
"source": -1724280020,
"target": -1146034065,
"value": 1
}, {
"source": 1176095248,
"target": -1146034065,
"value": 1
}, {
"source": -2085082741,
"target": -1146034065,
"value": 1
}, {
"source": 883542796,
"target": -1146034065,
"value": 1
}, {
"source": 369131020,
"target": -1146034065,
"value": 1
}, {
"source": -1276353015,
"target": -1146034065,
"value": 1
}, {
"source": -1557747058,
"target": -1146034065,
"value": 1
}, {
"source": 890427810,
"target": -990073683,
"value": 1
}, {
"source": -978700858,
"target": -1724280020,
"value": 1
}, {
"source": 1436673749,
"target": 1176095248,
"value": 1
}, {
"source": -489730654,
"target": -2085082741,
"value": 1
}, {
"source": -1461616187,
"target": 883542796,
"value": 1
}, {
"source": 1520755615,
"target": 369131020,
"value": 1
}, {
"source": 37644686,
"target": -1276353015,
"value": 1
}, {
"source": 1131720527,
"target": -1276353015,
"value": 1
}, {
"source": -1146034065,
"target": -1146034065,
"count": 1,
"value": 1
}, {
"source": -1146034065,
"target": -990073683,
"count": 1,
"value": 1
}, {
"source": -1146034065,
"target": -1724280020,
"count": 3,
"value": 1
}, {
"source": -1146034065,
"target": 1176095248,
"count": 3,
"value": 1
}, {
"source": -1146034065,
"target": -2085082741,
"count": 3,
"value": 1
}, {
"source": -1146034065,
"target": 883542796,
"count": 3,
"value": 1
}, {
"source": -1146034065,
"target": 369131020,
"count": 3,
"value": 1
}, {
"source": -1146034065,
"target": -1276353015,
"count": 1,
"value": 1
}, {
"source": -1146034065,
"target": -1557747058,
"count": 2,
"value": 1
}, {
"source": -990073683,
"target": -1146034065,
"count": 1,
"value": 1
}, {
"source": -990073683,
"target": -990073683,
"count": 1,
"value": 1
}, {
"source": -990073683,
"target": -1724280020,
"count": 1,
"value": 1
}, {
"source": -990073683,
"target": 1176095248,
"count": 1,
"value": 1
}, {
"source": -990073683,
"target": -2085082741,
"count": 1,
"value": 1
}, {
"source": -990073683,
"target": 883542796,
"count": 1,
"value": 1
}, {
"source": -990073683,
"target": 369131020,
"count": 1,
"value": 1
}, {
"source": -990073683,
"target": 890427810,
"count": 1,
"value": 1
}, {
"source": -1724280020,
"target": -1146034065,
"count": 1,
"value": 1
}, {
"source": -1724280020,
"target": -990073683,
"count": 1,
"value": 1
}, {
"source": -1724280020,
"target": -1724280020,
"count": 1,
"value": 1
}, {
"source": -1724280020,
"target": 1176095248,
"count": 1,
"value": 1
}, {
"source": -1724280020,
"target": -2085082741,
"count": 1,
"value": 1
}, {
"source": -1724280020,
"target": 883542796,
"count": 1,
"value": 1
}, {
"source": -1724280020,
"target": 369131020,
"count": 1,
"value": 1
}, {
"source": -1724280020,
"target": -978700858,
"count": 1,
"value": 1
}, {
"source": 1176095248,
"target": -1146034065,
"count": 1,
"value": 1
}, {
"source": 1176095248,
"target": -990073683,
"count": 1,
"value": 1
}, {
"source": 1176095248,
"target": -1724280020,
"count": 1,
"value": 1
}, {
"source": 1176095248,
"target": 1176095248,
"count": 1,
"value": 1
}, {
"source": 1176095248,
"target": -2085082741,
"count": 1,
"value": 1
}, {
"source": 1176095248,
"target": 883542796,
"count": 1,
"value": 1
}, {
"source": 1176095248,
"target": 369131020,
"count": 1,
"value": 1
}, {
"source": 1176095248,
"target": 1436673749,
"count": 1,
"value": 1
}, {
"source": -2085082741,
"target": -1146034065,
"count": 1,
"value": 1
}, {
"source": -2085082741,
"target": -990073683,
"count": 1,
"value": 1
}, {
"source": -2085082741,
"target": -1724280020,
"count": 1,
"value": 1
}, {
"source": -2085082741,
"target": 1176095248,
"count": 1,
"value": 1
}, {
"source": -2085082741,
"target": -2085082741,
"count": 1,
"value": 1
}, {
"source": -2085082741,
"target": 883542796,
"count": 1,
"value": 1
}, {
"source": -2085082741,
"target": 369131020,
"count": 1,
"value": 1
}, {
"source": -2085082741,
"target": -489730654,
"count": 1,
"value": 1
}, {
"source": 883542796,
"target": -1146034065,
"count": 1,
"value": 1
}, {
"source": 883542796,
"target": -990073683,
"count": 1,
"value": 1
}, {
"source": 883542796,
"target": -1724280020,
"count": 1,
"value": 1
}, {
"source": 883542796,
"target": 1176095248,
"count": 1,
"value": 1
}, {
"source": 883542796,
"target": -2085082741,
"count": 1,
"value": 1
}, {
"source": 883542796,
"target": 883542796,
"count": 1,
"value": 1
}, {
"source": 883542796,
"target": 369131020,
"count": 1,
"value": 1
}, {
"source": 883542796,
"target": -1461616187,
"count": 1,
"value": 1
}, {
"source": 369131020,
"target": -1146034065,
"count": 1,
"value": 1
}, {
"source": 369131020,
"target": -990073683,
"count": 1,
"value": 1
}, {
"source": 369131020,
"target": -1724280020,
"count": 1,
"value": 1
}, {
"source": 369131020,
"target": 1176095248,
"count": 1,
"value": 1
}, {
"source": 369131020,
"target": -2085082741,
"count": 1,
"value": 1
}
]
}
var width = 960,
height = 700
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(["a"])
.enter().append("marker")
.attr("id", function(d) { return d; })
.attr("viewBox", "0 -5 10 10")
.attr("refX", 15)
.attr("refY", -1.5)
.attr("markerWidth", 6)
.attr("markerHeight", 6)
.attr("orient", "auto")
.append("path")
.attr("d", "M0,-5L10,0L0,5");
var edges = [];
var fill = d3.scale.category10();
json.links.forEach(function (e) {
// Get the source and target nodes
var sourceNode = json.nodes.filter(function (n) {
return n.id === e.source;
})[0],
targetNode = json.nodes.filter(function (n) {
return n.id === e.target;
})[0],
count = e.count;
// Add the edge to the array
edges.push({
source: sourceNode,
target: targetNode,
count: count,
type: "a"
});
});
var force = d3.layout.force()
.gravity(0.01)
.distance(500)
.charge(-300)
.linkDistance(300)
.size([width, height])
.nodes(json.nodes)
.links(edges)
.start();
var link = svg.append("g").selectAll("link")
.data(edges)
.enter().append("path")
.attr("class", "link")
//.attr("marker-end", function(d) { return "url(#" + d.targetNode + ")"; })
.style("stroke-width", function (d) {
return Math.sqrt(d.count * 1, 5);
});
// Přidáme k uzlu kontextové menu a zvýrazníme sousedy
var node = svg.selectAll("node")
.data(json.nodes)
.enter().append("g")
.attr("class", "node")
.style("fill", function (d) {
return fill(d.group);
})
.call(force.drag).on("mouseover", fade(.1)).on("mouseout", fade(1))
.on("click", function (d, i) {
svg.selectAll(".node").style("fill", function (d) { return fill(d.group);});
d3.select(".menu").remove();
var thisNode = d3.select(this);
thisNode.attr('r', 25).style("fill", "lightcoral");
var menuDataSet = [{
size: 2,
label: "Item 1"
}, {
size: 1,
label: "Item 2"
}, {
size: 65,
label: "Item 3"
}, {
size: 45,
label: "Item 4"
}, {
size: 50,
label: "Item 5"
}];
// Barvy menu
var color = d3.scale.category20();
var pie = d3.layout.pie()
.sort(null)
.value(function (d) {
return Object.keys(menuDataSet).length;
}); // zde je nutné zadat celkovou populaci - početz prvků v
// Menu
var widthMenu = 180,
heightMenu = 180,
radiusMenu = Math.min(widthMenu, heightMenu) / 2;
// Arc setting
var arc = d3.svg.arc()
.innerRadius(radiusMenu - 70)
.outerRadius(radiusMenu - 25);
// Graph space
var svgMenu = thisNode.append("svg")
.attr("width", widthMenu)
.attr("height", heightMenu)
.attr("class","menu")
.attr("x", -90)
.attr("y", -90)
.append("g")
.attr("transform", "translate(" + widthMenu / 2 + "," + heightMenu / 2 + ")");
// Prepare graph and load data
var g = svgMenu.selectAll(".arc")
.data(pie(menuDataSet))
.enter().append("g")
.attr("class", "arc");
// Add colors
var path = g.append("path")
.attr("d", arc)
.attr("fill", function (d) {
return color(d.data.size);
})
// Add labels
var asdfd = g.append("text")
.attr("transform", function (d) {
return "translate(" + arc.centroid(d) + ")";
})
.attr("dy", ".35em")
.style("text-anchor", "middle")
.text(function (d) {
return d.data.label;
});
// Add hover action
path.on("mouseenter", function (d, i) {
var thisPath = d3.select(this);
thisPath.attr("fill", "blue")
.attr("cursor", "pointer")
.attr("class", "on");
})
path.on("mouseout", function (d) {
d3.select(this)
.attr("fill", function (d) {
return color(d.data.size);
})
.attr("class", "off");
});
});
/*
node.append("image")
.attr("xlink:href", "https://github.com/favicon.ico")
.attr("x", -8)
.attr("y", -8)
.attr("width", 16)
.attr("height", 16);
*/
node.append("circle").attr("r", 10);
node.append("text")
.attr("dx", 12)
.attr("dy", ".35em")
.text(function (d) {
return d.name
});
// přidá popisky k hranám
var labels = svg.selectAll('text')
.data(edges)
.enter().append('text')
.attr("x", function (d) {
return (d.source.y + d.target.y) / 2;
})
.attr("y", function (d) {
return (d.source.x + d.target.x) / 2;
})
.attr("text-anchor", "middle")
.text(function (d) {
return d.count;
});
force.on("tick", function () {
link.attr("d", function linkArc(d) {
var dx = d.target.x - d.source.x,
dy = d.target.y - d.source.y,
dr = Math.sqrt(dx * dx + dy * dy);
return "M" + d.source.x + "," + d.source.y + "A" + dr + "," + dr + " 0 0,1 " + d.target.x + "," + d.target.y;
}
);
node.attr("transform", function (d) {
return "translate(" + d.x + "," + d.y + ")";
});
labels.attr("x", function (d) {
return (d.source.x + d.target.x + 10) / 2;
})
.attr("y", function (d) {
return (d.source.y + d.target.y + 10) / 2;
})
});
function transform(d) {
return "translate(" + d.x + "," + d.y + ")";
}
var linkedByIndex = {};
edges.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 fade(opacity) {
return function (d) {
// přidá popisky k hranám
var labels = svg.selectAll('text')
.data(edges)
.enter().append('text')
.attr("x", function (o) {
return (o.source.y + o.target.y) / 2;
})
.attr("y", function (o) {
return (o.source.x + o.target.x) / 2;
})
.attr("text-anchor", "middle")
.text(function (o) {
return o.count;
});
node.style("stroke-opacity", function (o) {
thisOpacity = isConnected(d, o) ? 1 : opacity;
this.setAttribute('fill-opacity', thisOpacity);
return thisOpacity;
});
link.style("stroke-opacity", function (o) {
return o.source === d || o.target === d ? 1 : opacity;
});
};
}
body {
font-family:"Helvetica Neue", Helvetica, Arial, sans-serif;
margin: auto;
position: relative;
width: 960px;
}
.link {
fill: none;
stroke: #666;
stroke-width: 1.5px;
stroke-dasharray: 0,4 1;
}
text {
font: 10px sans-serif;
}
form {
position: absolute;
right: 10px;
top: 10px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>

Categories