i read lots of threads and the Mike's blog but i haven't found how to solve my problem.
I'm using the donut chart of D3.js and everything works fine.
I use that code and change the value for each diagram:
var w = 650;
var h = 400;
var r = 150;
var ir = 75;
var textOffset = 24;
var tweenDuration = 1050;
//OBJECTS TO BE POPULATED WITH DATA LATER
var lines, valueLabels, nameLabels;
var pieData = [];
var oldPieData = [];
var filteredPieData = [];
//D3 helper function to populate pie slice parameters from array data
var donut = d3.layout.pie().value(function(d){
return d.itemValue;
});
//D3 helper function to create colors from an ordinal scale
var color = d3.scale.category20c();
//D3 helper function to draw arcs, populates parameter "d" in path object
var arc = d3.svg.arc()
.startAngle(function(d){ return d.startAngle; })
.endAngle(function(d){ return d.endAngle; })
.innerRadius(ir)
.outerRadius(r);
///////////////////////////////////////////////////////////
// GENERATE FAKE DATA /////////////////////////////////////
///////////////////////////////////////////////////////////
var data;
var dataStructure = [
{
"data":[
{
"itemLabel":"Social Media",
"itemValue":90
},
{
"itemLabel":"Blogs",
"itemValue":30
},
{
"itemLabel":"Text Messaging",
"itemValue":60
},
{
"itemLabel":"Email",
"itemValue":90
},
],
"label":"2007"
},
{
"data":[
{
"itemLabel":"Social Media",
"itemValue":80
},
{
"itemLabel":"Blogs",
"itemValue":20
},
{
"itemLabel":"Text Messaging",
"itemValue":70
},
{
"itemLabel":"Email",
"itemValue":90
},
],
"label":"2009"
},
{
"data":[
{
"itemLabel":"Social Media",
"itemValue":70
},
{
"itemLabel":"Blogs",
"itemValue":20
},
{
"itemLabel":"Text Messaging",
"itemValue":70
},
{
"itemLabel":"Email",
"itemValue":90
},
],
"label":"2011"
},
{
"data":[
{
"itemLabel":"Social Media",
"itemValue":60
},
{
"itemLabel":"Blogs",
"itemValue":20
},
{
"itemLabel":"Text Messaging",
"itemValue":70
},
{
"itemLabel":"Email",
"itemValue":90
},
],
"label":"2013"
},
];
///////////////////////////////////////////////////////////
// CREATE VIS & GROUPS ////////////////////////////////////
///////////////////////////////////////////////////////////
var vis = d3.select("#pie-chart").append("svg:svg")
.attr("width", w)
.attr("height", h);
//GROUP FOR ARCS/PATHS
var arc_group = vis.append("svg:g")
.attr("class", "arc")
.attr("transform", "translate(" + (w/2) + "," + (h/2) + ")");
//GROUP FOR LABELS
var label_group = vis.append("svg:g")
.attr("class", "label_group")
.attr("transform", "translate(" + (w/2) + "," + (h/2) + ")");
//GROUP FOR CENTER TEXT
var center_group = vis.append("svg:g")
.attr("class", "center_group")
.attr("transform", "translate(" + (w/2) + "," + (h/2) + ")");
//PLACEHOLDER GRAY CIRCLE
// var paths = arc_group.append("svg:circle")
// .attr("fill", "#EFEFEF")
// .attr("r", r);
///////////////////////////////////////////////////////////
// CENTER TEXT ////////////////////////////////////////////
///////////////////////////////////////////////////////////
//WHITE CIRCLE BEHIND LABELS
var whiteCircle = center_group.append("svg:circle")
.attr("fill", "white")
.attr("r", ir);
///////////////////////////////////////////////////////////
// STREAKER CONNECTION ////////////////////////////////////
///////////////////////////////////////////////////////////
// to run each time data is generated
function update(number) {
data = dataStructure[number].data;
oldPieData = filteredPieData;
pieData = donut(data);
var sliceProportion = 0; //size of this slice
filteredPieData = pieData.filter(filterData);
function filterData(element, index, array) {
element.name = data[index].itemLabel;
element.value = data[index].itemValue;
sliceProportion += element.value;
return (element.value > 0);
}
//DRAW ARC PATHS
paths = arc_group.selectAll("path").data(filteredPieData);
paths.enter().append("svg:path")
.attr("stroke", "white")
.attr("stroke-width", 0.5)
.attr("fill", function(d, i) { return color(i); })
.transition()
.duration(tweenDuration)
.attrTween("d", pieTween);
paths
.transition()
.duration(tweenDuration)
.attrTween("d", pieTween);
paths.exit()
.transition()
.duration(tweenDuration)
.attrTween("d", removePieTween)
.remove();
//DRAW TICK MARK LINES FOR LABELS
lines = label_group.selectAll("line").data(filteredPieData);
lines.enter().append("svg:line")
.attr("x1", 0)
.attr("x2", 0)
.attr("y1", -r-3)
.attr("y2", -r-15)
.attr("stroke", "gray")
.attr("transform", function(d) {
return "rotate(" + (d.startAngle+d.endAngle)/2 * (180/Math.PI) + ")";
});
lines.transition()
.duration(tweenDuration)
.attr("transform", function(d) {
return "rotate(" + (d.startAngle+d.endAngle)/2 * (180/Math.PI) + ")";
});
lines.exit().remove();
//DRAW LABELS WITH PERCENTAGE VALUES
valueLabels = label_group.selectAll("text.value").data(filteredPieData)
.attr("dy", function(d){
if ((d.startAngle+d.endAngle)/2 > Math.PI/2 && (d.startAngle+d.endAngle)/2 < Math.PI*1.5 ) {
return 5;
} else {
return -7;
}
})
.attr("text-anchor", function(d){
if ( (d.startAngle+d.endAngle)/2 < Math.PI ){
return "beginning";
} else {
return "end";
}
})
.text(function(d){
var percentage = (d.value/sliceProportion)*100;
return percentage.toFixed(1) + "%";
});
valueLabels.enter().append("svg:text")
.attr("class", "value")
.attr("transform", function(d) {
return "translate(" + Math.cos(((d.startAngle+d.endAngle - Math.PI)/2)) * (r+textOffset) + "," + Math.sin((d.startAngle+d.endAngle - Math.PI)/2) * (r+textOffset) + ")";
})
.attr("dy", function(d){
if ((d.startAngle+d.endAngle)/2 > Math.PI/2 && (d.startAngle+d.endAngle)/2 < Math.PI*1.5 ) {
return 5;
} else {
return -7;
}
})
.attr("text-anchor", function(d){
if ( (d.startAngle+d.endAngle)/2 < Math.PI ){
return "beginning";
} else {
return "end";
}
}).text(function(d){
var percentage = (d.value/sliceProportion)*100;
return percentage.toFixed(1) + "%";
});
valueLabels.transition().duration(tweenDuration).attrTween("transform", textTween);
valueLabels.exit().remove();
//DRAW LABELS WITH ENTITY NAMES
nameLabels = label_group.selectAll("text.units").data(filteredPieData)
.attr("dy", function(d){
if ((d.startAngle+d.endAngle)/2 > Math.PI/2 && (d.startAngle+d.endAngle)/2 < Math.PI*1.5 ) {
return 17;
} else {
return 5;
}
})
.attr("text-anchor", function(d){
if ((d.startAngle+d.endAngle)/2 < Math.PI ) {
return "beginning";
} else {
return "end";
}
}).text(function(d){
return d.name;
});
nameLabels.enter().append("svg:text")
.attr("class", "units")
.attr("transform", function(d) {
return "translate(" + Math.cos(((d.startAngle+d.endAngle - Math.PI)/2)) * (r+textOffset) + "," + Math.sin((d.startAngle+d.endAngle - Math.PI)/2) * (r+textOffset) + ")";
})
.attr("dy", function(d){
if ((d.startAngle+d.endAngle)/2 > Math.PI/2 && (d.startAngle+d.endAngle)/2 < Math.PI*1.5 ) {
return 17;
} else {
return 5;
}
})
.attr("text-anchor", function(d){
if ((d.startAngle+d.endAngle)/2 < Math.PI ) {
return "beginning";
} else {
return "end";
}
}).text(function(d){
return d.name;
});
nameLabels.transition().duration(tweenDuration).attrTween("transform", textTween);
nameLabels.exit().remove();
}
///////////////////////////////////////////////////////////
// FUNCTIONS //////////////////////////////////////////////
///////////////////////////////////////////////////////////
// Interpolate the arcs in data space.
function pieTween(d, i) {
var s0;
var e0;
if(oldPieData[i]){
s0 = oldPieData[i].startAngle;
e0 = oldPieData[i].endAngle;
} else if (!(oldPieData[i]) && oldPieData[i-1]) {
s0 = oldPieData[i-1].endAngle;
e0 = oldPieData[i-1].endAngle;
} else if(!(oldPieData[i-1]) && oldPieData.length > 0){
s0 = oldPieData[oldPieData.length-1].endAngle;
e0 = oldPieData[oldPieData.length-1].endAngle;
} else {
s0 = 0;
e0 = 0;
}
var i = d3.interpolate({startAngle: s0, endAngle: e0}, {startAngle: d.startAngle, endAngle: d.endAngle});
return function(t) {
var b = i(t);
return arc(b);
};
}
function removePieTween(d, i) {
s0 = 2 * Math.PI;
e0 = 2 * Math.PI;
var i = d3.interpolate({startAngle: d.startAngle, endAngle: d.endAngle}, {startAngle: s0, endAngle: e0});
return function(t) {
var b = i(t);
return arc(b);
};
}
function textTween(d, i) {
var a;
if(oldPieData[i]){
a = (oldPieData[i].startAngle + oldPieData[i].endAngle - Math.PI)/2;
} else if (!(oldPieData[i]) && oldPieData[i-1]) {
a = (oldPieData[i-1].startAngle + oldPieData[i-1].endAngle - Math.PI)/2;
} else if(!(oldPieData[i-1]) && oldPieData.length > 0) {
a = (oldPieData[oldPieData.length-1].startAngle + oldPieData[oldPieData.length-1].endAngle - Math.PI)/2;
} else {
a = 0;
}
var b = (d.startAngle + d.endAngle - Math.PI)/2;
var fn = d3.interpolateNumber(a, b);
return function(t) {
var val = fn(t);
return "translate(" + Math.cos(val) * (r+textOffset) + "," + Math.sin(val) * (r+textOffset) + ")";
};
}
$( "#slider" ).slider({
value: 0,
min: 0,
max: 3,
step: 1,
slide: function( event, ui ) {
update(ui.value);
console.log(ui.value);
}
})
.each(function() {
//
// Add labels to slider whose values
// are specified by min, max and whose
// step is set to 1
//
// Get the options for this slider
var opt = $(this).data().uiSlider.options;
// Get the number of possible values
var vals = opt.max - opt.min;
// Space out values
for (var i = 0; i <= vals; i++) {
var el = $('<label>'+dataStructure[i].label+'</label>').css('left',(i/vals*100)+'%');
$( "#slider" ).append(el);
}
});
update(0);
Here is the Jsfiddle of the diagram i'm using:
jsfiddle.net/brusasu/AqP73/
I created two html page, each for the diagrams with the value i need to show.
My question is how to edit the code in a way where i can use the two diagrams in the same html page.
Thanks in advance
Oki Doki. I'm no expert in these things, but looking at the css in your jsfiddle, it appears to be missing the styles that you might want for the divs "slidercontainer2", "pie-chart2 and "slider2".
Make sure that you have these properly duplicated and that will at least get you closer.
Related
The sample JSON file containing the data looks like this :
services {
service name1: []
exposed interfaces: []
consumed interfaces: []
},
{
service name2: []
exposed interfaces: []
consumed interfaces: []
},
{
service name3: []
exposed interfaces: []
consumed interfaces: []
}
interfaces {
interface name1:
protocol stack:[]
},
interfaces {
interface name1:
protocol stack:[]
},
interfaces {
interface name1:
protocol stack:[]
}
I am trying to connect the services with their consumed interfaces, and exposed interfaces and the problem I am facing is with links, I am either able to connect only a few of the links of the services and other services and nodes are left without any connections even if they are related with interfaces and due this i guess force simulation is also not working and all the nodes are just lying in the center, the code here is as follows:
```
// set the dimensions and margins of the graph
var margin = {top: 10, right: 10, bottom: 10, left: 30},
width = 1200 - margin.left - margin.right,
height = 700 - margin.top - margin.bottom;
servicesColor = "green"
interfacesColor = "brown"
inputColor = "blue"
outputColor = "pink"
var sourceData = {}
suggestionsElement = document.getElementById("suggestions")
function dataPreProcessing(data){
console.log("processing the services");
data.services = data.services.map(d => {
d.type = "services"
d.id=d.name
d.clickToggle = false;
return d;
});
console.log("processing the interfaces");
data.interfaces = data.interfaces.map(d => {
d.type = "interfaces"
d.id=d.name
d.clickToggle = false;
return d;
});
data.nodes = data.services;
data.nodes = data.nodes.concat(data.interfaces);
data.links=[]
data.nodes.forEach(d=>{
if(d.type ==="services"){
d.exposedInterfaces.forEach(b=>{
data.links.push(
{
"source": b,
"target": d.id
}
)
})
d.consumedInterfaces.forEach(b=>{
data.links.push(
{
"source": d.id,
"target": b
}
)
})
}
})
return data;
}
function visualize(data) {
//dataPreProcessing(data);
// Initialize the links
// append the svg object to the body of the page
var svg = d3.select("#vizBox")
.append("svg")
.attr("width", width + margin.left + margin.right)
.attr("height", height + margin.top + margin.bottom).call(d3.zoom().on("zoom",
function () {
svg.attr("transform", d3.event.transform)
}))
.append("g");
// Let's list the force we wanna apply on the network
var simulation = d3.forceSimulation()
.force("link", d3.forceLink().id(function(d) { return d.id; }))
.force("charge", d3.forceManyBody().strength(-500))
.force("center", d3.forceCenter(width / 2, height / 2));
function dragstarted(d) {
if (!d3.event.active) simulation.alphaTarget(0.3).restart();
simulation.force("link", null).force("charge", null).force("center", null);
d.fx = d.x;
d.fy = d.y;
}
function dragged(d) {
d.fx = d3.event.x;
d.fy = d3.event.y;
}
function dragended(d) {
if (!d3.event.active) simulation.alphaTarget(0);
d3.event.x = null;
d3.event.fy = null;
}
// tooltip
tooltip = d3.select("body").append("div").style("width","fit-
content").style("height","auto").style("background","#f8f8f8")
.style("opacity","1").style("position","absolute").style("visibility","hidden").style("box-
shadow","0px 0px 6px #7861A5").style("padding","10px");
var link = svg
.selectAll("line")
.data(data.links)
.enter()
.append("line")
.style("stroke", "black")
// Initialize the nodes
var node = svg.append("g")
.attr("class", "nodes")
.selectAll("g")
.data(data.nodes)
.enter().append("g");
var circles = node.append("circle")
.attr("r", 10)
.attr("fill",function(d){return d.type==="services"? servicesColor:
interfacesColor});
// Create a drag handler and append it to the node object instead
var drag_handler = d3.drag()
.on("start", dragstarted)
.on("drag", dragged)
.on("end", dragended);
drag_handler(node);
var lables = node.append("text")
.text(function(d) {
return d.id;
})
.attr('x', 12)
.attr('y', 3);
node.on("click", (d) => {
if(!d.clickToggle){
tooltip.style("visibility","visible")
.style("top",(d3.event.pageY-30)+"px").style("left",(d3.event.pageX+20)+"px");
let aTags = d.type === "services" ? "<a Source Documentation</a> <br/>": "";
let htmlValue = "<strong>Name: </strong>"+d.name+"<br/><strong>Type:
</strong>"+d.type+" <br/>"+aTags+"<strong>JSON:</strong> <br/><div style=\"width: 300px;
height: 300px; overflow: scroll;\"><pre><code >"+hljs.highlightAuto(JSON.stringify(d,null,
2)).value+"</code></pre></div>";
tooltip.html(htmlValue);
d.clickToggle = true;
}
else{
tooltip.style("visibility","hidden");
d.clickToggle = false;
}
if(d.type == "services"){
svg.selectAll("circle").style("fill", (iterNode)=>{
if(d.exposedInterfaces.length == 0){
iterNode.inUse = false;
return iterNode.type=== "services"? servicesColor: interfacesColor;
}
if(d.exposedInterfaces.some((x) => x == iterNode.id)){
iterNode.inUse = true;
return outputColor;
}else{
iterNode.inUse = false;
return iterNode.type=== "services"? servicesColor: interfacesColor;
}
});
svg.selectAll("circle").style("fill", (iterNode)=>{
if( iterNode.inUse){
return outputColor;
}
if(d.consumedInterfaces.length == 0){
return iterNode.type=== "services"? servicesColor: interfacesColor;
}
if(d.consumedInterfaces.some((x) => x == iterNode.id)){
return inputColor;
}else{
return iterNode.type=== "services"? servicesColor: interfacesColor;
}
});
}
})
simulation
.nodes(data.nodes)
.on("tick", ticked);
simulation.force("link")
.links(data.links);
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 + ")";
})
}
}
d3.json("/data/api.json",(data) => {
sourceData = data;
data = window.dataPreProcessing(data);
visualize(data);
});
function suggest(value){
filteredData = [];
if(!!value){
filteredData = sourceData.nodes.filter((d)=> {
return d.name.toLowerCase().indexOf(value.toLowerCase()) >= 0;
})
}
filteredData = filteredData.slice(0, 3);
console.log(filteredData, value, filteredData.length);
if(filteredData.length === 0){
d3.select("#suggestions").html("");
d3.select("#suggestions").style("opacity", "0")
setVisibility("none");
return;
}
else{
d3.select("#suggestions").html("");
d3.select("#suggestions").selectAll("div")
.data(filteredData)
.enter()
.append("div")
.attr("id","suggestionItem")
.text((d)=> d.name).on("click",(d) => {
d3.select("#suggestions").html("");
SearchData = {};
SearchData.nodes = [d];
SearchData.links = [d.exposedInterfaces, d.consumedInterfaces];
d3.select("#vizBox").html("");
document.getElementById("searchInput").value = d.name;
visualize(SearchData);
});
}
}
function change(){
var value = document.getElementById("searchInput").value;
suggest(value);
setVisibility("block");
d3.select("#suggestions").style("opacity", "1")
}
function inputFocusOut(){
setVisibility("none");
d3.select("#suggestions").style("opacity", "0")
}
function setVisibility(value){
suggestionsElement.style.display= value;
}
function reset(){
d3.select("#vizBox").html("");
document.getElementById("searchInput").value = "";
setTimeout(d3.json("/data/api.json",(data) => {
data = dataPreProcessing(data);
visualize(sourceData);
}), 5000);
}
```
I'm working with some D3 examples in php driven pages. I've found a great example I want to use of a line chart with inline lables (full code here: https://bl.ocks.org/mbostock/4b66c0d9be9a0d56484e), but I can't figure out how to transition the code from a tsv import to an array provided directly from the database.
I am obviously providing the data directly like this:
var data = [
{date:2009, Apples:130, Bananas:40},
{date:2010, Apples:137, Bananas:58},
{date:2011, Apples:166, Bananas:97},
{date:2012, Apples:154, Bananas:117},
{date:2013, Apples:179, Bananas:98},
{date:2014, Apples:187, Bananas:120},
{date:2015, Apples:189, Bananas:84}
]
And then I'm trying to replace this chunk of code that handles the import and the sorting into an array automatically.
d3.requestTsv("data.tsv", function(d) {
d.date = parseTime(d.date);
for (var k in d) if (k !== "date") d[k] = +d[k];
return d;
}, function(error, data) {
if (error) throw error;
var series = data.columns.slice(1).map(function(key) {
return data.map(function(d) {
return {
key: key,
date: d.date,
value: d[key]
};
});
});
But I think I'm having problems replicating the portion that creates the series.
I've tried several variations of this:
var series = data.map(function(key) {
return data.map(function(d) {
return {
key: key,
date: d.date,
value: d[key]
};
});
});
followed with a function to set the data type at the end of the code:
function type(d) {
d.date = parseTime(d.date);
for (var k in d) if (k !== "date") d[k] = +d[k];
return d;
}
But nothing seems to work. I'm sure there is something simple I'm missing, but what should I specifically be changing here to use code by providing the data directly in an array instead of a tsv import?
Your data array is correct (regarding the TSV in Bostock's code).
However, you have two problems:
The d3.tsv function creates an array property named columns. Since you're ditching d3.tsv and using a variable to store the data, you'll have to create that array yourself:
data.columns = ["date", "Apples", "Bananas"]
The d3.tsv accepts a row function. Again, since you're using a variable to store the data, you'll have to use a forEach to do what the row function does in Bostock's code:
data.forEach(d=>{
d.date = parseTime(d.date);
for (var k in d) if (k !== "date") d[k] = +d[k];
});
Here is the updated code using a variable to store the data: https://bl.ocks.org/anonymous/749f2c5bc6a42d68bca3ec579646ff1d
And here the same code in the Stack snippet:
<style>
text {
font: 10px sans-serif;
}
.axis path,
.axis line {
fill: none;
stroke: #000;
shape-rendering: crispEdges;
}
.line {
fill: none;
stroke-width: 1.5px;
}
.label {
text-anchor: middle;
}
.label rect {
fill: white;
}
.label-key {
font-weight: bold;
}
</style>
<svg width="960" height="500"></svg>
<script src="//d3js.org/d3.v4.0.0-alpha.9.min.js"></script>
<script>
var parseTime = d3.timeParse("%Y");
var svg = d3.select("svg");
var margin = {top: 30, right: 50, bottom: 30, left: 30},
width = +svg.attr("width") - margin.left - margin.right,
height = +svg.attr("height") - margin.top - margin.bottom,
labelPadding = 3;
var g = svg.append("g")
.attr("transform", "translate(" + margin.left + "," + margin.top + ")");
var data = [
{date:2009, Apples:130, Bananas:40},
{date:2010, Apples:137, Bananas:58},
{date:2011, Apples:166, Bananas:97},
{date:2012, Apples:154, Bananas:117},
{date:2013, Apples:179, Bananas:98},
{date:2014, Apples:187, Bananas:120},
{date:2015, Apples:189, Bananas:84}
];
data.columns = ["date", "Apples", "Bananas"]
data.forEach(d=>{
d.date = parseTime(d.date);
for (var k in d) if (k !== "date") d[k] = +d[k];
});
var series = data.columns.slice(1).map(function(key) {
return data.map(function(d) {
return {
key: key,
date: d.date,
value: d[key]
};
});
});
var x = d3.scaleTime()
.domain([data[0].date, data[data.length - 1].date])
.range([0, width]);
var y = d3.scaleLinear()
.domain([0, d3.max(series, function(s) { return d3.max(s, function(d) { return d.value; }); })])
.range([height, 0]);
var z = d3.scaleCategory10();
g.append("g")
.attr("class", "axis axis--x")
.attr("transform", "translate(0," + height + ")")
.call(d3.axisBottom(x));
var serie = g.selectAll(".serie")
.data(series)
.enter().append("g")
.attr("class", "serie");
serie.append("path")
.attr("class", "line")
.style("stroke", function(d) { return z(d[0].key); })
.attr("d", d3.line()
.x(function(d) { return x(d.date); })
.y(function(d) { return y(d.value); }));
var label = serie.selectAll(".label")
.data(function(d) { return d; })
.enter().append("g")
.attr("class", "label")
.attr("transform", function(d, i) { return "translate(" + x(d.date) + "," + y(d.value) + ")"; });
label.append("text")
.attr("dy", ".35em")
.text(function(d) { return d.value; })
.filter(function(d, i) { return i === data.length - 1; })
.append("tspan")
.attr("class", "label-key")
.text(function(d) { return " " + d.key; });
label.append("rect", "text")
.datum(function() { return this.nextSibling.getBBox(); })
.attr("x", function(d) { return d.x - labelPadding; })
.attr("y", function(d) { return d.y - labelPadding; })
.attr("width", function(d) { return d.width + 2 * labelPadding; })
.attr("height", function(d) { return d.height + 2 * labelPadding; });
</script>
I am trying to use dynamic input to a draw function in d3js. So when the user changes the csv it would remove the current selection and draw the visualization for the new input. So my question is would I be using a onChange function with the select and then within this function parse the csv and call for the draw function.The current working code is here in plunker:
https://plnkr.co/edit/AjVBK3rTOF5aI4eDDbV5?p=preview
<svg width="1250" height="1080"></svg>
<script src="https://d3js.org/d3.v4.min.js"></script>
<script>
var svg = d3.select("svg"),
width = +svg.attr("width");
var format = d3.format(",d");
var color = d3.scaleOrdinal(d3.schemeCategory10);
var pack = d3.pack()
.size([width, width])
.padding(1.5);
var inputs = {};
function selectCity(){
//storing the drop-dsown selection in the ddSelection var
var ddSelection = document.getElementById("city").value;
//feeding that to create the csv filename you want
var str1 = ddSelection;
var str2 = ".csv";
var csvFile = str1.concat(str2);
str1.concat(str2);
console.log(csvFile);
d3.csv(csvFile, function(d) {
d.sno = +d.sno;
return d;
}, function(error, data) {
if (error) throw error;
d3.selectAll("input").on("change", function(){
inputs[this.id] = +this.value;
console.log(inputs.myValue + "-" + inputs.myRating)
if(inputs.myValue && inputs.myRating){
var classes = data.filter(d => d.value < inputs.myValue && d.rating >= inputs.myRating);
draw(classes);
}
})
function draw(classes) {
console.log(classes.length);
var root = d3.hierarchy({
children: classes
})
.sum(function(d) {
return d.value;
})
.each(function(d) {
if (id = d.data.id) {
var id, i = id.lastIndexOf(".");
d.id = id;
d.package = id.slice(0, i);
d.class = id.slice(i + 1);
}
});
var node = svg.selectAll(".node")
.data(pack(root).leaves())
.enter().append("g")
.attr("class", "node")
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
node.append("circle")
.attr("id", function(d) {
return d.id;
})
.attr("r", function(d) {
return d.r;
})
.style("fill", function(d) {
return color(d.package);
});
node.append("clipPath")
.attr("id", function(d) {
return "clip-" + d.id;
})
.append("use")
.attr("xlink:href", function(d) {
return "#" + d.id;
});
node.append("text")
.attr("clip-path", function(d) {
return "url(#clip-" + d.id + ")";
})
.selectAll("tspan")
.data(function(d) {
return d.class.split(/(?=[A-Z][^A-Z])/g);
})
.enter().append("tspan")
.attr("x", 0)
.attr("y", function(d, i, nodes) {
return 13 + (i - nodes.length / 2 - 0.5) * 10;
})
.text(function(d) {
return d;
});
node.append("title")
.text(function(d) {
return d.data.id + "\n" + format(d.value);
});
}
});
}
</script>
</div>
Here is one example how to do it: http://www.d3noob.org/2014/04/using-html-inputs-with-d3js.html
You don't have to redraw everything but update certain elements.
I don't understand your part about changing the CSV. The user does not change the CSV but your visual output is depending on some user data. So yes, within the callback function of d3.csv(), you write your code that calls some kind of a draw function. But the draw function does not have to be defined there. You can write the function outside and just call it there. This increased the readable of your code dramatically. ;)
I have different <div> and i am trying to put in some d3 <script>
Here are my div
<div id="pagepiling">
<div id = "frequence" class="section" id="section1"></div>
<div id = "menu" class="section" id="section2"></div>
<div class="section" id="section3"></div>
<div class="section" id="section4"></div>
</div>
and here my d3 for #frequence
<script>
var frequ = [];
d3.xml("database_menus.xml", "application/xml", function(xml) {
var dataset = [];
var data = xml.documentElement,
sections = data.getElementsByTagName("menubar");
for (var i = 0, lenS = sections.length; i < lenS; i++) {
nom = sections[i].getAttribute("appname");
dataset.push(nom);
frequ[nom] = (Math.random()*20)+15;
}
var fill = d3.scale.category20b();
var width = window.innerWidth,
height = window.innerHeight;
d3.layout.cloud().size([width,height])
.words(dataset.map(function(d) {return {text: d, size: frequ[d]}}))
.padding(5)
.rotate(function() { return ~~(Math.random() * 2)})
.font("Impact")
.fontSize(function(d) { return d.size; })
.on("end", draw)
.start();
function draw(words) {
words.sort(compare);
var text = d3.select("#frequence").append("svg")
.attr("width", width)
.attr("height", height)
.attr("x", 500)
.append("g")
.attr("transform", "translate("+(width/2)+","+(height/2)+")")
.selectAll("text")
.data(words)
.enter().append("text")
.style("font-size", function(d) { return d.size + "px"; })
.style("font-family", "Berlin Sans Fb")
.style("fill", function(d, i) { return fill(i); })
.attr("text-anchor", "middle")
.style("text-anchor", "middle")
.on("click", function(d){ var url ='strt_visu_boxes.html?app=';
//modifier " " en "_"
var t ="";
for (var i=0; i<d.text.length;i++){
if (d.text[i]==" "){
t += "_";
} else {
t += d.text[i];
}
}
url += t;
//window.location = url;
})
.on("mouseover", function(d) {
d3.select(this).style("font-size", function(d) {
if (d.size>20){
return d.size*1.5 + "px";
} else {
return d.size*2 + "px";
} })})
.on("mouseout", function(d) { d3.select(this).style("font-size", function(d) { return d.size + "px"; })})
.text(function(d) { return d.text; });
//fly-in animation
text.transition().duration(3000).attr("transform", function(d) {return "translate(" + [d.x, d.y] + ")";}).ease("elastic")
}
function mouseover() {
d3.select(this).select("circle").transition()
.duration(750)
.style("font-size", 50);
}
function mouseout() {
d3.select(this).select("circle").transition()
.duration(750)
.style("font-size", 10);
}
function compare(a,b) {
if (a.text < b.text)
return -1;
if (a.text > b.text)
return 1;
return 0;
}
});
</script>
I can not see what i am doing wrong...
(I have my code in this order between ..)
I am new to D3 and working on a force directed graph where the json data is dynamic. I am able to change the force graph upon receiving new data but that happens with a springing effect. The code that creates my force graph is :
<div class="graph"></div>
<script>
var w = 660,
h = 700,
r = 10;
var vis = d3.select(".graph")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("pointer-events", "all")
.append('svg:g')
.call(d3.behavior.zoom().on("zoom", redraw))
.append('svg:g');
vis.append('svg:rect')
.attr('width', w)
.attr('height', h)
.attr('fill', 'rgba(1,1,1,0)');
function redraw() {
console.log("here", d3.event.translate, d3.event.scale);
vis.attr("transform", "translate(" + d3.event.translate + ")" +
" scale(" + d3.event.scale + ")");
};
var force = d3.layout.force()
.gravity(.05)
.charge(-200)
.linkDistance( 260 )
.size([w, h]);
var svg = d3.select(".text")
.append("svg")
.attr("width", w)
.attr("height", h);
d3.json(graph, function(json) {
var nodeList = json.nodes;
var link = vis.selectAll("line")
.data(json.links)
.enter()
.append("line")
.attr("stroke-opacity", function(d) {
if(d.label == 'is a') {
return '0.8';
} else {
return '0.2';
};
})
.attr("stroke-width", function(d) {
if(d.value !== null) {
return d.value;
} else {
return 2;
};
})
.style("stroke", function(d) {
if(d.color !== null) {
return d.color;
};
})
.on("mouseover", function() {
d3.select(this)
.style("stroke", "#999999")
.attr("stroke-opacity", "1.0");
})
.on("mouseout", function() {
d3.select(this)
.style("stroke", function(d) {
if(d.color !== null) {
return d.color;
};
})
.attr("stroke-opacity", function(d) {
if(d.label == 'is a') {
return '0.8';
} else {
return '0.2';
};
})
});
link.append("title")
.text(function(d) { return d.label } );
var node = vis.selectAll("g.node")
.data(json.nodes)
.enter()
.append("svg:g")
.attr("class","node")
.call(force.drag);
node.append("svg:circle")
.attr("r", function(d) {
if (d.size > 0) {
return 10+(d.size*2);
} else {
return 10;
}
})
.attr("id", function(d) { return "Node;"+d.id; } )
.style("fill", function(d) {
if(d.style == 'filled') {
return d.color;
};
})
.style("stroke", function(d) {
if(d.style !== 'filled') {
return d.color;
};
})
.style("stroke-width", "2")
.on("mouseover", function() {
d3.select(this).style("fill", "#999");
fade(.1);
})
.on("mouseout", function(d) {
if (d.style == 'filled') {
d3.select(this).style("fill",d.color);fade(1);
} else {
d3.select(this).style("stroke",d.color);
d3.select(this).style("fill","black");
}
fade(1);
});
node.append("title")
.text(function(d) { return d.Location; } );
force.nodes(json.nodes)
.links(json.links)
.on("tick", tick)
.alpha(1)
.start();
function tick() {
node.attr("cx", function(d) { return d.x; })
.attr("cy", function(d) { return d.y; })
.attr("transform", function(d) {
return "translate(" + d.x + "," + d.y + ")";
});
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; });
}
});
</script>
I am able to create a new graph when a new json string is received by recalling the whole function again. This creates a new graph in place of the old. I am unable to update the old graph with the new set of values as the values are received; the nodes in my graph do not change, just the relation among them changes.
I did stumble upon an example (http://bl.ocks.org/1095795) where a new node is deleted and recreated, but the implementation is a bit different.
Any pointers or help will be really appreciated.
Well I could find the solution browsing through, posting it here for anyone needing help on this topic. The idea is to create an object of the graph and playing around with the nodes and links arrays.
The JS code goes as:
var graph;
function myGraph(el) {
// Add and remove elements on the graph object
this.addNode = function (id) {
nodes.push({"id":id});
update();
};
this.removeNode = function (id) {
var i = 0;
var n = findNode(id);
while (i < links.length) {
if ((links[i]['source'] == n)||(links[i]['target'] == n))
{
links.splice(i,1);
}
else i++;
}
nodes.splice(findNodeIndex(id),1);
update();
};
this.removeLink = function (source,target){
for(var i=0;i<links.length;i++)
{
if(links[i].source.id == source && links[i].target.id == target)
{
links.splice(i,1);
break;
}
}
update();
};
this.removeallLinks = function(){
links.splice(0,links.length);
update();
};
this.removeAllNodes = function(){
nodes.splice(0,links.length);
update();
};
this.addLink = function (source, target, value) {
links.push({"source":findNode(source),"target":findNode(target),"value":value});
update();
};
var findNode = function(id) {
for (var i in nodes) {
if (nodes[i]["id"] === id) return nodes[i];};
};
var findNodeIndex = function(id) {
for (var i=0;i<nodes.length;i++) {
if (nodes[i].id==id){
return i;
}
};
};
// set up the D3 visualisation in the specified element
var w = 500,
h = 500;
var vis = d3.select("#svgdiv")
.append("svg:svg")
.attr("width", w)
.attr("height", h)
.attr("id","svg")
.attr("pointer-events", "all")
.attr("viewBox","0 0 "+w+" "+h)
.attr("perserveAspectRatio","xMinYMid")
.append('svg:g');
var force = d3.layout.force();
var nodes = force.nodes(),
links = force.links();
var update = function () {
var link = vis.selectAll("line")
.data(links, function(d) {
return d.source.id + "-" + d.target.id;
});
link.enter().append("line")
.attr("id",function(d){return d.source.id + "-" + d.target.id;})
.attr("class","link");
link.append("title")
.text(function(d){
return d.value;
});
link.exit().remove();
var node = vis.selectAll("g.node")
.data(nodes, function(d) {
return d.id;});
var nodeEnter = node.enter().append("g")
.attr("class", "node")
.call(force.drag);
nodeEnter.append("svg:circle")
.attr("r", 16)
.attr("id",function(d) { return "Node;"+d.id;})
.attr("class","nodeStrokeClass");
nodeEnter.append("svg:text")
.attr("class","textClass")
.text( function(d){return d.id;}) ;
node.exit().remove();
force.on("tick", function() {
node.attr("transform", function(d) { return "translate(" + d.x + "," + d.y + ")"; });
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; });
});
// Restart the force layout.
force
.gravity(.05)
.distance(50)
.linkDistance( 50 )
.size([w, h])
.start();
};
// Make it all go
update();
}
function drawGraph()
{
graph = new myGraph("#svgdiv");
graph.addNode('A');
graph.addNode('B');
graph.addNode('C');
graph.addLink('A','B','10');
graph.addLink('A','C','8');
graph.addLink('B','C','15');
}
I took Rahuls great example, made some changes, and posted a bl.ock complete with animation over time if anyone is interested in a fully functioning example. Adding/removing links/nodes really should be easier than this, but still pretty cool.
http://bl.ocks.org/ericcoopey/6c602d7cb14b25c179a4
In addition to calling drawGraph() in the ready function, you can also embed the posted code inside an inline <script></script>block.
This is how most of the tutorials on the d3 site handle it.