2012-05-04 90 views
8

我有一個使用Google靜態地圖的JAVA項目,經過數小時和數小時的工作後,我無法找到工作,我會解釋所有內容,並希望有人會能夠幫助我。從Google Static Map獲取像素座標的lon/lat

我使用的是靜態地圖(480像素X 480像素),地圖的中心是LAT = 47,經度= 1.5和縮放級別爲5

現在我需要的是能夠得到緯度和經度什麼當我點擊這張靜態地圖上的一個像素時。經過一番搜索之後,我發現我應該使用墨卡託投影(右?),我還發現每個縮放級別的水平和垂直尺寸的精度加倍,但我找不到合適的公式來鏈接像素,縮放級別和緯度/ LON ...

我的問題是隻有從像素獲得經/緯度,瞭解該中心的COORDS和像素縮放級別...

預先感謝您!

+0

是否有鏈接到您瞭解墨卡託投影的頁面? –

回答

1

谷歌地圖使用地圖瓷磚有效地將世界劃分爲256^21像素的瓷磚網格。基本上這個世界是由最低變焦的4塊瓷磚組成的。當你開始放大時,你會得到16個瓷磚,然後是64個瓷磚,然後是256個瓷磚。它基本上是一個四叉樹。因爲這樣的1d結構只能使2d變平,所以你還需要一個mercantor投影或者轉換成WGS 84.這裏有一個很好的資源Convert long/lat to pixel x/y on a given picture。 Google地圖中有一個功能,可以將lat-long對轉換爲pixel。這是一個鏈接,但它說瓷磚只有128x128:http://michal.guerquin.com/googlemaps.html

  1. Google Maps V3 - How to calculate the zoom level for a given bounds
  2. http://www.physicsforums.com/showthread.php?t=455491
+0

事實上,我不關心瓷磚,我正在用Java來做這個項目,所以我擁有一張我知道它的大小(480像素×480像素)的圖片,並且我知道它的中心座標(lat = 47 ,lon = 1.5,所以中心的像素是240x240)及其縮放級別(5)。 所有我需要的是獲得任何像素座標的公式... 在此先感謝 – user1374021

2

使用墨卡託投影。

如果你的項目進入[0, 256)空間由[0,256]

LatLng(47,=1.5) is Point(129.06666666666666, 90.04191318303863) 

縮放級別爲5,這等同於像素座標:

x = 129.06666666666666 * 2^5 = 4130 
y = 90.04191318303863 * 2^5 = 2881 

因此,地圖的左上角是:

x = 4130 - 480/2 = 4070 
y = 2881 - 480/2 = 2641 

4070/2^5 = 127.1875 
2641/2^5 = 82.53125 

最後:

Point(127.1875, 82.53125) is LatLng(53.72271667491848, -1.142578125) 
+0

感謝您的答覆,但我認爲出了問題。 的確,你說我的左上角位於英國的LatLng(53.72271667491848,-1.142578125),但事實上,我的地圖的左上角更多地出現在愛爾蘭,請看一看: http://i50.tinypic.com /9vb2b7.jpg 你可以看到我的窗口, - 在地圖上經緯度中心(47,1.5) - 地圖的大小是小480x480 - 地圖的縮放級別爲5 - >所以我想經緯度(47 ,1.5)==點(240,240)否? (這似乎是真的當我把我的鼠標點(240,240)我真的很接近LatLng(47,1.5)) 在此先感謝 – user1374021

+0

@ user1374021:舊線程,我知道,但我正在這個問題和這個答案是一個很大的幫助。問題是用數學來計算x;它應該是x = 4130 - 480/2 = 3890. y = 2641是正確的。當你通過這些數字時,你會得到-9.00的長度,與你的圖像相匹配。 – 4mla1fn

0

基於對克里斯·布羅德福特的回答上面和some other code on Stack Overflow for the Mercator Projection算算,我得到這個

