I am trying to render multiple lines with x, y axis as 1st, 2nd values of each array out of 5 arrays within datarow dataset. i want to generate 3 different lines each for 0, 15, 1007 values of 3rd elements in all 5 arrays by nesting through each of the 3rd element in the datarow dataset.
What i tried: https://jsfiddle.net/data_x/23143n3r/
I followed http://bl.ocks.org/d3noob/d8be922a10cb0b148cd5.
The important code:
var line = d3.svg.line()
.x(function(d,i){return x(datarow[i][0]);})
.y(function(d,i){return y(datarow[i][1]);})
.interpolate("linear");
//Nest the entries
var nest = d3.nest()
.key( function(d) { return d[2]})
.entries(datarow);
//Loop through each d[2]
nest.forEach(
function(d,i)
{
svg.append("path")
.attr("class", "line")
.attr("d", line(d.values[0][2]))
.style("stroke-width", 1)
.style("stroke", "steelblue")
.style("fill", "none")
}
);
In above code, d.values[0] returns all the array elements. As i want to plot multiple lines for 3rd values in each array, i am returning 3rd element in each array. But, i am still not able to figure out why nothing is being rendered.
Any help or hints are appreciated,
Thank you,
With the help of a friend,
Problem is at
`var line = d3.svg.line()
.x(function(d,i){return x(datarow[i][0]);})
.y(function(d,i){return y(datarow[i][1]);})
.interpolate("linear");`
whereas it should be like
d3.svg.line()
.x(function(d){return x(d[0]);})
.y(function(d){return y(d[1]);})
.interpolate("linear");`
Line x,y accesory functions takes array of values as input not single values.
Related
What am I missing?
I am allowing users to remove and plot their own data point. My line path is drawn with this and it works fine.
let self = this;
let line = D3['line']()
.x(function (d) { return self.getColX(d.x); })
.y(function (d) { return self.getRowY(d.y); });
this.group = this.canvas.append("g")
.attr("transform", "translate(25,25)");
//bind the data
this.group.selectAll("path")
.data(this.data[this.site].drawLine)
.enter()
.append("path")
.attr("d", line)
.attr("fill", "none")
.attr("stroke", "black");
this.group.selectAll('path').exit().remove()
My problem is, if I pop the last coordinates out and add a new one, call the draw function, the new points gets added correctly but the old points doesn't get remove.
For example: my line goes from (x,y): (1,3) to (3,6) to (7,8), if i remove (7,8) and replace that with 5,6. i will see a new line from (3,6) to (5,6) but the line from (3,6) to (7,8) which is no longer in the data array still there.
The enter() and exit() selections are created after D3 compares your selection with the data provided. So they are available in the return of these calls:
this.group.selectAll("path")
.data(this.data[this.site].drawLine)
And that's why new data is appended, enter() works just fine.
With this.group.selectAll('path').exit().remove() you create a new selection but is not comparing the selection against any data, therefore enter() and exit() selections aren't available to work with.
Long story short, just apply .exit().remove() to your initial selection and it should work. Something like this:
let update = this.group.selectAll("path")
.data(this.data[this.site].drawLine)
update.enter()
.append("path")
.attr("d", line)
.attr("fill", "none")
.attr("stroke", "black")
update.exit()
.remove()
I hava a multi line chart with D3.js.
Initial rendering works just fine. When I try to update, only new lines are added instead of the old ones update/removed.
Example: http://jsfiddle.net/ty192n93/6/
This is the part where the line is rendered:
var node = svg.selectAll(".g.city")
.data(data, function(d) { return d.name; });
var enter = node.enter().append("g")
.attr("class", "city");
enter.append("path")
.attr("class", "line")
.attr("d", function(d) {
return line(d.data);
})
.style("stroke", function(d) {
return color(d.name);
});
// Text element left out
var remove = node.exit().remove();
How is it possible to update the old values (key function is specified) or remove them completely?
Your first selection is wrong. The selector .g.city selects all elements having both classes g and city. Your selection is valid, though, but will always return an empty selection putting all your data in the enter selection and leaving the exit selection empty. Instead, you are interested in group elements g having class city. Removing the first dot from the selector should do the trick:
var node = svg.selectAll("g.city") // <-- remove the dot
.data(data, function(d) { return d.name; });
The following toy problem illustrates my issue. I have an array of "locations", say a treasure map. Each item in the array for example monsters or treasure could exist at multiple locations on the map. e.g.
locations = [
{name:'treasure', color: 'blue', coords:[[100,100], [200,300]]},
{name:'monsters', color: 'red', coords:[[100,150], [220,420], [50,50]]}
]
Now I want to plot these using D3. The bad/naive approach (that works - see here for fiddle), would look like this:
for location in locations
for coords in location.coords
svg.append('circle')
.attr('cx', coords[0])
.attr('cy', coords[1])
.attr('r', 8)
.style('fill', location.color)
.datum(location)
However, when I modify the contents of the data, I don't want to have to run this naive code each time. It appears that using data() and enter() is the "correct" way to do it, but I can't figure out how it works with the sub-coordinates. e.g.
svg.selectAll('circle').data(locations).enter().append('circle')
.attr('cx', (d) -> d.coords[0][0])
.attr('cy', (d) -> d.coords[0][1])
.attr('r', 8)
.style('fill', (d) -> d.color)
This works great, but as you can see I am only printing the FIRST coordinate for each location, where I want to print them all. I suspect the only way to do this is to flatten my data array so there are 5 entries in total - 3 monsters and 2 treasure items.
Just wondering if there is a way to handle this better using D3.
For this, you need nested selections. The idea is that instead of appending a single element per data item, you append several. In code, it looks like this:
// append a `g` element for each data item to hold the circles
var groups = svg.selectAll("g.circle").data(locations)
.enter().append("g").attr("class", "circle");
// now select all the circles in each group and append the new ones
groups.selectAll("circle")
// the d in this function references a single data item in locations
.data(function(d) { return d.coords; })
.enter().append("circle")
.attr("cx", function(d) { return d[0]; })
.attr("cy", function(d) { return d[1]; });
It works the same for update and exit selections.
m creating a scatter plot and I have problems plotting my circles (datapoints).
When appending the first set of circles it is drawn fine on the graph. However the next set I try to append won't be draw for some reason. How can this be? Am I using the Enter/append/select wrong the 2nd time?
I have a JSFiddle with my code: http://jsfiddle.net/4wptM/
I have uncommented the parts where I load and manipulate my data and created the same array with a smaller sample. As it can be seen only the first set of circles are shown and the 2nd ones aren't.
My code of the circle section is pasted below:
var circle = SVGbody
.selectAll("circle")
.data(graphData[0])
.enter()
.append("circle")
.attr("cx",function(d){return xScale(100);})
.attr("cy",function(d){return yScale(parseFloat(d))})
.attr("r",5);
circle = SVGbody
.selectAll("circle")
.data(graphData[1])
.enter()
.append("circle")
.attr("cx",function(d){return xScale(200);})
.attr("cy",function(d){return yScale(parseFloat(d))})
.attr("r",5);
I just have those 2 copied to try and figure out the problem. The actual code I have is in the following for loop. When I run it in this loop it draws the circles for when the index is 1 and a few of when the index is 3.
for(var i = 0;i < graphData.length;i++){
var circle = SVGbody
.selectAll("circle")
.data(graphData[i])
.enter()
.append("circle")
.attr("cx",function(d){return xScale((i*100)+100);})
.attr("cy",function(d){return yScale(parseFloat(d))})
.attr("r",20);
//console.log(i + " ::::::: " + graphData[i])
}
Some help would be so greatly appretiated. I really can't figure out what I'm doing wrong.
When operating on a existing set of elements (as it is the case here on the second .selectAll("circle") the .data() method uses only these elements from the array which have no corresponding index in the selecton made with .selectAll(). To prevent this behaviour you will have to add a key function as the second parameter of .data() as explained here and here.
Here this function only has to return each element of the array:
function (d) {
return d;
}
In the fiddle it would look something like this:
var circle = SVGbody
.selectAll("circle")
.data(graphData[0], function(d) { return d; }) // <-- this one
.enter()
.append("circle")
.attr("cx",function(d){return xScale(100);})
.attr("cy",function(d){return yScale(parseFloat(d))})
.attr("r",5);
fiddle
And to change the fill color of an element you have to use .style("fill", ...) and not .attr("fill", ...)
.style("fill", "green")
I am using d3.js to build a stacked bar graph. I am referring to this graph http://bl.ocks.org/mbostock/3886208
I want to add a small square of different color on the bars which have equal value. For example in this graph- if population of 25 to 44 Years and 45 to 64 Years is equal then i want to show a square of 10,10(width,height) on both bars related to CA. This is what I was doing but its not showing on the bar:
var equalBar = svg.selectAll(".equalBar")
.data(data)
.enter().append("g")
.attr("class", "equalBar")
.attr("transform", function(d){ return "translate(" + x(d.states) + ",0"; });
equalBar.selectAll("rect")
.data(function(d) { return d.ages;} )
.enter().append("rect")
.attr("width", 10)
.attr("y", function(d){
return y(d.y1);
})
.attr("height", function(d)
{ return 10; })
.style("fill", "green");
Thanks a lot for help.
What you're trying to do isn't really compatible with the D3 approach to data management. The idea of selections relies on the data items to be independent, whereas in your case you want to compare them explicitly.
The approach I would take to do this is to create a new data structure that contains the result of these comparisons. That is, for each population group it tells you what other groups it is equal to. You can then use this new data to create the appropriate rectangles.