Related
I have implemented a layout change on button click so that it goes from the default force layout to radial : https://jsfiddle.net/mvLf579a/278/
It seems to work okay, other than the nodes only take up a quarter of the circle.
Here is the update code :
function radial() {
simulation
.force("charge", d3.forceCollide().radius(10))
.force("r", d3.forceRadial(function(d) {
if (d.id < 5) {
console.log('less than : ', d.id)
return 100;
} else {
console.log('more than : ', d.id)
return 300;
}
}))
.force("center", null)
.on("tick", newTicked);
simulation.alphaTarget(0.3).restart()
function newTicked() {
node
.attr("transform", function(d) {
return "translate(" + d.x + ", " + d.y + ")";
});
}
}
Here is how it ends up :
First of all, passing null to the force in fact removes it. The API is clear about that:
To remove the force with the given name, pass null as the force.
If I understand your problem correctly, you can fix it just by passing the x and y centres to the radial force, which accepts them. Like this:
.force("r", d3.forceRadial(function(d) {
if (d.id < 5) {
return 100;
} else {
return 300;
}
}, width / 2, height / 2))
Also, for a better effect, set strength(1) or a similar high value. Finally, use alpha to reheat the force, not alphaTarget.
Here is your code with those changes (and a smaller SVG):
function radial() {
simulation
.force("charge", d3.forceCollide().radius(10))
.force("r", d3.forceRadial(function(d) {
//console.log(d)
if (d.id < 5) {
return 100;
} else {
return 300;
}
//return d.id < 5 ? 100 : 200;
}, width / 2, height / 2).strength(1))
.force("center", null)
.on("tick", newTicked);
simulation.alpha(1).restart()
function newTicked() {
//node
// .attr("cx", function(d) { return d.x; })
// .attr("cy", function(d) { return d.y; });
node
.attr("transform", function(d) {
return "translate(" + d.x + ", " + d.y + ")";
});
}
}
document.getElementById("radial").addEventListener("click", radial);
var nodes = [{
"id": 1,
"name": "server 1"
},
{
"id": 2,
"name": "server 2"
},
{
"id": 3,
"name": "server 3"
},
{
"id": 4,
"name": "server 4"
},
{
"id": 5,
"name": "server 5"
},
{
"id": 6,
"name": "server 6"
},
{
"id": 7,
"name": "server 7"
},
{
"id": 8,
"name": "server 8"
},
{
"id": 9,
"name": "server 9"
},
{
"id": 2,
"name": "server 2"
},
{
"id": 3,
"name": "server 3"
},
{
"id": 4,
"name": "server 4"
},
{
"id": 5,
"name": "server 5"
},
{
"id": 6,
"name": "server 6"
},
{
"id": 7,
"name": "server 7"
},
{
"id": 8,
"name": "server 8"
},
{
"id": 9,
"name": "server 9"
},
{
"id": 2,
"name": "server 2"
},
{
"id": 3,
"name": "server 3"
},
{
"id": 4,
"name": "server 4"
},
{
"id": 5,
"name": "server 5"
},
{
"id": 6,
"name": "server 6"
},
{
"id": 7,
"name": "server 7"
},
{
"id": 8,
"name": "server 8"
},
{
"id": 9,
"name": "server 9"
},
{
"id": 2,
"name": "server 2"
},
{
"id": 3,
"name": "server 3"
},
{
"id": 4,
"name": "server 4"
},
{
"id": 5,
"name": "server 5"
},
{
"id": 6,
"name": "server 6"
},
{
"id": 7,
"name": "server 7"
},
{
"id": 8,
"name": "server 8"
},
{
"id": 9,
"name": "server 9"
},
{
"id": 2,
"name": "server 2"
},
{
"id": 3,
"name": "server 3"
},
{
"id": 4,
"name": "server 4"
},
{
"id": 5,
"name": "server 5"
},
{
"id": 6,
"name": "server 6"
},
{
"id": 7,
"name": "server 7"
},
{
"id": 8,
"name": "server 8"
},
{
"id": 9,
"name": "server 9"
},
{
"id": 2,
"name": "server 2"
},
{
"id": 3,
"name": "server 3"
},
{
"id": 4,
"name": "server 4"
},
{
"id": 5,
"name": "server 5"
},
{
"id": 6,
"name": "server 6"
},
{
"id": 7,
"name": "server 7"
},
{
"id": 8,
"name": "server 8"
},
{
"id": 9,
"name": "server 9"
},
{
"id": 2,
"name": "server 2"
},
{
"id": 3,
"name": "server 3"
},
{
"id": 4,
"name": "server 4"
},
{
"id": 5,
"name": "server 5"
},
{
"id": 6,
"name": "server 6"
},
{
"id": 7,
"name": "server 7"
},
{
"id": 8,
"name": "server 8"
},
{
"id": 9,
"name": "server 9"
},
{
"id": 2,
"name": "server 2"
},
{
"id": 3,
"name": "server 3"
},
{
"id": 4,
"name": "server 4"
},
{
"id": 5,
"name": "server 5"
},
{
"id": 6,
"name": "server 6"
},
{
"id": 7,
"name": "server 7"
},
{
"id": 8,
"name": "server 8"
},
{
"id": 9,
"name": "server 9"
}
]
var links = [{
source: 1,
target: 2
},
{
source: 1,
target: 3
},
{
source: 1,
target: 4
},
{
source: 2,
target: 5
},
{
source: 2,
target: 6
},
{
source: 3,
target: 7
},
{
source: 5,
target: 8
},
{
source: 6,
target: 9
},
]
var index = 10;
var svg = d3.select("svg"),
width = +svg.attr("width"),
height = +svg.attr("height"),
node,
link;
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) {
return d.id;
}))
.force("charge", d3.forceManyBody())
.force("center", d3.forceCenter(width / 2, height / 2));
update();
function update() {
link = svg.selectAll(".link")
.data(links, function(d) {
return d.target.id;
})
link = link.enter()
.append("line")
.attr("class", "link");
node = svg.selectAll(".node")
.data(nodes, function(d) {
return d.id;
})
node = node.enter()
.append("g")
.attr("class", "node")
.on("click", click)
.call(d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended));
node.append("circle")
.attr("r", 5)
node.append("title")
.text(function(d) {
return d.id;
});
simulation
.nodes(nodes)
.on("tick", ticked);
simulation.force("link")
.links(links);
}
function click(d) {
nodes.push({
id: index,
name: "server " + index
});
links.push({
source: d.id,
target: index
});
index++;
update();
}
function ticked() {
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 + ")";
});
}
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart()
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d.fx = undefined;
d.fy = undefined;
}
/*
var nodes = [].concat(
d3.range(80).map(function() { return {type: "a"}; }),
d3.range(160).map(function() { return {type: "b"}; })
);
var node = d3.select("svg")
.append("g")
.selectAll("circle")
.data(nodes)
.enter().append("circle")
.attr("r", 2.5)
.attr("fill", function(d) { return d.type === "a" ? "brown" : "steelblue"; })
var simulation = d3.forceSimulation(nodes)
.force("charge", d3.forceCollide().radius(5))
.force("r", d3.forceRadial(function(d) { return d.type === "a" ? 100 : 200; }))
.on("tick", ticked);
function ticked() {
node
.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; });
}
*/
.link {
stroke: #aaa;
}
.node {
pointer-events: all;
stroke: none;
stroke-width: 40px;
}
<script src="https://d3js.org/d3.v5.min.js"></script>
<button id='radial'>
Radial
</button>
<svg width="500" height="400" viewBox="-240 -125 960 500">
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.
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)
I'm having trouble with a d3.js bar chart. The full code and working demo are here: https://jsfiddle.net/monotasker/yurqkm3n/. The issue is that when I use the brush on the lower (context) chart, the upper (focus) chart overflows its margins.
Here is the function that the brush activates:
function brushed() {
time.domain(brush.empty() ? navTime.domain() : brush.extent())
.range([0, (width)]);
x.domain(max_extent_in_days(time))
.rangeBands([margin.left, (width)], 0.1, 0);
focus.selectAll('.bar.stack')
.attr('transform', function(d) {return "translate(" + time(d.date) + ",0)"; })
.attr('width', x.rangeBand());
focus.selectAll('.rect')
.attr('width', x.rangeBand());
focus.selectAll('.line').attr('d', line);
focus.select(".x.axis").call(x_axis);
};
One oddity about the chart is that there are two scales that together control the x axis: "time" and "x". The "time" scale is used for positioning the content on the x axis, but the "x" scale (which converts "time" to an ordinal scale) is used for calculating bar widths. I don't know whether this approach is somehow the source of the problem.
Any help is greatly appreciated.
As in this example where Mr. Bostock applies a clip path to the area path, you need to apply one to the bars. The easiest way is to put them in their own g then apply the clip-path to that:
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
...
// Plot the stacked bars
var focus_bar = focus
.append("g") //<-- g just for the bars to be clipped
.attr("class","barClipper")
.style('clip-path', 'url(#clip)') //<-- apply clipping
.selectAll('.g')
.data(data.answer_counts)
.enter().append('g')
.attr('class', 'g bar stack')
.attr('transform', function(d) {
return "translate(" + x(d.date) + ",0)"
});
Full code:
<!DOCTYPE html>
<html>
<head>
<script data-require="d3#3.5.3" data-semver="3.5.3" src="//cdnjs.cloudflare.com/ajax/libs/d3/3.5.3/d3.js"></script>
<style>
#milestones_attempts_combo .context {
/*fill: #efefef; */
}
#milestones_attempts_combo .focus .right,
#milestones_attempts_combo .context .right {
fill: #0064cd;
}
#milestones_attempts_combo .focus .wrong,
#milestones_attempts_combo .context .wrong {
fill: #cd001d;
}
#milestones_attempts_combo .y-axis-label,
#milestones_attempts_combo .y2-axis-label {
fill: #aaa;
font-size: 120%;
}
#milestones_attempts_combo .focus-line,
#milestones_attempts_combo .context-line {
stroke: orange;
stroke-width: 2;
fill-opacity: 0;
}
.brush .extent {
stroke: #efefef;
fill: #666;
fill-opacity: .125;
shape-rendering: crispEdges;
}
.stats-container {
width: 700px;
margin: 0 auto;
}
.chart .axis line,
.chart .axis path,
.chart .tick line {
fill: none;
stroke: #aaa;
shape-rendering: crispEdges;
}
</style>
</head>
<body>
<div id="milestones_attempts_combo"></div>
<script>
(function() {
var myjsondata = {
"answer_counts": [{
"date": "2012-09-13",
"ys": [{
"y1": 9,
"y0": 0,
"class": "right"
}, {
"y1": 12,
"y0": 9,
"class": "wrong"
}],
"total": 12
}, {
"date": "2012-09-16",
"ys": [{
"y1": 16,
"y0": 0,
"class": "right"
}, {
"y1": 17,
"y0": 16,
"class": "wrong"
}],
"total": 17
}, {
"date": "2012-09-17",
"ys": [{
"y1": 12,
"y0": 0,
"class": "right"
}, {
"y1": 14,
"y0": 12,
"class": "wrong"
}],
"total": 14
}, {
"date": "2012-09-19",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-09-20",
"ys": [{
"y1": 12,
"y0": 0,
"class": "right"
}, {
"y1": 16,
"y0": 12,
"class": "wrong"
}],
"total": 16
}, {
"date": "2012-09-21",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 22,
"y0": 20,
"class": "wrong"
}],
"total": 22
}, {
"date": "2012-09-22",
"ys": [{
"y1": 1,
"y0": 0,
"class": "right"
}, {
"y1": 1,
"y0": 1,
"class": "wrong"
}],
"total": 1
}, {
"date": "2012-09-23",
"ys": [{
"y1": 10,
"y0": 0,
"class": "right"
}, {
"y1": 12,
"y0": 10,
"class": "wrong"
}],
"total": 12
}, {
"date": "2012-09-24",
"ys": [{
"y1": 9,
"y0": 0,
"class": "right"
}, {
"y1": 9,
"y0": 9,
"class": "wrong"
}],
"total": 9
}, {
"date": "2012-09-25",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 4,
"y0": 2,
"class": "wrong"
}],
"total": 4
}, {
"date": "2012-09-29",
"ys": [{
"y1": 26,
"y0": 0,
"class": "right"
}, {
"y1": 37,
"y0": 26,
"class": "wrong"
}],
"total": 37
}, {
"date": "2012-10-01",
"ys": [{
"y1": 44,
"y0": 0,
"class": "right"
}, {
"y1": 44,
"y0": 44,
"class": "wrong"
}],
"total": 44
}, {
"date": "2012-10-02",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-10-03",
"ys": [{
"y1": 13,
"y0": 0,
"class": "right"
}, {
"y1": 13,
"y0": 13,
"class": "wrong"
}],
"total": 13
}, {
"date": "2012-10-05",
"ys": [{
"y1": 47,
"y0": 0,
"class": "right"
}, {
"y1": 47,
"y0": 47,
"class": "wrong"
}],
"total": 47
}, {
"date": "2012-10-08",
"ys": [{
"y1": 17,
"y0": 0,
"class": "right"
}, {
"y1": 17,
"y0": 17,
"class": "wrong"
}],
"total": 17
}, {
"date": "2012-10-09",
"ys": [{
"y1": 19,
"y0": 0,
"class": "right"
}, {
"y1": 20,
"y0": 19,
"class": "wrong"
}],
"total": 20
}, {
"date": "2012-10-10",
"ys": [{
"y1": 31,
"y0": 0,
"class": "right"
}, {
"y1": 31,
"y0": 31,
"class": "wrong"
}],
"total": 31
}, {
"date": "2012-10-11",
"ys": [{
"y1": 6,
"y0": 0,
"class": "right"
}, {
"y1": 6,
"y0": 6,
"class": "wrong"
}],
"total": 6
}, {
"date": "2012-10-14",
"ys": [{
"y1": 6,
"y0": 0,
"class": "right"
}, {
"y1": 6,
"y0": 6,
"class": "wrong"
}],
"total": 6
}, {
"date": "2012-10-19",
"ys": [{
"y1": 30,
"y0": 0,
"class": "right"
}, {
"y1": 32,
"y0": 30,
"class": "wrong"
}],
"total": 32
}, {
"date": "2012-10-20",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 22,
"y0": 20,
"class": "wrong"
}],
"total": 22
}, {
"date": "2012-10-23",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 20,
"y0": 20,
"class": "wrong"
}],
"total": 20
}, {
"date": "2012-10-24",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 21,
"y0": 20,
"class": "wrong"
}],
"total": 21
}, {
"date": "2012-10-29",
"ys": [{
"y1": 22,
"y0": 0,
"class": "right"
}, {
"y1": 22,
"y0": 22,
"class": "wrong"
}],
"total": 22
}, {
"date": "2012-11-01",
"ys": [{
"y1": 1,
"y0": 0,
"class": "right"
}, {
"y1": 1,
"y0": 1,
"class": "wrong"
}],
"total": 1
}, {
"date": "2012-11-02",
"ys": [{
"y1": 26,
"y0": 0,
"class": "right"
}, {
"y1": 29,
"y0": 26,
"class": "wrong"
}],
"total": 29
}, {
"date": "2012-11-05",
"ys": [{
"y1": 23,
"y0": 0,
"class": "right"
}, {
"y1": 27,
"y0": 23,
"class": "wrong"
}],
"total": 27
}, {
"date": "2012-11-06",
"ys": [{
"y1": 10,
"y0": 0,
"class": "right"
}, {
"y1": 11,
"y0": 10,
"class": "wrong"
}],
"total": 11
}, {
"date": "2012-11-07",
"ys": [{
"y1": 15,
"y0": 0,
"class": "right"
}, {
"y1": 18,
"y0": 15,
"class": "wrong"
}],
"total": 18
}, {
"date": "2012-11-09",
"ys": [{
"y1": 26,
"y0": 0,
"class": "right"
}, {
"y1": 31,
"y0": 26,
"class": "wrong"
}],
"total": 31
}, {
"date": "2012-11-10",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-11-14",
"ys": [{
"y1": 15,
"y0": 0,
"class": "right"
}, {
"y1": 17,
"y0": 15,
"class": "wrong"
}],
"total": 17
}, {
"date": "2012-11-16",
"ys": [{
"y1": 20,
"y0": 0,
"class": "right"
}, {
"y1": 23,
"y0": 20,
"class": "wrong"
}],
"total": 23
}, {
"date": "2012-11-18",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-11-19",
"ys": [{
"y1": 23,
"y0": 0,
"class": "right"
}, {
"y1": 26,
"y0": 23,
"class": "wrong"
}],
"total": 26
}, {
"date": "2012-11-21",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 3,
"y0": 2,
"class": "wrong"
}],
"total": 3
}, {
"date": "2012-11-23",
"ys": [{
"y1": 9,
"y0": 0,
"class": "right"
}, {
"y1": 10,
"y0": 9,
"class": "wrong"
}],
"total": 10
}, {
"date": "2012-11-26",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 2,
"y0": 2,
"class": "wrong"
}],
"total": 2
}, {
"date": "2012-11-27",
"ys": [{
"y1": 25,
"y0": 0,
"class": "right"
}, {
"y1": 26,
"y0": 25,
"class": "wrong"
}],
"total": 26
}, {
"date": "2012-11-28",
"ys": [{
"y1": 73,
"y0": 0,
"class": "right"
}, {
"y1": 75,
"y0": 73,
"class": "wrong"
}],
"total": 75
}, {
"date": "2012-12-02",
"ys": [{
"y1": 19,
"y0": 0,
"class": "right"
}, {
"y1": 20,
"y0": 19,
"class": "wrong"
}],
"total": 20
}, {
"date": "2012-12-05",
"ys": [{
"y1": 1,
"y0": 0,
"class": "right"
}, {
"y1": 1,
"y0": 1,
"class": "wrong"
}],
"total": 1
}, {
"date": "2013-01-11",
"ys": [{
"y1": 2,
"y0": 0,
"class": "right"
}, {
"y1": 3,
"y0": 2,
"class": "wrong"
}],
"total": 3
}, {
"date": "2013-01-16",
"ys": [{
"y1": 8,
"y0": 0,
"class": "right"
}, {
"y1": 8,
"y0": 8,
"class": "wrong"
}],
"total": 8
}],
"badge_set_reached": [{
"date": "2014-05-24",
"set": 3
}, {
"date": "2014-09-29",
"set": 5
}, {
"date": "2014-11-10",
"set": 6
}, {
"date": "2014-08-29",
"set": 7
}, {
"date": "2015-08-12",
"set": 9
}, {
"date": "2016-01-09",
"set": 9
}]
}
var data = myjsondata;
// preprocess data to get usable date objects
var parse_date = d3.time.format('%Y-%m-%d').parse
for (var i in data) {
var myObj = data[i]
for (var j in myObj) {
myObj[j].date = parse_date(myObj[j].date);
}
}
console.log(data);
// set variables
var margin = {
left: 60,
right: 60,
top: 10,
bottom: 140
},
navMargin = {
top: 300,
right: 60,
bottom: 40,
left: 60
},
height = 400 - margin.top - margin.bottom,
width = 800 - margin.left - margin.right,
navWidth = width, // for context band
navHeight = 400 - navMargin.top - navMargin.bottom,
max_total_counts = d3.max(data.answer_counts, function(d) {
return d.total;
}),
max_extent_dates = d3.extent(data.answer_counts, function(d) {
return d.date;
});
max_extent_in_days = function(timescale) {
return d3.time.days(timescale.domain()[0], d3.time.day.offset(timescale.domain()[1], 1))
};
// scales
var y = d3.scale.linear().domain([0, max_total_counts]).rangeRound([height, 0]),
navY = d3.scale.linear().domain([0, max_total_counts]).rangeRound([navHeight, 0]),
time = d3.time.scale().domain(max_extent_dates).range([0, width]),
navTime = d3.time.scale().domain(max_extent_dates).range([0, width]),
x = d3.scale.ordinal().domain(max_extent_in_days(time)).rangeBands([0, width], 0.1, 0), // used to calculate bar widths
navX = d3.scale.ordinal().domain(max_extent_in_days(navTime)).rangeBands([0, width], 0.1, 0),
y2 = d3.scale.linear().domain([0, d3.max(data.badge_set_reached, function(d) {
return d.set
})]).rangeRound([height, 0]);
navY2 = d3.scale.linear().domain([0, d3.max(data.badge_set_reached, function(d) {
return d.set
})]).rangeRound([navHeight, 0]);
// axes
var x_axis = d3.svg.axis().scale(time).orient('bottom') // .tickFormat(d3.time.format('%Y-%m-%d'))
.outerTickSize(0), // at start and end of axis line
nav_x_axis = d3.svg.axis().scale(navTime).orient('bottom') // .tickFormat(d3.time.format('%Y-%m-%d'))
.outerTickSize(0), // at start and end of axis line
y_axis = d3.svg.axis().scale(y).orient('left').tickFormat(d3.format('d'));
y2_axis = d3.svg.axis().scale(y2).orient('right').tickFormat(d3.format('d'));
// add brush
var brush = d3.svg.brush()
.x(navTime)
.on("brush", brushed);
// svg context
var svg = d3.select("#milestones_attempts_combo")
.append('svg')
.attr('class', 'chart')
.attr('width', width + margin.left + margin.right)
.attr('height', height + margin.top + margin.bottom)
svg.append("defs").append("clipPath")
.attr("id", "clip")
.append("rect")
.attr("width", width)
.attr("height", height);
var focus = svg.append('g')
.attr('class', 'focus')
.attr('transform', 'translate(' + margin.left + ',' + margin.top + ')');
var context = svg.append('g')
.attr('class', 'context')
.attr('transform', 'translate(' + navMargin.left + ',' + navMargin.top + ')');
// Plot the stacked bars
var focus_bar = focus
.append("g")
.attr("class","barClipper")
.style('clip-path', 'url(#clip)')
.selectAll('.g')
.data(data.answer_counts)
.enter().append('g')
.attr('class', 'g bar stack')
.attr('transform', function(d) {
return "translate(" + x(d.date) + ",0)"
});
var focus_rects = focus_bar.selectAll('rect')
.data(function(d) {
return d.ys;
})
.enter().append('rect')
.attr('width', x.rangeBand())
.attr('height', function(d) {
return y(d.y0) - y(d.y1);
})
.attr('y', function(d) {
return y(d.y1);
})
.attr('class', function(d) {
return 'rect ' + d['class'];
});
var context_bar = context.selectAll('.g')
.data(data.answer_counts)
.enter().append('g')
.attr('class', 'g')
.attr('transform', function(d) {
return "translate(" + navTime(d.date) + ",0)"
});
var context_rects = context_bar.selectAll('rect')
.data(function(d) {
return d.ys;
})
.enter().append('rect')
.attr('width', navX.rangeBand())
.attr('height', function(d) {
return navY(d.y0) - navY(d.y1);
})
.attr('y', function(d) {
return navY(d.y1);
})
.attr('class', function(d) {
return 'rect ' + d['class'];
});
// Plot lines
var line = d3.svg.line()
.x(function(d) {
return time(d.date)
})
.y(function(d) {
return y2(d.set)
})
.interpolate('step-after');
var focus_line = focus.append('path')
.datum(data.badge_set_reached)
.attr('class', 'line focus-line')
.attr('d', line);
var line2 = d3.svg.line()
.x(function(d) {
return time(d.date)
})
.y(function(d) {
return navY2(d.set)
})
.interpolate('step-after');
var context_line = context.append('path')
.datum(data.badge_set_reached)
.attr('class', 'line context-line')
.attr('d', line2);
// Plot axes
focus.append('g')
.attr('class', 'x axis')
.attr('transform', 'translate(0, ' + height + ')')
.call(x_axis)
.selectAll('text')
.style('text-anchor', 'end')
// .attr('transform', 'rotate(-45)')
// .attr('dx', '-.5em')
// .attr('dy', '.5em');
focus.append('g')
.attr('class', 'y axis')
.attr('transform', 'translate(0, 0)')
.call(y_axis);
focus.append('g')
.attr('class', 'y2 axis')
.attr('transform', 'translate(' + width + ', 0)')
.call(y2_axis);
context.append('g')
.attr('class', 'navX axis')
.attr('transform', 'translate(0, ' + navHeight + ')')
.call(nav_x_axis);
// Label axes
svg.append('text')
.attr('class', 'label y-axis-label')
.attr('transform', 'rotate(-90)')
.attr('x', 0 - ((height + margin.top) / 2))
.attr('y', 0)
.attr('dy', 20)
.style('text-anchor', 'middle')
.text('Paths Attempted');
svg.append('text')
.attr('class', 'label y2-axis-label')
.attr('transform', 'rotate(+90)')
.attr('y', 0 - (width + margin.left + margin.right))
.attr('x', 0 + ((height + margin.top) / 2))
.attr('dy', '2em')
.style('text-anchor', 'middle')
.text('Badge Set Reached');
// Add brush to svg
context.append('g')
.attr('class', 'x brush')
.call(brush)
.selectAll("rect")
.attr("y", -6)
.attr("height", navHeight + 7);
function brushed() {
time.domain(brush.empty() ? navTime.domain() : brush.extent())
.range([0, (width)]);
x.domain(max_extent_in_days(time))
.rangeBands([margin.left, (width)], 0.1, 0);
focus.selectAll('.bar.stack')
.attr('transform', function(d) {
return "translate(" + time(d.date) + ",0)";
})
.attr('width', x.rangeBand());
focus.selectAll('.rect')
.attr('width', x.rangeBand());
focus.selectAll('.line').attr('d', line);
focus.select(".x.axis").call(x_axis);
};
})();
</script>
</body>
</html>
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>