public class MercatorProjection implements Projection { 

    private static final double DEFAULT_PROJECTION_WIDTH = 256; 
    private static final double DEFAULT_PROJECTION_HEIGHT = 256; 

    private double centerLatitude; 
    private double centerLongitude; 
    private int areaWidthPx; 
    private int areaHeightPx; 
    // the scale that we would need for the a projection to fit the given area into a world view (1 = global, expect it to be > 1) 
    private double areaScale; 

    private double projectionWidth; 
    private double projectionHeight; 
    private double pixelsPerLonDegree; 
    private double pixelsPerLonRadian; 

    private double projectionCenterPx; 
    private double projectionCenterPy; 

    public MercatorProjection(
      double centerLatitude, 
      double centerLongitude, 
      int areaWidthPx, 
      int areaHeightPx, 
      double areaScale 
    ) { 
     this.centerLatitude = centerLatitude; 
     this.centerLongitude = centerLongitude; 
     this.areaWidthPx = areaWidthPx; 
     this.areaHeightPx = areaHeightPx; 
     this.areaScale = areaScale; 

     // TODO stretch the projection to match to deformity at the center lat/lon? 
     this.projectionWidth = DEFAULT_PROJECTION_WIDTH; 
     this.projectionHeight = DEFAULT_PROJECTION_HEIGHT; 
     this.pixelsPerLonDegree = this.projectionWidth/360; 
     this.pixelsPerLonRadian = this.projectionWidth/(2 * Math.PI); 

     Point centerPoint = projectLocation(this.centerLatitude, this.centerLongitude); 
     this.projectionCenterPx = centerPoint.x * this.areaScale; 
     this.projectionCenterPy = centerPoint.y * this.areaScale; 
    } 

    @Override 
    public Location getLocation(int px, int py) { 
     double x = this.projectionCenterPx + (px - this.areaWidthPx/2); 
     double y = this.projectionCenterPy + (py - this.areaHeightPx/2); 

     return projectPx(x/this.areaScale, y/this.areaScale); 
    } 

    @Override 
    public Point getPoint(double latitude, double longitude) { 
     Point point = projectLocation(latitude, longitude); 

     double x = (point.x * this.areaScale - this.projectionCenterPx) + this.areaWidthPx/2; 
     double y = (point.y * this.areaScale - this.projectionCenterPy) + this.areaHeightPx/2; 

     return new Point(x, y); 
    } 

    // from https://stackoverflow.com/questions/12507274/how-to-get-bounds-of-a-google-static-map 

    Location projectPx(double px, double py) { 
     final double longitude = (px - this.projectionWidth/2)/this.pixelsPerLonDegree; 
     final double latitudeRadians = (py - this.projectionHeight/2)/-this.pixelsPerLonRadian; 
     final double latitude = rad2deg(2 * Math.atan(Math.exp(latitudeRadians)) - Math.PI/2); 
     return new Location() { 
      @Override 
      public double getLatitude() { 
       return latitude; 
      } 

      @Override 
      public double getLongitude() { 
       return longitude; 
      } 
     }; 
    } 

    Point projectLocation(double latitude, double longitude) { 
     double px = this.projectionWidth/2 + longitude * this.pixelsPerLonDegree; 
     double siny = Math.sin(deg2rad(latitude)); 
     double py = this.projectionHeight/2 + 0.5 * Math.log((1 + siny)/(1 - siny)) * -this.pixelsPerLonRadian; 
     Point result = new org.opencv.core.Point(px, py); 
     return result; 
    } 

    private double rad2deg(double rad) { 
     return (rad * 180)/Math.PI; 
    } 

    private double deg2rad(double deg) { 
     return (deg * Math.PI)/180; 
    } 
} 

下面是原來的答案

public class MercatorProjectionTest { 

