javascript - Omvandla X- och Y-koordinater inne i en (roterad) ImageOverlay till latitud och longitud
Efter att ha läst igenom Projections dokumentation och gjort vissa försök och misstag, har jag funnit mig själv oförmögen att hitta en lösning på detta problem.
Alla lösningar jag hittar leker med det faktum att x- och y-koordinaterna är i förhållande till Mercator-kartprojektionen, men i det här fallet har jag dem från en lokal bild placerad på ett lager över kartan.
Låt oss anta att jag har en 400px * 200px grå bild, placerad på en leaflet-karta med ImageOverlay.Rotated, på positionen:
var topleft = L.latLng(41.604766, 0.606402);
var topRight = L.latLng(41.605107, 0.606858);
var bottomleft = L.latLng(41.604610, 0.606613);
Denna bild placeras på kartan med hjälp av koordinaterna övre vänster, övre höger och nedre vänster som referens.
Resultatet är en rektangel som är något roterad mot norr.
Om jag visste att jag lämnade min telefon på pixel 100,50 (det vänstra hörnet av den ursprungliga bilden), hur kunde jag omvandla denna kartesiska koordinat till en latitud och longitud för att min karta ska visa den?
om det var en rektangel placerad perfekt i linje med norr, antar jag att jag skulle kunna få skillnaden i º mellan topLeft- och bottomLeft-hörnen, dela det med antalet pixlar, och sedan multiplicera resultatet med min telefons Y för att få latituden. med det tidigare värdet skulle jag multiplicera det med min telefons X för att få min telefons longitud.
men eftersom formen är roterad, världen inte är platt, och jag var verkligen dålig på geografi och trigonometri, är jag inte säker på att det är tillräckligt precist.
i slutändan pratar vi om en telefon, inte en byggnad. några meter bort (2-4) är okej, en kvarterslängd bort är det inte.
Svar #1
Åh, du pratar om min egen Leaflet.ImageOverlay.Rotated. Jag hjälper gärna till.
Du borde titta på dess källkod. All matematiken finns där. Låt oss gå igenom några bitar av den koden:
var pxTopLeft = this._map.latLngToLayerPoint(this._topLeft);
var pxTopRight = this._map.latLngToLayerPoint(this._topRight);
var pxBottomLeft = this._map.latLngToLayerPoint(this._bottomLeft);
Detta konverterar de tre lat-long-koordinaterna till skärmrelativa pixlar. Leaflet-handledningen om att skapa underklasser av L.Layer har en bra förklaring av vad "lagringspunkt" betyder.
Du kan ersätta dessa beräkningar med vilka andra reprojektioner du vill, förutsatt att du arbetar i ett plan och inte på en geoid. Med andra ord: du kanske vill omvandla dina lat-long-koordinater till EPSG:3857 sfärisk mercator-koordinater (vilket är den projektion som Leaflet använder för visning). Om du gör det kan du senare konvertera en interpolerad EPSG:3857-koordinat till en EPSG:4326 "vanlig latitud-longitud"-koordinat.
// Calculate the skew angles, both in X and Y
var vectorX = pxTopRight.subtract(pxTopLeft);
var vectorY = pxBottomLeft.subtract(pxTopLeft);
CSS-transformeringarna behöver snedställningsvinklarna för att förvränga bilden korrekt. Det är kanske kontraintuitivt, men ImageOverlay.Rotated använder CSS:s skew() snarare än rotate. Men du behöver inte snedställningsvinklarna, du behöver bara de där differentiella vektorerna.
Om du vill visualisera det, är vectorX en 2D-vektor som går längs den övre sidan av din bild (från vänster till höger), och vectorY är en 2D-vektor som går längs vänstra sidan (från toppen till botten).
Dessa vektorer gör det möjligt för dig att få skärmkoordinaten för vilken punkt som helst i bilden, förutsatt att ingången är i intervallet ([0..1], [0..1]) (med 0 som toppen eller vänster av bilden, och 1 som botten eller höger). Men du vill förmodligen inte arbeta med siffror i förhållande till bildens höjd/bredd, eftersom du i din fråga nämner pixlar. Så låt oss ta pixlar.
var imgW = this._rawImage.width;
var imgH = this._rawImage.height;
Bra. Vi har extraherat från ImageOverlay.Rotated alla matematikdelar som krävs.
Nu, om du delar de tidigare vektorerna med bildens dimensioner i pixlar...
var vectorXperPx = vectorX.divideBy(imgW);
var vectorYperPx = vectorY.divideBy(imgW);
Dessa vektorer går från en pixel i din ursprungliga bild till en pixel antingen till vänster (x) eller under (y). Så given en pixel (x,y) i den ursprungliga bilden, kommer den projicerade vektorn i förhållande till bildens hörn att vara:
var deltaVector =
xPixelCoordinate.scaleBy(vectorXPerPx)
.add(yPixelCoordinate.scaleBy(vectorYPerPx))
Det är vektorn, i skärmkoordinater, från det övre vänstra hörnet av din bild till (xPixelCoordinate, yPixelCoordinate)-pixeln i din bild.
Lägg nu till skärmkoordinaten för bildens övre vänstra hörn, så är du redo:
var finalCoordinateForImagePixel = pxTopLeft.add(deltaVector);
Eftersom vi använde latLngToLayerPoint, kommer resultatet att vara relativt den referensramen. Vill du få tillbaka det? Lätt:
var finalCoordinateForImagePixelInLatLong =
map.layerPointToLatLng(finalCoordinateForImagePixel);
men med tanke på att formen är roterad, att världen inte är platt, och att jag var riktigt dålig på geografi och trigonometri, är jag inte säker på att det är tillräckligt exakt.
Om du använder det koordinatreferenssystem som Leaflet använder för visning (EPSG:3857), eller något homomorfiskt koordinatsystem (pixlkoordinater i förhållande till skärmen, pixlkoordinater i förhållande till lagrets ursprungspunkt), kommer du inte ha några problem.
