Scaling or scaling with d3.js
I created a graph with d3 to show surface defects. The surface itself is about 1000 mm wide, but can be several kilometers. To see the defects more clearly, I implemented d3 scaling, but sometimes the defects extend over the x-range, so scaling in this case will result in scrolling from left to right.
Here's a simplified jsFiddle
I could change the scale to see a specific defect, say one starts at 1000mm and ends at 1500mm, I could do:
var yScale = d3.scale.linear()
.domain([1000 - margin, 1500 + margin])
.range([0, pixelHeight]);
But since my defects are rectangles, I need to calculate the width using yScale like this:
.attr("height", function (d) {
return yScale(d.height);
})
Which won't work if I change the scale domain (the height can be less than the domain value, giving negative values).
So how can I solve this problem? Is there a way to calculate the height of the defect relative to yScale. Or is there another scalability option?
UPDATE Following Marks suggestion, I implemented it and made a second jsFiddle
The problem I am currently facing is also related to scale. I tried to fix this a bit, but once you pan or zoom the zoom functions (xScale and yScale) won't give the correct values ββ(mostly negative because it's from the viewport).
.on('click', function (d) {
d3.selectAll('rect').classed('selected-rect', false);
d3.select(this).classed('selected-rect', true);
var dx = xScale(d.width)*2.2,
dy = yScale(d.height)*2.2,
x = xScale(d.x) ,
y = yScale(d.y) ,
scale = .9 / Math.max(dx / width, dy / pixelHeight),
translate = [pixelWidth / 2 - (scale*1.033) * x,
pixelHeight / 2 - (scale*1.033) * y];
svg.transition()
.duration(750)
.call(zoom.translate(translate).scale(scale).event);
});
So, without panning or zooming and clicking directly, the above code works. Can anyone give me an idea of ββwhat I did wrong?
source to share
Ok I figured it out. You can scale and translate using the zoom function. Scaling to a bounding rectangular example is a good example , but this example is based on d3.geo without the x and y scaling functions.
With scaled objects x and y, you can do this:
.on('click', function (d) {
d3.selectAll('rect').classed('selected-rect', false);
d3.select(this).classed('selected-rect', true);
zoom.translate([0, 0]).scale(1).event;
var dx = xScale(d.width),
dy = yScale(d.height),
x = xScale(d.x) + xScale(d.width/2),
y = yScale(d.y) + yScale(d.height/2),
scale = .85 / Math.max(dx / pixelWidth, dy / pixelHeight),
translate = [pixelWidth / 2 - scale * x,
pixelHeight / 2 - scale * y];
svg.transition()
.duration(750)
.call(zoom.translate(translate).scale(scale).event);
});
First of all, you must reset translate and scale before doing any xScale and yScale calculations. I just do it with this statement:
zoom.translate([0, 0]).scale(1).event;
dx and dy - scaled width / height of objects. To get to the middle of the object, you need to take the scaled x and y value and add half the width and height to it. Otherwise it won't center properly (I say, of course, because I fiddled with it).
The scale is calculated by assuming the maximum width or height of the object, and the device has 0.85 divided by it to get a small margin around it.
Here's the jsFiddle
source to share