D3.js: image does not snap to projection during rotation
I re-project the image onto a canvas element using d3 d3.geo. I want to allow most predictions (Eckert, Polyconic, Orthographic, Albers, etc.). I let the user zoom and pan with the mouse / cursor.
Initially, the image displays correctly on the canvas. The zoom control seems to be working correctly. However, when I rotate the canvas element, the image seems to go out of the clipping projection. This causes a weird duplication effect that I find difficult to describe in words.
Here is a jsfiddle of this situation.
Edit: Examples of images :
Zoomed-out:
Zoomed-out-and-nudged (translated):
Edit # 2: Clearer example I made a fiddle with a different projection and with the code removed: http://jsfiddle.net/bB6hD/
Zoomed-out:
Zoomed-out-and-nudged:
I removed scaling and translation aspects entirely, so this seems to be the reason (at least indirectly) of the rotation.
I tried to follow this example: http://bl.ocks.org/mbostock/4329423 but with rotation and scaling added.
Below is the code similar to the script:
var scale = 1,
radius = 150;
var width = 300,
height = 300;
var projection = d3.geo.kavrayskiy7();
var path = d3.geo.path()
.projection(projection);
var canvas = d3.select("body").append("canvas")
.attr("width", width)
.attr("height", height);
var context = canvas.node().getContext("2d");
var image = new Image();
image.onload = onload;
image.crossOrigin = "anonymous";
image.src = "https://dl.dropboxusercontent.com/s/j8v1nvyz6gmo8u7/tooth-color-300x300.jpg";
function onload() {
var dx = image.width,
dy = image.height;
context.drawImage(image, 0, 0, dx, dy);
var sourceData = context.getImageData(0, 0, dx, dy).data,
target = context.createImageData(width, height),
targetData = target.data;
for (var y = 0, i = -1; y < height; ++y) {
for (var x = 0; x < width; ++x) {
var p = projection.invert([x, y]),
λ = p[0],
φ = p[1];
if (λ > 180 || λ < -180 || φ > 90 || φ < -90) {
i += 4;
continue;
}
var q = ((90 - φ) / 180 * dy | 0) * dx + ((180 + λ) / 360 * dx | 0) << 2;
targetData[++i] = sourceData[q];
targetData[++i] = sourceData[++q];
targetData[++i] = sourceData[++q];
targetData[++i] = 255;
}
}
context.clearRect(0, 0, width, height);
context.putImageData(target, 0, 0);
}
var zoom = d3.behavior.zoom()
.center([width / 2, height / 2])
.on("zoomstart", function () {
console.log('zoom');
m0 = [d3.event.sourceEvent.pageX, d3.event.sourceEvent.pageY];
var proj = projection.rotate();
o0 = [-proj[0], -proj[1]];
d3.event.sourceEvent.stopPropagation();
})
.on("zoom", function () {
if (m0) {
var constant = (scale < 4) ? 4 : scale * 2;
var m1 = [d3.event.sourceEvent.pageX, d3.event.sourceEvent.pageY],
o1 = [o0[0] + (m0[0] - m1[0]) / constant, o0[1] + (m1[1] - m0[1]) / constant];
}
rotate = [-o1[0], -o1[1]];
scale = (d3.event.scale >= .25) ? d3.event.scale : .25;
projection.scale((radius - 2) * scale).rotate(rotate).translate([width / 2, height / 2]).precision(2);
context.clearRect(0, 0, width, height);
onload();
});
d3.select('canvas').call(zoom);
source to share
No one has answered this question yet
See similar questions:
or similar: