Trying to practice with some D3 and using select dropdowns to change the colors, etc. So I've been practicing with the Sankey (Parallel Sets example on the D3 website).
There are two buttons - one basically is selecting different columns from the data, and the second button populates the unique values under that column.
Right now, I'm just trying to get the 2nd button to update with the unique value when the first button changes. I know I'm getting the new unique values but the 2nd button never changes.
https://jsfiddle.net/jimdholland/vrq7mw3g/4/
var dataRaw = 'Survived,Sex,Age,Class,value\nPerished,Male,Adult,Crew,670\nPerished,Male,Adult,Third Class,387\nPerished,Male,Adult,Second Class,154\nPerished,Male,Adult,First Class,118\nPerished,Male,Child,Third Class,35\nPerished,Female,Adult,Crew,3\nPerished,Female,Adult,Third Class,89\nPerished,Female,Adult,Second Class,13\nPerished,Female,Adult,First Class,4\nPerished,Female,Child,Third Class,17\nSurvived,Male,Adult,Crew,192\nSurvived,Male,Adult,Third Class,75\nSurvived,Male,Adult,Second Class,14\nSurvived,Male,Adult,First Class,57\nSurvived,Male,Child,Third Class,13\nSurvived,Male,Child,Second Class,11\nSurvived,Male,Child,First Class,5\nSurvived,Female,Adult,Crew,20\nSurvived,Female,Adult,Third Class,76\nSurvived,Female,Adult,Second Class,80\nSurvived,Female,Adult,First Class,140\nSurvived,Female,Child,Third Class,14\nSurvived,Female,Child,Second Class,13\nSurvived,Female,Child,First Class,1'
var data = d3.csvParse(dataRaw);
for (const d of data) {d.value = +d.value; }
var keys = data.columns.slice(0, -1)
let dropDefault = 'Survived'
var subOptions = [...new Set(data.map(d => d[dropDefault]))];
var mainButton = d3.select("#selectButton")
.selectAll('option')
.data(keys)
.enter()
.append('option')
.text(function (d) { return d; }) // text showed in the menu
.attr("value", function (d) { return d; }) // corresponding value returned
// add the options to the button
var subButton = d3.select("#selectButtonAfter")
.selectAll('option')
.data(subOptions)
.enter()
.append('option')
.text(function (d) { return d; }) // text showed in the menu
.attr("value", function (d) { return d; }) // corresponding value returned
// Chart stuff here - see Fiddle below with all of it.
// Second issue, doing mainButton.on("change"...) doesn't work, have to do the select.
d3.select("#selectButton").on("change", function(d){
var newOption = d3.select(this).property('value');
console.log(newOption);
var NewsubOptions = [...new Set(data.map(d => d[newOption]))];
console.log(NewsubOptions);
d3.select("#selectButtonAfter").data(NewsubOptions)
.exit()
.enter()
.append('option')
.text(function (d) { return d; }) // text showed in the menu
.attr("value", function (d) { return d; }) // corresponding value returned
});
There are several problems here:
d3.select("#selectButtonAfter").data(NewsubOptions)
.exit()
.enter()
.append('option')
.text(function (d) { return d; }) // text showed in the menu
.attr("value", function (d) { return d; }) // corresponding value returned
Some of them are:
you cannot use data with d3.select()
You need to have a key function, otherwise you're binding by index
You cannot chain data, exit and enter like you did here.
A conventional approach would be:
let select = d3.select("#selectButtonAfter").selectAll("option")
.data(NewsubOptions, d => d);
select.exit().remove();
const selectEnter = select.enter()
.append('option');
select = selectEnter.merge(select);
select.text(function(d) {
return d;
}) // text showed in the menu
.attr("value", function(d) {
return d;
}) // corresponding value returned
It could be even shorter, but the snippet above allows for selects with different number of options.
Here's your code with that change:
var dataRaw = 'Survived,Sex,Age,Class,value\nPerished,Male,Adult,Crew,670\nPerished,Male,Adult,Third Class,387\nPerished,Male,Adult,Second Class,154\nPerished,Male,Adult,First Class,118\nPerished,Male,Child,Third Class,35\nPerished,Female,Adult,Crew,3\nPerished,Female,Adult,Third Class,89\nPerished,Female,Adult,Second Class,13\nPerished,Female,Adult,First Class,4\nPerished,Female,Child,Third Class,17\nSurvived,Male,Adult,Crew,192\nSurvived,Male,Adult,Third Class,75\nSurvived,Male,Adult,Second Class,14\nSurvived,Male,Adult,First Class,57\nSurvived,Male,Child,Third Class,13\nSurvived,Male,Child,Second Class,11\nSurvived,Male,Child,First Class,5\nSurvived,Female,Adult,Crew,20\nSurvived,Female,Adult,Third Class,76\nSurvived,Female,Adult,Second Class,80\nSurvived,Female,Adult,First Class,140\nSurvived,Female,Child,Third Class,14\nSurvived,Female,Child,Second Class,13\nSurvived,Female,Child,First Class,1'
var data = d3.csvParse(dataRaw);
for (const d of data) {
d.value = +d.value;
}
var keys = data.columns.slice(0, -1)
let dropDefault = 'Survived'
var subOptions = [...new Set(data.map(d => d[dropDefault]))];
var mainButton = d3.select("#selectButton")
.selectAll('option')
.data(keys)
.enter()
.append('option')
.text(function(d) {
return d;
}) // text showed in the menu
.attr("value", function(d) {
return d;
}) // corresponding value returned
console.log(subOptions);
console.log("Just loaded subs");
// add the options to the button
var subButton = d3.select("#selectButtonAfter")
.selectAll('option')
.data(subOptions)
.enter()
.append('option')
.text(function(d) {
return d;
}) // text showed in the menu
.attr("value", function(d) {
return d;
}) // corresponding value returned
var width = 975;
var height = 720;
var color = d3.scaleOrdinal(["Perished"], ["#da4f81"]).unknown("#ccc")
var sankey = d3.sankey()
.nodeSort(null)
.linkSort(null)
.nodeWidth(4)
.nodePadding(20)
.extent([
[0, 5],
[width, height - 5]
])
let index = -1;
const nodes = [];
const nodeByKey = new Map;
const indexByKey = new Map;
const links = [];
for (const k of keys) {
for (const d of data) {
const key = JSON.stringify([k, d[k]]);
if (nodeByKey.has(key)) continue;
const node = {
name: d[k]
};
nodes.push(node);
nodeByKey.set(key, node);
indexByKey.set(key, ++index);
}
};
for (let i = 1; i < keys.length; ++i) {
const a = keys[i - 1];
const b = keys[i];
const prefix = keys.slice(0, i + 1);
const linkByKey = new Map;
for (const d of data) {
const names = prefix.map(k => d[k]);
const key = JSON.stringify(names);
const value = d.value || 1;
let link = linkByKey.get(key);
if (link) {
link.value += value;
continue;
}
link = {
source: indexByKey.get(JSON.stringify([a, d[a]])),
target: indexByKey.get(JSON.stringify([b, d[b]])),
names,
value
};
links.push(link);
linkByKey.set(key, link);
}
};
let graphData = {
nodes,
links
};
const svg = d3.select("#chart").append("svg")
.attr("viewBox", [0, 0, width, height]);
let g = sankey(graphData);
let gtest = sankey({
nodes: graphData.nodes.map(d => Object.assign({}, d)),
links: graphData.links.map(d => Object.assign({}, d))
});
svg.append("g")
.selectAll("rect")
.data(g.nodes)
.join("rect")
.attr("x", d => d.x0)
.attr("y", d => d.y0)
.attr("height", d => d.y1 - d.y0)
.attr("width", d => d.x1 - d.x0)
.append("title")
.text(d => `${d.name}\n${d.value.toLocaleString()}`);
svg.append("g")
.attr("fill", "none")
.selectAll("g")
.data(g.links)
.join("path")
.attr("d", d3.sankeyLinkHorizontal())
.attr("stroke", d => color(d.names[0]))
.attr("stroke-width", d => d.width)
.style("mix-blend-mode", "multiply")
.append("title")
.text(d => `${d.names.join(" → ")}\n${d.value.toLocaleString()}`);
svg.append("g")
.style("font", "10px sans-serif")
.selectAll("text")
.data(g.nodes)
.join("text")
.attr("x", d => d.x0 < width / 2 ? d.x1 + 6 : d.x0 - 6)
.attr("y", d => (d.y1 + d.y0) / 2)
.attr("dy", "0.35em")
.attr("text-anchor", d => d.x0 < width / 2 ? "start" : "end")
.text(d => d.name)
.append("tspan")
.attr("fill-opacity", 0.7)
.text(d => ` ${d.value.toLocaleString()}`);
d3.select("#selectButton").on("change", function(d) {
var newOption = d3.select(this).property('value');
console.log(newOption);
var NewsubOptions = [...new Set(data.map(d => d[newOption]))];
console.log(NewsubOptions);
let select = d3.select("#selectButtonAfter").selectAll("option")
.data(NewsubOptions, d => d);
select.exit().remove();
const selectEnter = select.enter()
.append('option');
select = selectEnter.merge(select);
select.text(function(d) {
return d;
}) // text showed in the menu
.attr("value", function(d) {
return d;
}) // corresponding value returned
});
<body>
<!-- Initialize a select button -->
<select id="selectButton"></select>
<select id="selectButtonAfter"></select>
<div id="chart"></div>
<!-- load the d3.js library -->
<script src="https://d3js.org/d3.v7.min.js"></script>
<script src="https://unpkg.com/d3-sankey#0"></script>
</body>
Related
I have been trying to reproduce this ObservableHQ code on collapsible tree into regular html-css-js format. Below is my implementation.
height = 1000;//+svg.attr("height");
width = 1000;//+svg.attr("width");
radius = width/2;
tree = d3.tree()
.size([2 * Math.PI, radius])
.separation((a, b) => (a.parent == b.parent ? 1 : 2) / a.depth)
const data = d3.json("https://cdn.jsdelivr.net/gh/d3/d3-hierarchy#master/test/data/flare.json");
//const data = d3.json("network.json");
data.then(function(data){
console.log(data);
var svg = d3.select("svg")
.attr("width", width)
.attr("height",height)
var g = svg.append("g")
// .attr("transform",'translate('+width/2+','+height/2+')')
const linkgroup = g.append("g")
.attr("fill", "none")
.attr("stroke", "#555")
.attr("stroke-opacity", 0.4)
.attr("stroke-width", 1.5);
const nodegroup = g.append("g")
.attr("stroke-linejoin", "round")
.attr("stroke-width", 3);
function newdata (animate = true) {
let root = tree(d3.hierarchy(data));
let links_data = root.links();
let links = linkgroup
.selectAll("path")
.data(links_data, d => d.source.data.name+"_"+d.target.data.name);
links.exit().remove();
let newlinks = links
.enter()
.append("path")
.attr("d", d3.linkRadial()
.angle(d => d.x)
.radius(0.1));
let t = d3.transition()
.duration(animate ? 400 : 0)
.ease(d3.easeLinear)
.on("end", function() {
const box = g.node().getBBox();
svg.transition().duration(1000).attr("viewBox", `${box.x} ${box.y} ${box.width} ${box.height}`);
});
let alllinks = linkgroup.selectAll("path")
alllinks
.transition(t)
.attr("d", d3.linkRadial()
.angle(d => d.x)
.radius(d => d.y));
let nodes_data = root.descendants().reverse();
let nodes = nodegroup
.selectAll("g")
.data(nodes_data, function (d) {
if (d.parent) {
return d.parent.data.name+d.data.name;
}
return d.data.name});
nodes.exit().remove();
let newnodes = nodes
.enter().append("g");
let allnodes = animate ? nodegroup.selectAll("g").transition(t) : nodegroup.selectAll("g");
allnodes
.attr("transform", d => `
rotate(${d.x * 180 / Math.PI - 90})
translate(${d.y},0)
`);
newnodes.append("circle")
.attr("r", 4.5)
.on ("click", function (d) {
let altChildren = d.data.altChildren || [];
let children = d.data.children;
d.data.children = altChildren;
d.data.altChildren = children;
newdata ();
});
nodegroup.selectAll("g circle").attr("fill", function (d) {
let altChildren = d.data.altChildren || [];
let children = d.data.children;
return d.children || (children && (children.length > 0 || altChildren.length > 0)) ? "#555" : "#999" } );
newnodes.append("text")
.attr("dy", "0.31em")
.text(d => d.data.name)
.clone(true).lower()
.attr("stroke", "white");
nodegroup.selectAll("g text")
.attr("x", d => d.x < Math.PI === !d.children ? 6 : -6)
.attr("text-anchor", d => d.x < Math.PI === !d.children ? "start" : "end")
.attr("transform", d => d.x >= Math.PI ? "rotate(180)" : null);
}
newdata (false);
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
<html>
<head>
<title>Test</title>
</head>
<body>
<div>
<svg>
</svg>
</div>
</body>
</html>
It is running perfectly fine here as a snippet inside Stackoverflow, but when I am running it in my localhost web server this is showing me an error when I click any of the nodes:
Uncaught TypeError: Cannot read property 'altChildren' of undefined
at SVGCircleElement.<anonymous> ((index):101)
at SVGCircleElement.<anonymous> (d3.v6.min.js:2)
Line 101 (where the error happens when I click on a node) is the following:
let altChildren = d.data.altChildren || [];
I am trying to understand what should be changed in my code so that when I click any node it gets collapsed/expanded.
Edit:
I just realized this is an artifact of using d3 v6 locally. In the code snippet that is working here is using d3 v5. So Now I realized this is happening because of d3 v6.
Now my updated question is that, what does it require to make sure it is running with d3 v6?
Based on d3 v6 migration guide, local events are passed as the first argument to all event listeners. In this case we have a mouse click event listener.
So changing .on ("click", function (d) { to .on ("click", function (event,d) { as below:
from
newnodes.append("circle")
.attr("r", 4.5)
.on ("click", function (d) {
let altChildren = d.data.altChildren || [];
let children = d.data.children;
d.data.children = altChildren;
d.data.altChildren = children;
newdata ();
});
to
newnodes.append("circle")
.attr("r", 4.5)
.on ("click", function (event,d) {
let altChildren = d.data.altChildren || [];
let children = d.data.children;
d.data.children = altChildren;
d.data.altChildren = children;
newdata ();
});
did the trick.
I have included a legend to my Heat Map following this example, built with the threshold scale, and a Linear scale for its axis. The axis is created without issue, the problem is, I believe, in the code where I'm appending its rect elements.
Note: heatMap is a svg element.
The D3.js library version I'm using is D3.v5, in case this might be relevant to mention.
function (dataset,xScale,yScale,heatMap,w,h,p,colorScale,colorDomain)
{
let variance = dataset.monthlyVariance.map(function(val){
return val.variance;
});
const legendColors= [0,1.4,2.8,5.2,6.6,8,9.4,10.8,12.2,13.6].map((n)=> colorScale(n));
const minTemp = dataset.baseTemperature + Math.min.apply(null, variance);
const maxTemp = dataset.baseTemperature + Math.max.apply(null, variance);
const legendDomain= ((min,max,len)=>
{
let arr= [];
let step=(max-min)/len;
for (let i=0; i<len; i++)
{
let eq=min+i*step;
arr.push(eq.toFixed(2));
}
return arr;
})(minTemp,maxTemp,legendColors.length);
//The legend scaling threshold
let threshold= d3.scaleThreshold()
.domain(legendDomain)
.range(legendColors);
//Legend's axis scaling
let legendBar= d3.scaleLinear()
.domain([minTemp,maxTemp])
.range([0,w/3]);
**//Legend's axis**
let legendAxis= d3.axisBottom(legendBar)
.tickSize(6)
.tickValues(threshold.domain())
.tickFormat(d3.format(".1f"));
//append legend's element
let legendEle= heatMap.append("g")
.attr("id","legend")
.classed("legend",true)
.attr("transform", `translate(62,${h-p.bottom+60})`);
//Map legend
legendEle.append("g")
.selectAll("rect")
.data(threshold.range().map(function(color){
let d = threshold.invertExtent(color);
if(d[0] == null) d[0] = legendBar.domain()[0];
if(d[1] == null) d[1] = legendBar.domain()[1];
return d;
}))
.enter()
.append('rect')
.attr('x',(d)=> legendAxis(d[0]))
.attr('y', 0)
.attr('width', (d)=> legendAxis(d[1])-legendAxis(d[0]))
.attr('height', 20)
.attr("fill", function(d) { return threshold(d[0]); });
//append legend's axis
legendEle.append("g").call(legendAxis);
//rect grid map
heatMap.selectAll("rect")
.data(dataset.monthlyVariance)
.enter()
.append("rect")
.attr("class","cell")
.attr('data-month',function(d){
return d.month;
})
.attr('data-year',function(d){
return d.year;
})
.attr('data-temp',function(d){
return dataset.baseTemperature + d.variance;
})
.attr('x', (d)=> xScale(d.year))
.attr('y', (d)=> yScale(d.month))
.attr("width", function(d,i){
return 5;
})
.attr("height", function(d,i){
return yScale.bandwidth(d.month);
})
.attr("fill", (d)=> colorScale(dataset.baseTemperature + d.variance))
.attr("transform", `translate(62,0)`);
}
Would like to know if I could use two html input elements can be used to filter out d.rating and d.value(see code below).Also could I replace the csv dynamically if I use a dropdown and allow the user to select which csv they want to see the visualization for.
https://plnkr.co/edit/WgGs5bcHUP3AmjncivPM?p=preview
<html>
<head>
<!-- Mobile Specific Metas
–––––––––––––––––––––––––––––––––––––––––––––––––– -->
<meta name="viewport" content="width=device-width, initial-scale=1">
</head>
<body>
<input id="myInput" type="number">
<div class="container">
<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 myInput;
d3.select("#myInput").on("change", function(){
myInput = this.value;
d3.csv("austin_fsq.csv", function(d) {
d.sno = +d.sno;
if (d.sno && d.rating>=9&&d.value < myInput) return d;
}, function(error, classes) {
if (error) throw error;
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>
</html>
You have to convert the input value to a number before using it in the if condition (with the unary operator plus, for instance):
if (d.sno && d.rating >= 9 && d.value < +myInput) return d;
//converting to a number -----------^
Here is your updated plunker: https://plnkr.co/edit/wmFIxN6ZydAxLhVDSWNf?p=preview
EDIT: avoinding using the row function.
Right now, you're using the row function to filter your data. This has the unwanted effect of loading and parsing the CSV file after the input being entered, which can lead to some delay (never a good user experience).
An alternative is filtering the data after loading and parsing it. This is the logic:
//d3.csv loads and parses the CSV file
d3.csv("austin_fsq.csv", function(d) {
d.sno = +d.sno;
return d;
}, function(error, data) {
if (error) throw error;
//check the input inside d3.csv
d3.select("#myInput").on("change", function() {
myInput = this.value;
var classes = data.filter(d => d.value < myInput && d.rating >= 9);
//on change, call a function to draw the results
draw(classes);
});
//the function to draw the results here
function draw(classes) {
//code here...
}
});
Actually, if you use this approach, you can easily solve the second problem in your question, which is getting both input values:
var inputs = {};
d3.selectAll("input").on("change", function() {
inputs[this.id] = +this.value;
if (inputs.myValue && inputs.myRating) {
var classes = data.filter(d => d.value < inputs.myValue && d.rating >= inputs.myRating);
draw(classes);
}
})
Here is the plunker showing it, you can set both values and you can see that there is almost no delay after you hit "enter" for the second one: https://plnkr.co/edit/AjVBK3rTOF5aI4eDDbV5?p=preview
I'm trying to figure out how to modify and update from a selection of data with d3.js wordcloud.
Currently, I'm showing the top 10 results from a selection of data depending on indexed keys. I'd like to be able to switch this data depending on the keys, or if I want the top 10 or bottom 10 words.
here is a plnk so far;
http://plnkr.co/edit/cDTeGDaOoO5bXBZTHlhV?p=preview
I've been trying to refer to these guides, General Update Pattern, III and Animated d3 word cloud. However, I'm struggling to comprehend how to introduce a final update function, as almost all guides referring to this usually use a setTimeout to demonstrate how to update, and my brain just won't make the connection.
Any advice is most welcome!
Cheers,
(code here)
var width = 455;
var height = 310;
var fontScale = d3.scale.linear().range([0, 30]);
var fill = d3.scale.category20();
var svg = d3.select("#vis").append("svg")
.attr("width", width)
.attr("height", height)
.append("g")
.attr("transform", "translate(" + (width / 2) + "," + (height / 2) + ")")
// .selectAll("text")
d3.json("data.json", function(error, data) {
if (error) {
console.log(error)
}
else {
data = data
}
function sortObject(obj) {
var newValue = [];
var orgS = "MC";
var dateS = "Jan";
for (var question = 0; question < data.questions.length; question++) {
var organization = data.organizations.indexOf(orgS);
var date = data.dates.indexOf(dateS);
newValue.push({
label: data.questions[question],
value: data.values[question][organization][date]
});
}
newValue.sort(function(a, b) {
return b.value - a.value;
});
newValue.splice(10, 50)
return newValue;
}
var newValue = sortObject();
fontScale.domain([
d3.min(newValue, function(d) {
return d.value
}),
d3.max(newValue, function(d) {
return d.value
}),
]);
d3.layout.cloud().size([width, height])
.words(newValue)
.rotate(0)
.text(function(d) {
return d.label;
})
.font("Impact")
.fontSize(function(d) {
return fontScale(d.value)
})
.on("end", draw)
.start();
function draw(words) {
var selectVis = svg.selectAll("text")
.data(words)
selectVis
.enter().append("text")
.style("font-size", function(d) {
return fontScale(d.value)
})
.style("font-family", "Impact")
.style("fill", function(d, i) {
return fill(i);
})
.attr("text-anchor", "middle")
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.text(function(d) {
return d.label;
})
selectVis
.transition()
.duration(600)
.style("font-size", function(d) {
return fontScale(d.value)
})
.attr("transform", function(d) {
return "translate(" + [d.x, d.y] + ")rotate(" + d.rotate + ")";
})
.style("fill-opacity", 1);
selectVis.exit()
.transition()
.duration(200)
.style('fill-opacity', 1e-6)
.attr('font-size', 1)
.remove();
}
});
I did not see any update function within your code so I added that functionality in order to watch how the update works.
// Add a select elemnt to the page
var dropDown = d3.select("#drop")
.append("select")
.attr("name", "food-venues");
// Join with your venues
var foodVenues = data.organizations.map(function(d, i) {
return d;
})
// Append the venues as options
var options = dropDown.selectAll("option")
.data(foodVenues)
.enter()
.append("option")
.text(function(d) {
return d;
})
.attr("value", function(d) {
return d;
})
// On change call the update function
dropDown.on("change", update);
In order for a d3 word cloud to update correctly you need to calculate again the layout with the desired data
function update() {
// Using your function and the value of the venue to filter data
var filteredData = sortObject(data, this.value);
// Calculate the new domain with the new values
fontScale.domain([
d3.min(newValue, function(d) {
return d.value
}),
d3.max(newValue, function(d) {
return d.value
}),
]);
// Calculate the layout with new values
d3.layout.cloud()
.size([width, height])
.words(filteredData)
.rotate(0)
.text(function(d) {
return d.label;
})
.font("Impact")
.fontSize(function(d) {
return fontScale(d.value)
})
.on("end", draw)
.start();
}
I modified your sortObject function to receive an extra parameter which is the desired venue:
function sortObject(obj, venue) {
var newValue = [];
var orgS = venue || "MC";
// ....
}
Here is the working plnkr: http://plnkr.co/edit/B20h2bNRkyTtfs4SxE0v?p=preview
You should be able to use this approach to update with your desired restrictions. You may be able to add a checkbox with a event listener that will trigger the update function.
In your html:
<input checked type="checkbox" id="top" value="true"> <label for="top">Show top words</label>
In your javascript:
var topCheckbox = d3.select('#top')
.on("change", function() {
console.log('update!')
});
I'm adding nodes to a D3 tree layout at runtime but when the new child nodes are inserted, the original child is pushed over to the very left. I would like the original child to be in the middle (or close to) of the group of children so that if the graph is something like :
Parent
Child C
adding additional nodes A,B,D, and E results in a graph like this:
Parent
ChildA ChildB ChildC ChildD ChildE
rather than this:
Parent
ChildC ChildA ChildB ChildD ChildE
If relevant, code for this update function is below:
function update(record_to_add, parent) {
if (nodes.length >= 500) return clearInterval(timer);
// Add a new node to a random parent.
var n = {id: nodes.length, Username: record_to_add.Username},
p = nodes[parent];
if (p.children) p.children.push(n); else p.children = [n];
nodes.push(n);
// Recompute the layout and data join.
node = node.data(tree.nodes(root), function(d) { return d.id; });
link = link.data(tree.links(nodes), function(d) { return d.source.id + "-" + d.target.id; });
nodes.forEach(function (d) {
});
// Add entering links in the parent’s old position.
link.enter().insert("path", ".node")
.attr("class", "link")
.attr("d", function(d) {
var o = {x: d.source.px, y: d.source.py};
return diagonal({source: o, target: o});
});
node.enter().insert("text")
.attr("x", function(d) { return (d.parent.px);})
.attr("y", function(d) { return (d.parent.py);})
.text(function(d) { return d.Username; });
// Add entering nodes in the parent’s old position.
node.enter().append("circle", "g")
.attr("class", "node")
.attr("r", 10)
.attr("cx", function(d) { return d.parent.px; })
.attr("cy", function(d) { return d.parent.py; });
node.on("mousedown", function (d) {
var g = d3.select(this); // The node
// The class is used to remove the additional text later
console.log(d.Username);
if (d.id == null)
{
console.log("ASDgasd");
}
else
{
try {
downstream_length =
DownstreamRecords[d.Username].length;
}
catch(err) {
downstream_length = 0;
}
for (var i = 0; i < downstream_length; ++i)
{
update(DownstreamRecords[d.Username][i], d.id);
}
}
});
node.on("mouseover", function (d) {
var g = d3.select(this); // The node
// The class is used to remove the additional text later
var info = g.append('text')
.classed('info', true)
.attr('x', 20)
.attr('y', 10)
.text('More info');
});
// Transition nodes and links to their new positions.
var t = svg.transition()
.duration(duration);
t.selectAll(".link")
.attr("d", diagonal);
t.selectAll(".node")
.attr("cx", function(d) { return d.px = d.x; })
.attr("cy", function(d) { return d.py = d.y; });
t.selectAll("text")
.style("fill-opacity", 1)
.attr("x", function(d) { return d.px = d.x + 20; })
.attr("y", function(d) { return d.py = d.y; });
}
rather than using push for adding child use
arry.splice(index, 0, newObject);
so you can add new child on your selected position but you have put some validation like lenth or array and index point etc.
like this
if (p.children) p.children.push(n); else p.children = [n];
replace it with
if (p.children){
p.children.splice(0, 0, n);
// or you can do some calculation according to number of child
available and made index
//p.children.push(n);
}
else{ p.children = [n];}