D3: Use brush to zoom to specific interval - javascript

please see the following D3 example:
https://bl.ocks.org/mbostock/f48fcdb929a620ed97877e4678ab15e6
In this example, the user is able to draw a brush on the screen and then the graph zooms to the region encapsulated by the brush. The way this example works is by updating the x and y scale domains based on the brush extent. You can see that in the brushended() function.
I am looking to achieve this same functionality. However, I do not want to update the x and y scale domains but want to update the zoom scale and translate. The following example achieves this for a map:
https://bl.ocks.org/mmazanec22/586ee6674f52840492c16cc194aacb1f
You can see this is the brushend() function. Here the writer calculates a scale and translation based on the brush extent and then uses them to set a new scale and translate for the zoom behaviour. I am looking to achieve this however for a scatter plot.
I have a written an example of what I have tried in this jsfiddle: https://jsfiddle.net/SSS123/uxdd760k/1/ - see the part where I do my scale and translate calculations below:
var extent = brush.extent();
// Get box coordinates
var lowerX = d3.min([extent[0][0], extent[1][0]]);
var upperX = d3.max([extent[0][0], extent[1][0]]);
var lowerY = d3.min([extent[0][1], extent[1][1]]);
var upperY = d3.max([extent[0][1], extent[1][1]]);
var scale = Math.max( ( width / (x(upperX) - x(lowerX)) ), ( height / (y(upperY) - y(lowerY)) ) );
graphContainer.transition()
.duration(750)
.call(zoom.scale(scale).translate([-x(lowerX), -y(upperY)]).event);
I believe this now works fine for the case when you draw a brush with a width the same size as the height i.e. if you try drawing a box that stretches from 0 to 5 on the xAxis and 0 to 5 on the yAxis then this will zoom in quite well.
However, if you draw a box with 0 - 5 on the xAxis but 0 - 30 on the yAxis then this will no longer calculate a sensible zoom scale.
Has anyone any advise on how to solve this?

Related

D3 radial bar chart adjust radius scaling

If one examines this block:
https://bl.ocks.org/mbostock/8d2112a115ad95f4a6848001389182fb
The gridlines are in increments of 20. However the radius of each gridline does not appear to be equal as it scales up:
I'm guessing there is some geometric justification for this, but that's not what I'm after for my chart. I only want aesthetics, I need the gridline circles to be equidistant from each other.
Question
Using Bostock's radial scale script as seen in the above block, is there any way to adjust the scaling of the radii? I want the scaling to be equidistant.
The only thing you need is to change this...
var y = d3.scaleRadial()
... for this:
var y = d3.scaleLinear()
Here is the bl.ocks with that change only: https://bl.ocks.org/GerardoFurtado/0a0b22d15c4e715e4c748335e37330fb/1670bbcdfdcbed6b6a0ae2a56d5f153570d969d1
PS: There is indeed a geometrical explanation for this: a circle with radius 2r has an area four times bigger than a circle with a radius r. That's why we always (at least in truthful charts) scale the circle's radius to the square root of the encoded datum. Well, you mentioned that "I only want aesthetics". As a data visualisation specialist/enthusiast who happens to be a D3 programmer, not the other way around, I suggest you reconsider your approach and keep the radial scale. Charts that prioritise aesthetics over information are normally bad charts, and charts that impose aesthetics ignoring information are simply untruthful charts.

Zoom extent issue in d3 axis

I am using d3 axis to show the bar graph and i have added zoom interaction to it.
I am adding zoom interaction like this,
d3.zoom(xScale).on("zoom", jQuery.proxy(function(){
//Do somthing on zooming.
},this));
},this));
On Zooming the axis i have to get the at what level the axis is zoomed,
suppose consider i have time axis on zooming it will show the labels in day level, after zooming again it will show the axis in hour level. So how to get whether it is zoomed in day level or hour level after zooming?
Assuming you are targeting d3 version 4, the best way to handle this is with the zoom functionality's rescaleX. Say we have the following zoom handler where x is the scale tied to the x-axis:
function zoomed() {
var newScale = d3.event.transform.rescaleX(x));
console.log(newScale.domain());
}
Would log the bounds (min/max) of your zoomed axis.

