With this code as a starting point: https://bl.ocks.org/d3noob/15e4f2a49d0d25468e76ab6717cd95e7 I'm attempting to make a simple line graph with multiple data series. Per #wasserholz suggestion, I've added a complete MVP: https://github.com/djmcmath/d3-fail-mvp
The data parsing portion seems to be working correctly, in that I'm creating a map with 5 elements, each of which is a "sailing date" and array of values associated with that date. This is the variable named "group_by_sailing_date".
The axes appear to be reasonable: For the X-axis, I'm taking the "since_midnight" value, pulling the extents, and formatting it as a time. The Y-axis, similarly, is just the extents of the "margin" value. I get this -- so far so good:
Next, I want to add some lines to my chart. My thinking is that I iterate through the map, and for each of the map elements, I add the element as data to a series. What I get is a gigantic "Nothing Happens" though. No lines, no errors, just "Gosh, your data looks great, but I'm going to ignore it."
//line generator?
var valueline = d3.line()
.x(function(d) { return x(d.since_midnight); })
.y(function(d) { return y(d.margin); });
group_by_sailing_date.forEach(function (s) {
svg_summ.append("path")
.data(s)
.attr("stroke", "steelblue")
.attr("stroke-width", "3px")
.attr("d", valueline);
});
I feel like I'm missing something really fundamental here, but I'm drawing a complete blank (pun intended, ha ha ha). Help?
Following the rules of selection within d3.js (https://bost.ocks.org/mike/selection/) the code should look something like this:
svg_summ.selectAll("path")
.data(group_by_sailing_date)
.enter()
.append("path")
.attr("stroke", "steelblue")
.attr("fill", "none")
.attr("stroke-width", "1px")
.attr("d", (d) => valueline(d));
Ok, I've made it work, but I don't know why. I removed the .data(s) line, and changed "valueline" to "valueline(s)". No idea why this works. Technically, it's all good, but I'd be thrilled if someone could help me understand what this code actually means.
group_by_sailing_date.forEach(function (s) {
svg_summ.append("path")
.attr("stroke", "steelblue")
.attr("fill", "none")
.attr("stroke-width", "1px")
.attr("d", valueline(s));
});
Related
I am trying to draw a line between two lat/long points on a US map. I have circles indicating the origin and destination, however when I try to draw the line nothing is appearing. No errors are being thrown.
I'm able to manually draw a line between those points by writing:
svg.selectAll("line")
.data(data)
.enter()
.append("line")
.attr("x1", projection(data[0])[0])
.attr("y1", projection(data[0])[1])
.attr("x2", projection(data[1])[0])
.attr("y2", projection(data[1])[1])
.attr("stroke-width", 1)
.attr("stroke", "black");
But this isn't a sustainable approach since my dataset will consist of thousands of lat/long pairs.
Hoping someone can point my code in the right direction. I hope I'm close since no errors are being thrown. Or maybe I'm taking a horrible approach to drawing lines between points, in which case someone let me know before I am too far down the rabbit hole. Here is a working Plunker to my code.
Thanks in advance.
Plunker: http://plnkr.co/edit/ooLkR8iec5NKx0Ns1zcD?p=preview
You seem to understand the data join based on your creation of the circles
svg.selectAll("circle")
.data(points).enter()
.append("circle")
.attr("cx", function (d) {return projection(d)[0]; })
.attr("cy", function (d) { return projection(d)[1]; })
There's no reason you cannot do the same for lines.
svg.selectAll("line")
.data(data)
.enter()
.append("line")
.attr("x1", d=>projection(d[0])[0])
.attr("y1", d=>projection(d[0])[1])
.attr("x2", d=>projection(d[1])[0])
.attr("y2", d=>projection(d[1])[1])
Notice what i did to the initial data structure, and how I modified it to work with circles vs lines.
By the way, the attribute d is for the path element only.
It is quite easy to enter data in d3 as long as all of the data are in one file or in one variable like this:
svg.selectAll("path").data(datavariable).enter()
.append("path")
....
I need to enter data stored in separate variables into one graph. I tried
svg.selectAll("path").data(datavariable1).enter()
.append("path")
....
svg.selectAll("path").data(datavariable2).enter()
.append("path")
....
It doesn't seem to work. I also tried svg.selectAll("path1")... and svg.selectAll("path.datavariable1")..., but it just plots the second path over the first using the same data (datavariable1). Does anyone know how to get data from two variables to work on one graph at the same time?
I figured it out with the help of Nixie's comment. This is what worked for me. For the paths I didn't need to svg.selectAll anything. line1 and line2 get the data from data variables:
var line1 = d3.line()
.x(function(d,i) { return x(datavariable1[i].xvalue); })
.y(function(d,i) { return y(datavariable1[i].yvalue); });
var line2 = d3.line()
.x(function(d,i) { return x(datavariable2[i].xvalue); })
.y(function(d,i) { return y(datavariable2[i].yvalue); });
Then I just appended SVG as follows:
svg.append("path")
.attr("class", "line")
.attr("d", line1(datavariable1))
svg.append("path")
.attr("class", "line")
.attr("d", line2(datavariable2))
Answer in Nixie's comment to my question wouldn't work in my case because path has to stay path for the line graph, but it works for a dot graph. Hopefully this helps someone who might run into similar issue.
I'm implementing a reactive line chart in meteor.js based on this example line chart. In the code that I lifted for that chart, I have the following block, which works fine.
var paths = svg.selectAll("path.line")
.data([dataset]);
paths
.enter()
.append("path")
.attr("class", "line")
.attr('d', line);
paths
.attr('d', line);
paths
.exit()
.remove();
However, when I try writing something like the following, the axes still show, but the path does not render. Why the heck could that be?
var paths = svg.selectAll("path.line")
.data([dataset])
.enter()
.append("path")
.attr("class", "line")
.attr('d', line)
.exit()
.remove();
It's because you're calling the functions on different objects. D3 returns update, enter, and exit selections from calls to .data() -- this is what you're storing in paths in the first code block. Then you get the enter, update, and exit selections and handle them.
In the second code block, you're calling .enter() you're handling the enter selection afterwards. That is, all the code after the .enter() is being applied to the enter selection and not to the other selections as well as before.
So the .exit().remove() is being called on the newly-appended path elements (which should give you an error) instead of the return value of .data() as in the first block of code.
I have used D3 to make a map of the US and filled in the colors
var map = d3.select("#map").append("svg")
.attr("width", svgWidth)
.attr("height", svgHeight);
d3.json("us.json", function (error, us) {
map.append("g")
.selectAll("path")
.data(topojson.feature(us, us.objects.states).features)
.enter().append("path")
.attr("d", path)
.style("stroke", function (d) { return "#000000"; } )
.style("fill", function (d) { return gradient(Math.random()); } )
};
Now, I want to change the color of each state but rather than removing the map and then re-adding it, I would like to transition the colors.
I have tried:
d3.selectAll("#map").selectAll("g").selectAll("path")
But then trying to loop through the elements of this array does not help.
What am I doing wrong?
EDIT:
The code I am using to try and change the colors of each state (each path variable) is...
d3.select(this).transition().style("fill", gradient(Math.random()));
I do not believe the problem has to do with the code above - it's the code I am trying to use to select the paths/states that is giving me trouble.
I have also tried
d3.selectAll("path").attr("fill", function (d) { ... });
But, that too, did not do anything. :(
As Lars said in the comments, since I used "style" before, I have to do it again [instead of using attr as I was before].
Selecting the data as I did (d3.selectAll("path")) is the correct way to select the states.
I'm trying to implement the d3 multi-series line chart example at http://bl.ocks.org/mbostock/3884955.
But I'm having a problem when using my own data - as the lines don't seem to match the legend/ values in the JSON data. For example at 10:00, the "high" line should be measuring 3512 "threats" on the y-axis.
city.append("path")
.attr("class", "line")
.attr("d", function(d) { return line(d.values); }) // Problem here?
.style("stroke", function(d) { return color(d.name); });
Please take a look at this js fiddle:- http://jsfiddle.net/Ca8Uj/
Many thanks.
You're seeing this behaviour because you're using the basis interpolation on your lines, which smooths out peaks (ie the line is not guaranteed to go through each point).
If you change to:
var line = d3.svg.line()
.interpolate("linear")
You should see the correct values. Alternatively to get a smooth-ish line that shows correct exact y-values you could use monotone. To experiment with interpolations, have a look at: http://bl.ocks.org/mbostock/4342190