I have a large dataset in an array which translates into about a million 6-pixel wide bars,
Given that about 130 bars fit into 800 pixels of screen, I need to only display a portion of the data at a time. Then, when the user pans the chart, new data will be added and non-visible data will be removed. The user could pan right or left so data need to be able to enter and exit from both sides of the chart.
I feel like the solution involves D3's enter and exit, but I'm unsure how to implement it.
One idea I've had is to use Array.prototype.concat and redraw on pan. Another idea would be to use Array.prototype.slice. I hope those are fast enough.
Any examples?
There are no examples that I'm aware of. Roughly, you would need to do the following.
Draw the initial bar chart. Pass in only the data you'll display (i.e. the first 130 data points if your screen is 800px wide).
On pan, get the translation for the pan. The x translation corresponds to a certain number of bars. Take off this many data points from the beginning of your data array and add that many to the end.
Pass that new data array to D3 and redraw. This tutorial should give you some pointers. Remember to pass a key function to .data() so it knows how to match data to DOM elements.
Similar thing on subsequent pans. Figure out how many bars have been panned and modify the data array accordingly (e.g. using slice and concat -- doesn't matter that much). Redraw the bars.
The concept is very similar to what's done in the tutorial I've linked to, except that the redraw isn't triggered by a timer, but by the pan event.
There are fundamentally two ways to do this. One is that you draw all 1M bars using svg and rely on svg's built in panning. The other is that you draw 130 bars and redraw each time the user pans. enter() and exit() are useful for doing things to data elements in your set that are entering or exiting the scene since the last time it rendered. (d3 determines which elements are
"new" by calling a predicate function on each element. The function has a default but can be user supplied.) You don't actually have to use them here unless you want to be fancy. You can just selectAll the rects from last draw, and .data() them with your new data, and supply the usual calls for drawing rects.
Related
I have a graph that has zoom and pan from this example code: (https://bl.ocks.org/mbostock/34f08d5e11952a80609169b7917d4172). My graph is almost exactly like that one, except with different data points, and circles noting each data point.
Now I'm trying to allow for a mouseover function that will display the data for each data point when hovering. However, the zoom behavior is taking precedence, and I'm not sure how to switch the behaviors so that the zoom is listening and the mouseover/hover happens (first, maybe?).
I'm trying to use this code sample since it has both the hover and zoom behaviors: (https://bl.ocks.org/lorenzopub/013c0c41f9ffab4d27f860127f79c5f5)
Adding
d3.select("#rect").style("pointer-events", "all")
Have solved my problem.
I think the mouse events where not propagated properly.
I have map data for all of the towns in a state -- let's say California. I display every single town on a map using d3 -- everything is good so far. The projection I use makes use of the scale() and translate() functions, among others, to show my map how I want -- it looks nice.
Here is my problem. I need an automated way to expand my map projection so that it uses the entire svg area. In other words, I want to make my map bigger and bigger until either its width exactly equals the svg width or its height exactly equals the svg height.
I know the bounds function helps do this for an individual feature, but I want the entire state, not the individual town features, to fill the svg area.
Thank you for your help!
I have a general question (I know I should present specific code with a problem, but in my case the problem is of a more general nature).
In Processing, let's say I make an ellipse:
ellipse(30, 30, 10, 10);
Now, is there a way to get the pixels where this ellipse is on the canvas? The reason would be to have a way of creating user interaction with the mouse (for instance). So when someone clicks the mouse over the ellipse, something happens.
I thought of turning everything into objects and use a constructor to somehow store the position of the shape, but this is easier said than done, particularly for more complex shapes. And that is what I am interested in. It's one thing to calculate the position of an ellipse, but what about more complex shapes? Are there any libraries?
Check out the geomerative library. It has a way to check whether the mouse is inside any SVG shape. I can't remember off the top of my head but it works something like you make a shape:
myShape = RG.loadShape("shape.svg");
and a point:
RPoint p = new RPoint(mouseX, mouseY);
and the boolean function contains() will tell you if the point is inside the shape:
myShape.contains(p);
It's better to use a mathematical formula than pixel-by-pixel checking of the mouse position (it's much faster, and involves less code).
For a perfect circle, you can calculate the Euclidean distance using Pythagoras' theorem. Assume your circle is centred at position (circleX,circleY), and has a radius (not diameter) of circleR. You can check if the mouse is over the circle like this:
if(sq(mouseX-circleX)+sq(mouseY-circleY) <= sq(circleR)) {
// mouse is over circle
} else {
// mouse is not over circle
}
This approach basically imagines a right-angled triangle, where the hypotenuse (the longest side) runs from the centre of the circle to the mouse position. It uses Pythagoras' theorem to calculate the length of that hypotenuse, and if it's less than the circle's radius then the mouse is inside the circle. (It includes a slight optimisation though -- it's comparing squares to avoid doing a square root, as that can be comparatively slow.)
An alternative to my original mathematical answer also occurred to me. If you can afford the memory and processing power of drawing all your UI elements twice then you can get good results by using a secondary buffer.
The principle involves having an off-screen graphics buffer (e.g. using PGraphics). It must be exactly the same size as the main display, and have anti-aliasing disabled. Draw all your interactive UI elements (buttons etc.) to this buffer. However, instead of drawing them the normal way, give each one a unique colour which it uses for fill and stroke (don't add any text or images... just solid colours). For example, one button might be entirely red, and another entirely green. Any other RGB value works, as long as each item has a unique colour. Make sure the background has a unique colour too.
The user never sees that buffer, so don't draw it to the screen (unless you're debugging or something). When you want to detect what item the mouse is over, just lookup the mouse position on that off-screen buffer. Get the pixel colour at that location, and match it to the UI element.
After you've done all that, just go ahead and draw everything to the main display as normal.
It's worth noting that you can cut-down the processing time of this approach a lot if your UI elements never (or rarely) move. You only need to redraw the secondary buffer when something appears/disappears, animates, or changes size/position.
I want to draw marks in custom positions, but what is the best event or procedure for it?
Because if I process them in OnAfterDraw event my chart will be rendered twice and etc.
Is it possible to set the position before drawing the chart?
You need the series to have been drawn at least once (so its marks' positions have been populated) to be able to modify these positions.
So I am using jqPlot to display distance (y-axis) over time (x-axis) in a simple line graph.
However, while I have actual distances as the underlying data used to position the points, I'm not interested in displaying those distances on the y-axis.
Instead, there are a series of landmarks at given distances, that I want to appear on the y-axis as a 'tick' next to the appropriate point for the distance to that landmark. That way the line appears to "pass" each landmark as it travels upward.
Currently I'm "faking" this by hiding the ticks and putting a manually-created series of labels next to the graph. This works well enough, but I had to disable vertical zooming because if the user chose to zoom in, the labels would not match up with the actual distances. My users would really like to zoom in vertically, however, and I want to allow them to.
Therefore, does anyone know of a way (a plugin or similar) that would allow me to associate custom labels with given tick marks in jqPlot, that will match up nicely and respond to zooming? It would be necessary to hide some of the landmarks if the graph is zoomed out too much, so that would have to be a feature.
Alternatively, if someone knows of a "zoom event" that passes in the min and max y values, I could probably recreate my manual labels with that data, so let me know if you know of a way to get that information. I haven't been able to find one.
I would need to see a bit of your code to customize it to the distance but to start with you could label the ticks and it will display over the size of the chart.
axes:{yaxis:{ticks:['DiscanceA','DiscanceB', 'DiscanceC', 'DiscanceD', 'DiscanceE'],
renderer: $.jqplot.CategoryAxisRenderer,},
Let me know how it goes and if you have any code lets see it!