I am trying to modify this D3 example to use with my dataset
http://mbostock.github.io/d3/talk/20111116/airports.html
I think my problem is creating the array of coordinates for calculating the Voronoi polygons, but at this point I'm not sure.
I'm getting a Uncaught TypeError: Cannot read property '0' of undefined error that points to the line where I am calling the array. The code is live, please see here
http://cds.library.brown.edu/projects/mapping-genres/symbol-maps/brown-voronoi-map.html
The map displays just fine, but the data points, the radio button, and the voronoi lines do not appear (I'm not trying to show lines between data points, so that code has been removed).
Any thoughts or suggestions would be most appreciated. Many thanks!
There are a number of small things going on here which can be easily sorted out but some will require a bit of work. The first is that you're using a new version of d3 (v3) and the example you're trying to replicate is an older version. There have been significant changes between the versions on the mapping side of things. So you can either use an old version of d3 (v2 will work I think) or investigate the changes.
The Uncaught TypeError: Cannot read property '0' of undefined error is being generated because d3.geom.voronoi(positions) line is producing NaN. I'm not 100% sure why but you could just filter these out to get a temporary fix. A simple filter would be something like:
g.append("svg:path")
.attr("class", "cell")
.attr("d", function(d, i) {
if (polygons[i][0][0]) {
return "M" + polygons[i].join("L") + "Z";
}
})
with the if true when there is not a false value (NULL, NaN, etc) in the first element of the polygon. Please note that this is temporary fix while you investigate why you're getting NaN and this will not produce the correct voronoi polygons.
The fix for the data points is very simple, you just have to set the radius to something greater than 0 such as 10 (although I imagine that you might want to scale the dots to a property of your data like TotalPublished). For instance see the last line:
circles.selectAll("circle")
.data(locations.rows
.sort(function(a, b) { return b.TotalPublished - a.TotalPublished; }))
.enter().append("svg:circle")
.attr("transform", function(d) { return "translate(" + projection(d.coordinates) + ")"; })
.attr("r", 10);
The radio button (or checkbox) does not show up as it's not generated by the javascript (although it can be). In the example you refer to its generated in the html in this snippet:
<div style="position:absolute;bottom:0;font-size:18px;">
<input type="checkbox" id="voronoi"> <label for="voronoi">show Voronoi</label>
</div>
Once you've worked through this you'll need to get the voronoi lines working. I think with your code as is the voronoi lines won't show up as the checkbox is not checked. So you could comment these lines out and see what happens. A quick tip with paths is that if you want to just show the lines you need to set the fill style to none and the stroke to whatever colour you want. If you don't set the fill to none you'll just get a black screen.
Related
I've created a scatter graph using the following code as a basis:
http://bl.ocks.org/peterssonjonas/4a0e7cb8d23231243e0e
However I'd like to change the background of the tooltip either based upon the colour of the selected element, or by adding a data column related to colour (i.e. d.colour).
The code currently generates tooltip text based upon the selected element via the following lines:
var tip = d3.tip()
.attr("class", "d3-tip")
.offset([-10, 0])
.html(function(d) {
return xCat + ": " + d[xCat] + "<br>" + yCat + ": " + d[yCat];
});
I was hoping that by adding something like:
.style("background", function(d) { return d.colour; })
I'd be able to achieve this. However when I do this I find that d is undefined (by adding a console.log before returning).
I'm a super novice when it comes to this kind of thing, so any advice anyone could give me would be super helpful.
Thanks!
Here is a positive criticism: don't use d3-tip or any other plugin to create your tooltips. Create them yourself. That way, you can have better control over them and customise them the way you want.
Back to the question: without even looking at that plugin's documentation, you can select the element (in this case, a <div>) by class:
d3.select(".d3-tip").style("background-color", color(d[colorCat]));
Here is the updated bl.ocks: http://bl.ocks.org/GerardoFurtado/70f2608e455b61514cc96dff6fe41ea6/65c940cb987ae1cbda5dc352cda54a382a945ae8
Regarding the undefined: the tip.style is not receiving the datum when the event is fired, apparently only tip.html does. To be sure about that you have to check their source code.
I'm working on a visualization project in which one component is a line chart overlayed on a bar graph. I delayed the bar transitions at a time. I would like the line to transition similarly so each point on the line remains "attached" to the bar.
Here's the code for the line:
var line = d3.svg.line()
.x(function(d, i) {
return xScale(i) + 20;
})
.y(function(d) {
return h - yScale(parseFloat(d.performance));
});
svg1.append("svg:path").attr("d", line(dataset[0].months));
And here's where I transition it:
svg1.select("path")
.transition()
.ease("linear")
.duration(1000)
.attr("d", line(dataset[count].months));
I've seen other questions addressing d3 line transitions but none that seem to address my issue, so I hope I'm not a repeater. Thanks in advance!
Note: I did try putting the delay() function in after transition which didn't work. I'm assuming this is because the line is a single <path> instead of multiple <rect> elements...
So this fell off my radar for a while, but I had some time the other day and figured out one approach for doing the delayed transition...
Here is the pen I wrote. I generated some random data and created a simple line chart to showing stock prices to play around with. The trick here is instead of iterating through a selection of elements using transition, we iterate through the dataset updating it point by point and transitioning the line as we go:
dataset.forEach(function(item, index) {
let set = dataset.slice();
// Update the current point in the copy
set[index].price = newPrice();
stock_line.transition()
.delay(index * 500)
.attr('d', line_generator(set));
});
Admittedly this is a bit hacky and possibly overkill, you could just update the whole line at once. Also #Lars mentioned the possibility of using the stroke-dashoffset trick to accomplish this as well. I have played around with that method to animate drawing the line but I'm not sure how I'd use it to accomplish the same delayed animation shown in the pen. If there is a less hacky implementation please let me know and I'll update this answer.
I'm trying to make an chart using the default line plus bar chart, but I want to use two or more streams in the bars, is it possible?
Currently, when I try to do this, I got some trouble with the effects of the chart, and so I can't show properly the hover balloon of the bars, he always display the data of just one of the streams. But the main problem is the dates of x axis, displaying 1970's dates, when I remove the second stream of bars, the dates display well:
Anyone already tried to do this kind of chart successfully?
EDIT
Adding Fiddles:
Fiddle with two columns stream and messy dates
Fiddle with just one column stream and ok dates
I'm calling this kind of graph:
linePlusBarChart()
The problem with the dates is that your data contains timestamps (i.e. in seconds), but Javascript expects milliseconds. This is easily fixed by multiplying the values by 1000:
series.values = series.values.map(function (d) {
return {
x: d[0]*1000,
y: d[1]
}
});
The tooltip problem is actually a bug in NVD3 -- it's not meant to be used this way. The problem boils down to the mouseover handler assuming that the first item of the data is representative of what you want. You can fix this for your case by selecting the item by data point number modulo 2 (because there're two bars):
.on('mouseover', function(d,i) {
d3.select(this).classed('hover', true);
dispatch.elementMouseover({
point: d,
series: data[i%2],
pos: [x(getX(d,i)), y(getY(d,i))],
pointIndex: i,
seriesIndex: i%2,
e: d3.event
});
})
This will only work for exactly two bar series though. Updated jsfiddle with the modified NVD3 code here.
I have created a multi line chart with a simple animation. In the beginning there are no data and after clicking a button new values are emulated and the line "move" left. The move is animated using a "shift".
The problem occurs when the lines "fill" the whole graph area (that means there are y values for all x values) and then the lines are animated in a different way. It looks like the y values are animated on a curve, not slided to the left.
The animation works good for both axes:
svg.selectAll("g .x.axis")
.transition()
.duration(500)
.ease("linear")
.call(xAxis);
svg.selectAll("g .y.axis")
.transition()
.duration(500)
.ease("linear")
.call(yAxis);
And not for lines (this code helped me a lot)
svg.selectAll("g .city path")
.data(processedData).transition().duration(500)
.ease("linear")
.attr("d", function(d, i) { return line(d.values); })
.attr("transform", null);
The Fiddle is accessible here.
Thanks for help.
The problem is that you're deleting data when there is too much. The way d3 matches data to existing data (when you call the .data() function) is by index. That is, the first element in the array that you pass to .data() matches the first bound data element, regardless of what the data actually looks like.
What happens in your case is that as soon as you start deleting data, the individual data points are updated instead of shifted. That's why you're seeing the "squiggle" -- it's updating each data point to its new value, which is the value the data point to the right had before.
With the code you currently have, this is hard to fix because you are not matching the data for individual lines explicitly. I would recommend that you have a look at nested selections which allow you to draw multiple lines and still explicitly match the data for individual ones. The key is to use the optional second argument to .data() to supply a function that tells it how to match the data (see the documentation). This way you can tell it that some data points disappeared and the other ones should be shifted.
you can get around this problem in 2 step.
in function update() : redraw you .data() with the new point at the end but without remove the first old point (with animation), like that each key is the same before and after transition.
at the end of function update() : you can remove the old value and redraw .data() without animation.
I'm attempting to update a d3 multi-line graph by pulling data at 5 second intervals from a mySQL database using PHP. At the moment the graph is displaying but not updating - I know that there is something not right with my updateData function but have tried everything can think of and nothing seems to work. Can anyone please help?
https://gist.github.com/Majella/ab32fe0151fd487da3f6
UPDATE:
As you can see the x-axis line is only showing sporadically and some of the lines aren't lined up with the y-axis.
Updated gist:
https://gist.github.com/Majella/ab32fe0151fd487da3f6
UPDATE 2: For some bizarre reason the lines are changing colour - or moving completely not exactly sure. So while on graph above the lines are from top - blue, orange then white - when graph updating the blue might move to bottom with orange on top and white in middle etc - but happening randomly?
In your original drawing of the graph, you correctly use:
var parameter = svg.selectAll(".parameter")
.data(data, function(d) { return d.key; })
.enter().append("g")
.attr("class", "parameter");
which joins the data (data) to the elements (g.parameter)
During your update function, you will need to join the data again in order to perform updates, deletes, and adds of elements. The 3 little circles tutorial is an excellent place to learn more about this.
Anyway, in your update function, you may want something like this (untested):
// re-acquire joined data
var containers = svg.selectAll("g.parameter")
.data( data );
// update existing elements for this data
containers
.select( "path.line" )
.attr( "d", function(d) { return line(d.values); })