I am new to D3. I am creating an interactive visualization. I want to update the data based on user's input and re-render (not-re-draww everything) the visualization.
Code for creating visualization:
cals = d3.select("body").select("svg").selectAll("g")
.data(yearlyData) //I want to update this "yearlyData"
.enter()
.append("g")
.attr("id","svgGroup")
.attr("transform",function(d,i){
return "translate(0,"+(yOffset+(i*(height+calY)))+")";
})
dataRects = cals.append("g")
.attr("id","dataDays")
.selectAll("dataDays")
.data(function(d){
return d.values;
})
.enter()
.append("rect")
.attr("id",function(d) {
return format(d.date)+":"+d.DEPARTURE_DELAY;
})
.attr("stroke","#ccc")
.attr("width",cellSize)
.attr("height",cellSize)
.attr("x", function(d){return xOffset+calX+(d3.time.weekOfYear(d.date) * cellSize);})
.attr("y", function(d) { return calY+(d.date.getDay() * cellSize); })
.attr("fill", function(d) {
if (d.DEPARTURE_DELAY<breaks[0]) {
return colours[0];
}
for (i=0;i<breaks.length+1;i++){
if (d.DEPARTURE_DELAY>=breaks[i]&&d.DEPARTURE_DELAY<breaks[i+1]){
return colours[i];
}
}
if (d.DEPARTURE_DELAY>breaks.length-1){
return colours[breaks.length]
}
})
I tried to use
dataRects.data(newData) And
d3.select("body").select("svg").selectAll("g").data(newData);
in the Onclick() function, but these don't work. The source data is not changed. Can I get some help?
Thanks!
Related
For a project I am attempting to have three visualizations for data based on car stats, where if you hover over one, the others will show the affects of that hovering as well.
The first is a bar graph, the second is a scatterplot, and the third is a line graph. For the line graph I wanted to group by manufacturer so that I don't have a couple hundred lines on my line graph, as the plot coordinates on the x and y are acceleration and model year. The other two don't need to be grouped in this way because one of their axes is the manufacturer.
I have the interactions from the line graph to the other two working since there is no nesting on the bar or scatterplot, and both the scatterplot and the bar graph can affect each other perfectly fine, but since the data is nested for the line graph, I can't seem to figure out how to access it, as the way I was doing it for the other two (using filtering) does not seem to work.
Below I am first showing where I am trying to create interactions when the mouse hovers (this is for the bar graph), and below that I include how my line graph is set up to show how it works. All I want is to make the corresponding line stand out more from the others by thickening the stroke when I hover over the bar or plot (in the scatterplot), and then go back to the normal size upon moving my cursor.
I followed the tutorial on the D3 website for line graphs, so there shouldn't be anything particularly wrong with that code.
Creating the bars for the bar graph, the mouseover and mouseout are the important parts:
var path1 = svg1.selectAll("myRect")
.data(data)
.enter()
.append("rect")
.attr("x", x1(0.1) )
.attr("y", function(d) { return y1(d.Manufacturer); })
.attr("height", y1.bandwidth() )
.attr("width", function(d) { return x1(d.Cylinders); })
.attr("fill", function (d) {
return color1(d.Cylinders);
})
.on('mouseover', function (d, i) {
svg1.selectAll('rect')
.filter(function(f) {
return f.Manufacturer === d.Manufacturer;
})
.attr("fill", function (d) {
return color4(d.Cylinders);
})
svg2.selectAll('circle')
.filter(function(f) {
return f.Manufacturer === d.Manufacturer;
})
.attr('r', 9)
.attr("fill", function (d) {
return color5(d.Horsepower);
});
svg3.selectAll('path') //THIS IS THE LINE GRAPH
.filter(function(f) {
console.log(this)
return ; // <-------This is where I don't know what to return to just get one line
})
.attr("stroke-width", 7)
})
.on('mouseout', function (d, i) {
svg1.selectAll('rect')
.filter(function(f) {
return f.Manufacturer === d.Manufacturer;
})
.attr("fill", function (d) {
return color1(d.Cylinders);
});
svg2.selectAll('circle')
.filter(function(f) {
return f.Manufacturer === d.Manufacturer;
})
.attr('r', 5)
.attr("fill", function (d) {
return color2(d.Acceleration);
});
d3.selectAll('path') //DELESLECTING LINE GRAPH
.filter(function(f) {
return f.key === d.Manufacturer; //this is what I tried before but it doesn't work
})
.attr("stroke-width", 1.5)
});
Creating the line graph:
var sumstat = d3.nest()
.key(function(d) { return d.Manufacturer;})
.entries(data);
// Add X axis
var x3 = d3.scaleLinear()
.domain([69, 84])
.range([ 0, width3 ]);
svg3.append("g")
.attr("transform", "translate(0," + height3 + ")")
.call(d3.axisBottom(x3).ticks(5));
// Add Y axis
var y3 = d3.scaleLinear()
.domain([8, d3.max(data, function(d) { return +d.Acceleration; })])
.range([ height3, 0 ]);
svg3.append("g")
.call(d3.axisLeft(y3));
var div3 = d3.select("#my_div").append("div")
.attr("class", "#tool_tip")
.style("opacity", 0)
.style("font-size", "xx-large");
// color palette
var res = sumstat.map(function(d){ return d.key }) // list of group names
var color = d3.scaleOrdinal()
.domain(res)
.range(['darkolivegreen','darkred','palevioletred','indianred', 'hotpink'])
// Draw the line
svg3.selectAll(".line")
.data(sumstat)
.enter()
.append("path")
.attr("fill", "none")
.attr("stroke", function(d){ return color(d.key) })
.attr("stroke-width", 1.5)
.attr("d", function(d){
return d3.line()
.x(function(d) { return x3(d.ModelYear); })
.y(function(d) { return y3(+d.Acceleration); })
(d.values)
})
.on('mouseover', function (d, i) {
//highlight;
svg3.selectAll("path")
.attr("stroke-width", 0.9)
d3.select(this)
.attr("stroke", function(d){ return color(d.key)})
.attr("stroke-width", 6)
svg1.selectAll('rect')
.filter(function(f) {
return f.Manufacturer === d.key;
})
.attr("fill", function (d) {
return color4(d.Cylinders);
})
svg2.selectAll('circle')
.filter(function(f) {
return f.Manufacturer === d.key;
})
.attr('r', 9)
.attr("fill", function (d) {
return color5(d.Horsepower);
});
})
.on('mouseout', function (d, i) {
svg3.selectAll("path")
.attr("stroke-width", 1.5)
d3.select(this)
.attr("stroke", function(d){ return color(d.key)})
.attr("stroke-width", 1.5)
svg1.selectAll('rect')
.filter(function(f) {
return f.Manufacturer === d.key;
})
.attr("fill", function (d) {
return color1(d.Cylinders);
})
svg2.selectAll('circle')
.filter(function(f) {
return f.Manufacturer === d.key;
})
.attr('r', 5)
.attr("fill", function (d) {
return color2(d.Horsepower);
});
});
Any assistance I can get would be greatly appreciated!!
I think I may have figured out the problem. It would seem that trying to filter the paths causes an issue because the x and y axes are also technically lines, and thus have paths that are null. I tried
svg3.selectAll('path')
.filter(function(f) {
console.log(f)
if(f!=null)
return f.key === d.Manufacturer;
})
.attr("stroke-width",7)
In the .on('mouseover') function, and it seems to be working. The issue was the nulls, not the actual accessing of the keys.
Still taking suggestions if there is a better way to do this!
I have multiple CSV files that looks like this:
name,state,x,y
Anderson,VIC,34,765
Martin,VIC,55,345
James,NSW,46,129
Zoe,QLD,63,76
I'm using this data with a barchart, and have it loaded in so that all data is shown. I'm currently toying with the idea of when a user clicks a dropdown menu, only certain values will show. For example, if they click VIC, only data with a state of VIC will be shown. But I'm confused about how I can segregate the data like that? I thought my code below would work, but it doesn't:
svg.selectAll("mybar")
.data(data, function(d) { return d.state["vic"];})
.enter()
.append("rect")
.attr("x", function(d) { return x(d.x); })
.attr("y", function(d) { return y(d.y); })
.attr("width", x.bandwidth())
.attr("height", function(d) { return height - y(d.y); })
.attr("fill", "blue")
The ideal output is that return d.state["..."]; is updated depending on the state that has been selected in the dropdown menu.
You must have to add filter with data like.
data.filter(function(d){
return d.category == category;
})
AS
d3.select("body")
.selectAll("div.h-bar")
.data(data.filter(function(d){
return d.category == category;
}))
.select("span")
.text(function (d) {
return d.category;
});
Here is the working example https://bl.ocks.org/fabiomainardi/00fd581dc5ba92d99eec
Plunker: https://next.plnkr.co/edit/17t5ujwC71IK3PCi
Why is following not adding a "test" label to all my polygons?
/* NOT Working code */
groups.selectAll('.path_placeholder')
.enter()
.append('text')
.text("test")
Update
.enter() wasn't required as mentioned by Xavier. Removing it showed "test" for all nodes. But why then its not working when I do provide data and use enter() as following:
groups.selectAll('.path_placeholder')
.data(groupIds, function(d) {
return d;
})
.enter()
.append('text')
.text(function(d){
console.log(d);
return d;
})
I am trying to be able to show label for each of my polygon and for now just trying to add a dummy label to each of them.
Your problem here is the paths is a <path> selection, not a <g> one:
paths = groups.selectAll('.path_placeholder')
.data(groupIds, function(d) { return +d; })
.enter()
.append('g')
.attr('class', 'path_placeholder')
.append('path')//this makes the selection pointing to <path> elements
.attr('stroke', function(d) { return color(d); })
.attr('fill', function(d) { return color(d); })
.attr('opacity', 0);
Because of that, when you do...
groups.selectAll('.path_placeholder')
.data(groupIds, function(d) {
return d;
})
.enter()
//etc...
... your "enter" selection is empty, because you already have data associated to that paths selection.
Besides that, it makes little sense using a proper "enter" selection for the texts, since the data is the same data bound to the groups.
Solution: the solution here, which is the idiomatic D3 for this situation, is creating an actual <g> selection.
We can do that by breaking the paths selection, and giving it another name:
pathGroups = groups.selectAll('.path_placeholder')
.data(groupIds, function(d) {
return +d;
})
.enter()
.append('g')
.attr('class', 'path_placeholder');
Then you can just do:
paths = pathGroups.append('path')
.attr('stroke', function(d) {
return color(d);
})
.attr('fill', function(d) {
return color(d);
})
.attr('opacity', 0)
texts = pathGroups.append('text')
.text(function(d) {
return d;
});
Here is the forked Plunker: https://next.plnkr.co/edit/31ZPXIvSI287RLgO
Hello I'm new to d3 and I couldn't update the data in my bar chart.
I'm creating the bars using following code.
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return x(d.letter) + 1; })
.attr("y", function(d) { return y(d.frequency); })
.attr("width", x.bandwidth() - 1)
.attr("height", function(d) { return height - y(d.frequency); })
I was able to modify the data using hard coded values:
var bar = svg.selectAll(".bar");
bar.transition().duration(750).data(data).enter()
.attr("y", function(d) { return 0; })
.attr("height", function(d) { return Math.random()*100; });
How can i properly bind the new data?
See the snippet below for an example of updating the bar chart with new data programmatically and showing a transition animation in the process.
var data = [{x:10, y:10, w:50, h:25},
{x:10, y:40, w:150, h:25},
{x:10, y:70, w:70, h:25}];
var g = d3.select("g");
// Bind initial data to empty selection, then use enter()
// to access the virtual selection from the data-join
// and subsequently append a rect for each virtual selection
g.selectAll(".bar")
.data(data)
.enter().append("rect")
.attr("class", "bar")
.attr("x", function(d) { return d.x; })
.attr("y", function(d) { return d.y; })
.attr("width", function(d) {return d.w; })
.attr("height", function(d) { return d.h; });
var new_data = [{x:10, y:10, w:150, h:25},
{x:10, y:40, w:50, h:25},
{x:10, y:70, w:100, h:25}];
// Bind the new data to the rect objects. Since new_data
// is of the same size as number of rects, enter() and exit()
// selections from data-join will be empty and the rects
// with updated bound data are now available in the default
// update selection.
g.selectAll(".bar")
.data(new_data) // grab the update selection by default
.transition().duration(3000)
.attr("width", function(d) { return d.w; }); // Update the attribute
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/3.4.11/d3.min.js"></script>
<svg width="400" height="400">
<g>
</g>
</svg>
I have some nested data in this format:
[{"key":"PFOA",
"values":[
{"sampleDate":"2016-0126T05:00:00.000Z",
"shortName":"PFOA",
"pfcLevel":0,
"chemID":1},
{"sampleDate":"2016-01-19T05:00:00.000Z",
"shortName":"PFOA",
"pfcLevel":0,
"chemID":1},
{"sampleDate":"2016-01-12T05:00:00.000Z",
"shortName":"PFOA",
"pfcLevel":0,
"chemID":1}
],
"visible":0}
]
I'm trying to use this data to add circles to a multi-line graph. I can do this if I use the raw, non-nested data directly from the database, but that is causing other issues. I'd rather use the same nested data for the lines and the circles if possible. The nest function and the circle code is below:
var nested_data = d3.nest()
.key(function(d) { return d.shortName; })
.entries(data);
var circles = svg.selectAll(".circle")
.data(nested_data)
.enter().append("g")
.attr("class", "circle");
circles.append("circle")
.attr("stroke", function(d) { return color(d.key); })
.attr("fill", "white")
.attr("cx", function(d, i) { return x(d.values['sampleDate']) })
.attr("cy", function(d, i) { return y(d.values['pfcLevel']) })
.attr("r", 2);
I've tried different things like d.values[sampleDate] or .data(nested_data.values) but I am getting undefined errors on all of them.
Thanks in advance.
You are looking for a Nested Selection:
var nested_data = d3.nest()
.key(function(d) {
return d.shortName;
})
.entries(data);
var groups = svg.selectAll(".circle")
.data(nested_data)
.enter().append("g")
.attr("class", "circle");
var circles = groups.selectAll("circle") // start a nested selection
.data(function(d) {
return d.values; // tell d3 where the children are
})
.enter().append("circle")
.attr("stroke", function(d) {
return color(d.shortName);
})
.attr("fill", "white")
.attr("cx", function(d, i) {
return x(d.sampleDate) // use the fields directly; no reference to "values"
})
.attr("cy", function(d, i) {
return y(d.pfcLevel)
})
.attr("r", 2);