d3 fill under interpolated line - javascript

In the following d3 line chart example, I would like to fill in the the area under the lines with fill color equivalent to line color and a transparency of 50%.
In addition to adding area to the CSS
.area {
fill: blue;
opacity: 0.5
}
adding the following to function updateGraph(data)
svg.append("path")
.data([data])
.attr("class", "area")
.attr("d", area);
and
var area = d3.svg.area()
.x(function(d) { return x(d.year); })
.y0(height)
.y1(function(d) { return y(d.value); });
what is needed for the filled in area to fit under the interpolated line and for fill color to be equivalent to line color?

Actually, you have to create a new selection before the lines' selection (named states), here named stateArea:
var stateArea = svg.selectAll(".area")
.data(result, function(d) {
return d.key
});
stateArea.enter().append("path")
.attr("class", "area");
stateArea.transition()
.style("fill", function(d, i) {
return d.color = color(d.key);
})
.style("opacity", 0.5)
.attr("id", function(d) {
return 'tagArea' + d.key.replace(/\s+/g, '');
})
.attr("d", function(d) {
return area(d.values)
});
stateArea.exit().remove();
Here is the updated bl.ocks: http://bl.ocks.org/anonymous/0c80f4f72247dcc8f590aa2d63d40da0
PS: I'm just appending the areas here, you'll have to refactor the code if you want that legend to the left changing the areas as well.

Related

How can I use column values for SVG colors using JS/D3?

I searched for this and found a lot of info on how to set scales, but I'm trying to color individual SVGs created in D3 using values from a column that's populated with hex values. In the code below "Color1" is the column populated with different hex color values, e.g., #000000;
Here's what I've tried that makes intuitive sense to me but isn't working, instead the chart populates with the fill as black:
var circles = svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx',function (d) { return xScale(d.xvalue) })
.attr('cy',function (d) { return yScale(d.yvalue) })
.attr('r','3')
.attr('stroke','black')
.attr('stroke-width',1)
.attr('fill', function (d) {return d.Color1})
I've also tried surrounding the function with "'" but was unsuccessful.
The color property should not include a semicolon:
var data = [
{Color1: "#aaaaaa;"},
{Color1: "#aaaaaa"}
]
var svg = d3.select("body").append("svg");
var circles = svg.selectAll('circle')
.data(data)
.enter()
.append('circle')
.attr('cx',(d,i)=>i*100+50)
.attr('cy', 100)
.attr('r','10')
.attr('stroke','black')
.attr('stroke-width',1)
.attr('fill', function(d) { return d.Color1; })
<script src="https://cdnjs.cloudflare.com/ajax/libs/d3/5.7.0/d3.min.js"></script>
You could just slice the last character off if you have the semicolon hard coded in your data:
.attr("fill", function(d) { return d.Color1.slice(0,-1); })

How to coordinate interactions between multiple data visualizations, particularly when one of them uses nesting? JavaScript and D3

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!

Using General update pattern in line graph

I have a demo here
Its a line bar chart using D3 in an Angular app.
I want the chart to be responsive so when the page is resized the chart width will increase and the height will be stay the same.
I'm doing this by capturing the window resize and then calling the function that draws the chart.
This works for the axis but I cant get the line and points to redraw.
I think it's to do with the way I'm trying to us the update pattern
How can I use the update pattern to redraw this line graph
const that = this;
const valueline = d3.line()
.x(function (d, i) {
return that.x(d.date) + 0.5 * that.x.bandwidth();
})
.y(function (d) {
return that.y(d.value);
});
this.x.domain(data.map((d: any) => d.date));
this.y.domain(d3.extent(data, function (d) {
return d.value
}));
const thisLine = this.chart.append("path")
.data([data])
.attr("class", "line")
.attr("d", valueline);
const totalLength = thisLine.node().getTotalLength();
thisLine.attr("stroke-dasharray", totalLength + " " + totalLength)
.attr("stroke-dashoffset", totalLength);
thisLine.transition()
.duration(1500)
.attr("stroke-dashoffset", 0)
let circle = this.chart.selectAll("line-circle")
.data(data);
circle = circle
.enter()
.append("circle")
.attr("class", "line-circle")
.attr("r", 4)
.attr("cx", function (d) {
return that.x(d.date) + 0.5 * that.x.bandwidth();
})
.attr("cy", function (d) {
return that.y(d.value);
})
circle
.attr("r", 4)
.attr("cx", function (d) {
return that.x(d.date) + 0.5 * that.x.bandwidth();
})
.attr("cy", function (d) {
return that.y(d.value);
})
circle
.exit()
.remove()
You have problems in both circles' selection and the line selection.
The circles' selection:
You're selecting "line-circle". Instead of that, you have to select by class: ".line-circle";
You're reassigning the circle selection:
circle = circle.enter()//etc...
Don't do that, otherwise circle will point to the enter selection, not to the update selection anymore. Just do:
circle.enter()//etc...
The path:
You're appending a new path every time you call the function. Don't do that. Instead, select the existing path and change its d attribute, or append a new path if there is none. Both behaviours can be achieved with this code:
let thisLine = this.chart.selectAll(".line")
.data([data]);
thisLine = thisLine.enter()
.append("path")
.attr("class", "line")
.merge(thisLine)
.attr("d", valueline);
Here is your forked code: https://stackblitz.com/edit/basic-scatter-mt-vvdxqr?file=src/app/bar-chart.ts

Why is label not getting added to all my paths?

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

D3.js category color scale not working properly

So I have one view that creates paths and I'm trying to have it so that each line that I draw has a random color.
I'm currently doing this-
var color = d3.scale.category20();
//other code that does stuff
this.path = svg.append("path")
.attr("d", line(newData))
.style("stroke", function(d,i) {
var colorIndex = Math.random() * (20 - 0) + 0;
return color(colorIndex); })
.attr("fill","none")
.attr("class","line");
This does not draw lines with different colours. Further, when I do this
this.path = svg.append("path")
.attr("d", line(newData))
.style("stroke", function(d,i) {
return color(4); })
.attr("fill","none")
.attr("class","line");
The color is still blue.
Why is that happening?
This is happening because domain was not set -
d3.scale.category10() not behaving as expected
has the answer
I have set up a minimal fiddle to show you the proper way to set up the line function. I also changed the color scheme to category(10) to show more contrasting colors (you can still use category20 and see a difference in colors though). Here is the FIDDLE.
var lineFunction = d3.svg.line()
.x(function (d) {
return d.x;
})
.y(function (d) {
return d.y;
})
.interpolate("linear");

Categories

Resources