    @Test 
    public void testExample() { 

     // tests against values in https://stackoverflow.com/questions/10442066/getting-lon-lat-from-pixel-coords-in-google-static-map 

     double centerLatitude = 47; 
     double centerLongitude = 1.5; 

     int areaWidth = 480; 
     int areaHeight = 480; 

     // google (static) maps zoom level 
     int zoom = 5; 

     MercatorProjection projection = new MercatorProjection(
       centerLatitude, 
       centerLongitude, 
       areaWidth, 
       areaHeight, 
       Math.pow(2, zoom) 
     ); 

     Point centerPoint = projection.projectLocation(centerLatitude, centerLongitude); 
     Assert.assertEquals(129.06666666666666, centerPoint.x, 0.001); 
     Assert.assertEquals(90.04191318303863, centerPoint.y, 0.001); 

     Location topLeftByProjection = projection.projectPx(127.1875, 82.53125); 
     Assert.assertEquals(53.72271667491848, topLeftByProjection.getLatitude(), 0.001); 
     Assert.assertEquals(-1.142578125, topLeftByProjection.getLongitude(), 0.001); 

     // NOTE sample has some pretty serious rounding errors 
     Location topLeftByPixel = projection.getLocation(0, 0); 
     Assert.assertEquals(53.72271667491848, topLeftByPixel.getLatitude(), 0.05); 
     // the math for this is wrong in the sample (see comments) 
     Assert.assertEquals(-9, topLeftByPixel.getLongitude(), 0.05); 

     Point reverseTopLeftBase = projection.projectLocation(topLeftByPixel.getLatitude(), topLeftByPixel.getLongitude()); 
     Assert.assertEquals(121.5625, reverseTopLeftBase.x, 0.1); 
     Assert.assertEquals(82.53125, reverseTopLeftBase.y, 0.1); 

     Point reverseTopLeft = projection.getPoint(topLeftByPixel.getLatitude(), topLeftByPixel.getLongitude()); 
     Assert.assertEquals(0, reverseTopLeft.x, 0.001); 
     Assert.assertEquals(0, reverseTopLeft.y, 0.001); 

     Location bottomRightLocation = projection.getLocation(areaWidth, areaHeight); 
     Point bottomRight = projection.getPoint(bottomRightLocation.getLatitude(), bottomRightLocation.getLongitude()); 
     Assert.assertEquals(areaWidth, bottomRight.x, 0.001); 
     Assert.assertEquals(areaHeight, bottomRight.y, 0.001); 
    } 

} 

如果你是一個單元測試(比如)與空中攝影一起工作時,我覺得算法沒有考慮到墨卡託投影的拉伸效應,所以如果你感興趣的區域不相對接近eq,它可能會失去準確性uator。我猜你可以通過將x座標乘以中心的cos(緯度)來近似它?

0

似乎值得一提的是,您實際上可以通過Google地圖API爲您提供緯向&縱向座標的像素座標。

雖然在V3中有點複雜,但這裏有一個如何實現它的例子。
(注:這是假設你已經有一張地圖和像素頂點轉換爲緯度& LNG座標):

let overlay = new google.maps.OverlayView(); 
overlay.draw = function() {}; 
overlay.onAdd = function() {}; 
overlay.onRemove = function() {}; 
overlay.setMap(map); 

let latlngObj = overlay.fromContainerPixelToLatLng(new google.maps.Point(pixelVertex.x, pixelVertex.y); 

overlay.setMap(null); //removes the overlay 

希望幫助別人。

更新:我意識到我做了這兩種方法,兩者仍然使用相同的方式來創建覆蓋(所以我不會重複該代碼)。

let point = new google.maps.Point(628.4160703464878, 244.02779437950872); 
console.log(point); 
let overlayProj = overlay.getProjection(); 
console.log(overlayProj); 
let latLngVar = overlayProj.fromContainerPixelToLatLng(point); 
console.log('the latitude is: '+latLngVar.lat()+' the longitude is: '+latLngVar.lng());