Ah, you're inquiring about my very own creation called Leaflet.ImageOverlay.Rotated. I'd be delighted to assist you.
Take a peek at the source code for all the mathematical details. Let's delve into a few sections of that code:
var pxTopLeft = this._map.latLngToLayerPoint(this._topLeft);
var pxTopRight = this._map.latLngToLayerPoint(this._topRight);
var pxBottomLeft = this._map.latLngToLayerPoint(this._bottomLeft);
These lines convert lat-long coordinates into screen-relative pixel coordinates. The Leaflet tutorial on subclass creation of L.Layer
provides a clear explanation of "layer point."
You can swap these calculations with any other reprojections you desire, as long as you're operating in a flat plane rather than on a geoid. In simpler terms, you might want to convert your lat-long coordinates into EPSG:3857 spherical mercator coordinates (Leaflet's display projection). Subsequently, you can convert an interpolated EPSG:3857 coordinate back to an EPSG:4326 "plain latitude-longitude" coordinate.
// Determine the skew angles, both in X and Y
var vectorX = pxTopRight.subtract(pxTopLeft);
var vectorY = pxBottomLeft.subtract(pxTopLeft);
CSS transforms require skew angles for proper image distortion. Surprisingly, ImageOverlay.Rotated
utilizes CSS's skew()
instead of rotate
. However, you only need those differential vectors, not the skew angles.
Visualize it this way: vectorX
represents a 2D vector across the top side of your image (left to right), while vectorY
signifies a 2D vector along the left side (top to bottom).
These vectors enable you to obtain the screen coordinate of any image point, assuming an input range of ([0..1], [0..1]) (0 being top or left, 1 being bottom or right). However, working with numbers relative to image height/width may not be ideal since you mentioned pixels. Let's work with pixels instead.
var imgW = this._rawImage.width;
var imgH = this._rawImage.height;
Excellent. We've extracted all necessary math components from ImageOverlay.Rotated
.
By dividing the previous vectors by image dimensions in pixels...
var vectorXperPx = vectorX.divideBy(imgW);
var vectorYperPx = vectorY.divideBy(imgW);
These vectors move from one pixel of your original image to a pixel either to its left (x) or beneath (y). So, for a pixel (x,y) in the original image, the projected vector from the image corner would be:
var deltaVector =
xPixelCoordinate.scaleBy(vectorXPerPx)
.add(yPixelCoordinate.scaleBy(vectorYPerPx))
This is the vector, in screen coordinates, from the image's top-left corner to the (xPixelCoordinate, yPixelCoordinate) pixel of your image.
Add the screen coordinate of the image's top-left corner, and you're good to go:
var finalCoordinateForImagePixel = pxTopLeft.add(deltaVector);
As we utilized latLngToLayerPoint
, the outcome will be relative to that particular frame of reference. Need to revert? Piece of cake:
var finalCoordinateForImagePixelInLatLong =
map.layerPointToLatLng(finalCoordinateForImagePixel);
Considering the rotated shape, the non-flat world, and my lack of expertise in geography and trigonometry, I'm uncertain about its precision.
If you employ 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's origin point), accuracy won't be an issue.