Zoom on graph jumps to random point with d3js - d3.js

I am learning d3js and trying to incorporate zooming/panning features on a graph, however on the initial zoom event it jumps to a random spot and zooms in. After that, the zooming and panning work as expected. Why is the initial event moving the starting point and adjusting the scale oddly?
Code and example here: bl.ocks.org/dbaileychess/7570631

This is because the zoom behaviour is attached to a different element than the one you're zooming. It determines the position of the mouse cursor relative to its own position and passes that information to the callback. If you're zooming a different element, the relative coordinates are no longer correct.
To fix, add the offset between the elements that the zoom behaviour is attached to and the elements you're zooming to the translation. This question has come up a number of times already, see e.g. here.

Related

d3 zoom and pan with hover functions

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.

D3.js zooming and panning - lock on y axis

I'm trying to build a stock chart with zooming functionality using D3.js
I'm looking to start with this example here and attempt to make the zoom feel more natural for a stock chart. A perfect example is this. So the difference as far as I understand is that zoomng and panning are both locked on the Y-axis, and the only way the Y-axis moves is to autmatically fill the price range of the currently visible data.
Another noticeable difference is that zooming does not zoom into the current position of the mouse like it does in the first example.
How can the example be adjusted to work more closely as the other chart? What is the pertitent code, how should it be changed?
Setting the zoom behaviour to not affect the y-axis is simple: just don't attach your y-scale to the zoom behaviour.
In the sample code you linked to, the zoom functionality is added in this line:
this.plot.call(d3.behavior.zoom()
.x(this.x)
.y(this.y)
.on("zoom", this.redraw() )
);
That creates a zoom behaviour object/function, links it to the graphs x and y scales, and tells it to call the function returned by this.redraw() after every zoom event. The zoom behaviour automatically changes the domain of the scales on zoom, and then the redraw function uses the modified zoom. If you don't give it a y scale to modify, then zooming won't affect the y domain.
Getting the y scale to automatically adjust to the given extent of the data is a little trickier. However, remember that the zoom behaviour will have automatically adjusted the domain of the x scale to represent the extent of visible data horizontally. You'll then have to get the corresponding slice of your data array and figure out the extent of y values from it, set your y domain accordingly, and then call the redraw function (remembering that this.redraw() just returns the redraw function, to call it within another function you'll need to use this.redraw()() ).
To have the zoom be independent of the mouse position, set a center point for the zoom behaviour.

d3 zooming to mouse location, not the centroid

I have a map of counties in a state.
I am using the code found here http://bl.ocks.org/mbostock/2206590 to implement zooming on my map.
The original example works like this:
This works well enough, the user clicks on a state and it is zoomed and centered using the state centroid. Is it possible to use the mouse location as the point about which to zoom? It would seem easy enough, but I'm not sure how to get the mouse coordinates.
Use d3.event.x and d3.event.y instead of the computed centroid. See here.

User interaction in Processing

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.

jqPlot axis with ticks labeled differently from underlying data

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!

Resources