Scale is being ignored by d3.behavior.zoom()

I have setup this fiddle to try to figure out why the scale that I pass to d3.behavior.zoom() is being ignored. I am setting the scale to 10 but the effective scale is 1. Then if I try to pan the grid the scale jumps to 10. Same thing happens when I try to zoom the grid. The scale jumps to 10 and then the additional scale is applied. What am I doing wrong? How can I work this out in order to have an effective initial scale value?
You're initializing a var scale = 10 to represent that initial zoom, and you're applying that to zoomer via .scale(10). So zoomer knows the initial scale, but nothing in your code is doing the work of applying the initial scale to the d3 selection vis. The first time the scale is actually applied is once the user interacts, which in turn calls function zoom() {...}, where the scale is finally applied.
The time/place to apply the initial scale, is when you create vis:
//create a group that will hold all the content to be zoomed
var vis = graph.append("svg:g")
.attr("class", "plotting-area")
.attr("transform", "scale(" + scale + ")");// <-- THIS
Here's an updated fiddle

d3.js: pan with limits

I'm working on a basic linear chart with pan functionality.
I managed to limit the extent to which the chart elements can be dragged by limiting the d3.event.translate values:
var tx = Math.max(0, d3.event.translate[0]),
ty = Math.min(0, d3.event.translate[1]);
All I need now is to limit the x and y axes accordingly. See the example:
http://jsfiddle.net/Q2SWV/
The bars on the chart are limited by 0 when dragging down or to the left. The x and y axes aren't. Any ideas on how to fix the axis problem?
You're very close, but you're missing the final step of updating the zoom behavior with your updated translation coordinates. This will fix your problem since both axes are updated using the zoom. Add the following right after determining tx and ty:
zoom.translate([tx, ty]);
This will apply the limits to your axes. See updated fiddle here: http://jsfiddle.net/mdml/nZD3E/.

Pinch zoom on touch devices

I have a graph rendered within the HTML5 canvas. The working is good till this point. Now I need to implement pinch zoom on the graph for touch devices. The logic is as the two finger stretches apart the graph zooms in and as the finger moves together the graph zooms out. In this case we need to constantly update the axis value. The problem here is how do we get the individual X and Y axis value of both the fingers and then calculate the amount of zoom to be done. As for example, for zooming using mouse we can get the start X and Y value on mouse down and on mouse up we get the end X and Y axis value. Using this start and end value of X and Y axis the graph can be zoomed accordingly. The canvas should not zoom in/out. The zoom in can be infinite but the zoom out will be till the default plotting of the graph. Any idea or help would be really appreciable. I am not getting the proper calculation.
I have implemented it in the following way. Any suggestions are welcome.
First I have taken the screen co-ordinates of the two fingers touch on start and converted it to its corresponding view co-ordinates by calculating its distance from the topmost and leftmost position. After that I have calculated the scale and new co-ordinates for X-axis in the following way.
Let
d1 and d2 be the dataspace coordinates of the initial/starting touches.
newx1 and newx2 are the x positions of the new touches.
screenW is the current screen width (i.e. width of plot in screen space).
Then
scale = (d2 - d1) / (newx2 - newx1)
If we use newd1, newd2 to denote the new datarange min and max values that we're trying to compute:
newd1 = d1 - newx1 * scale
newd2 = newd1 + screenW * scale
Similarly, we can do the calculation for new datarange min and max values of Y-axis.
Variant A:
Take a JavaScript library like Hammer.js which abstracts away all then event handling and gives you an event in case of a pinch. This should look like this:
var element = document.getElementById('test_el');
var hammertime = Hammer(element).on("pinchout", function(event) {
console.log("Zoom out!!");
// event.scale should contain the scaling factor for the zoom
});
Variant B:
Read about the touch events and how to identify if it is a multitouch. Figure out when it is a pinch and if how far the fingers have moved. There is nice write up here.

Categories