I'm trying to take the contents of a canvas element (which really is just an image loaded onto the canvas) and distort them into different map projections using d3. So for I've found exactly one example that does this (this other SO question).
The problem is that it doesn't work with every projection. The code:
var height = 375,
width = 750;
var canvas = document.createElement('canvas');
document.body.appendChild(canvas);
canvas.width = width;
canvas.height = height;
var context = canvas.getContext('2d');
var projection = d3.geo.lagrange()
.translate([width/2, height/2])
.scale(100); //SET SCALE HERE
var path = d3.geo.path().projection(projection);
var image = new Image();
image.crossOrigin = 'anonymous';
image.src = 'http://i.imgur.com/zZkxbz7.png';
image.onload = function() {
var dx = width,
dy = height;
context.drawImage(image, 0, 0, dx, dy);
var sourceData = context.getImageData(0, 0, dx, dy).data,
target = context.createImageData(dx, dy),
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]), //ERROR HERE
λ = 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);
}
In the above example, if I set the scale of the projection too low (say 80), then the variable p (in the for loop) ends up being null
. I'm not sure why this happens and I need to set the scale so that the projection fits within the canvas area.
A working jsfiddle example: http://jsfiddle.net/vjnfyd8t/
(This is an interesting question)
I can't quite decipher what's at play here, but FWIW, this is the implementation of the Lagrange invert()
method. Evidently, there are cases when it returns null, presumably whenever x or y are outside some extents (x,y are pre-transformed here). So I guess you're supposed to ignore the nulls (by e.g. setting the output pixel to transparent), and you can still get a proper scaled map at the end.
This is what I get by just ignoring null
s (but highlighting them in red, to illustrate where it happens)
if (!p) {
targetData[++i] = 255;
targetData[++i] = 0;
targetData[++i] = 0;
targetData[++i] = 255;
continue;
}
If the scale is set to 94, the red band disappears completely and it seems like a best fit (at least at that scale). Is that what you wanted?
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With