Oh, you're talking about my very own Leaflet.ImageOverlay.Rotated. I'll be happy to oblige.
You should have a look at its source code. All the maths are there. Let's go over some bits of that code:
var pxTopLeft = this._map.latLngToLayerPoint(this._topLeft);
var pxTopRight = this._map.latLngToLayerPoint(this._topRight);
var pxBottomLeft = this._map.latLngToLayerPoint(this._bottomLeft);
This is converting the three lat-long coordinates into screen-relative pixel coordinates. The Leaflet tutorial about creating subclasses of L.Layer
has a good explanation of what "layer point" means.
You can replace these calculations to any other reprojections you want, assuming that you're working in a plane and not on a geoid. In other words: you might want to transform your lat-long coordinates into EPSG:3857 spherical mercator coordinates (which is the display projection Leaflet uses). If you do so, you can later convert an interpolated EPSG:3857 coordinate into a EPSG:4326 "plain latitude-longitude" coordinate.
// Calculate the skew angles, both in X and Y
var vectorX = pxTopRight.subtract(pxTopLeft);
var vectorY = pxBottomLeft.subtract(pxTopLeft);
The CSS transforms need the skew angles to distort the image properly. It is perhaps counter-intuitive, but ImageOverlay.Rotated
uses CSS's skew()
rather than rotate
. But you don't need the skew angles, you just need those differential vectors.
If you want to visualize that, vectorX
is a 2D vector that goes along the top side of your image (from left to right), and vectorY
is a 2D vector that goes along the left side (from top to bottom).
Those vectors allow you to get the screen coordinate of any point in the image, assuming an input in the range of ([0..1], [0..1]) (with 0 being the top or left of the image, and 1 being the bottom or the right). But you probably don't want to work in numbers relative to the height/width of the image, because in your question you mention pixels. So let's grab pixels.
var imgW = this._rawImage.width;
var imgH = this._rawImage.height;
Good. We have extracted from ImageOverlay.Rotated
all the bits of math required.
Now, if you divide the previous vectors by the image dimensions in pixels...
var vectorXperPx = vectorX.divideBy(imgW);
var vectorYperPx = vectorY.divideBy(imgW);
These vectors go from one pixel of your original image to a pixel either on its left (x) or underneath (y). So given a pixel (x,y) of the original image, the projected vector relative to the image corner will be:
var deltaVector =
xPixelCoordinate.scaleBy(vectorXPerPx)
.add(yPixelCoordinate.scaleBy(vectorYPerPx))
That's the vector, in screen coordinates, from the top-left corner of your image to the (xPixelCoordinate, yPixelCoordinate) pixel of your image.
Now add the screen coordinate of the top-left corner of the image, and you're set:
var finalCoordinateForImagePixel = pxTopLeft.add(deltaVector);
As we used latLngToLayerPoint
, the result will be relative to that frame of reference. Want to get it back? Easy:
var finalCoordinateForImagePixelInLatLong =
map.layerPointToLatLng(finalCoordinateForImagePixel);
but given the shape is rotated, the world is not flat, and i was really bad at geography and trigonometry, i'm not sure about it being precise enough.
If you use the coordinate reference system that Leaflet uses for display (EPSG:3857), or any homomorphic coordinate systems (pixel coordinates relative to the screen, pixel coordinates relative to the layer origin point), you'll have no problem.