15

我將iOS應用程序移植到Android,並使用Google Maps Android API v2.應用程序需要在地圖上繪製熱圖覆蓋圖。TileProvider方法getTile - 需要將x和y轉換爲緯度/經度

到目前爲止,看起來最好的選擇是使用TileOverlay並實現自定義TileProvider。在方法getTile中,我的方法被給予x,y和縮放,並且需要返回Tile形式的位圖。到現在爲止還挺好。

我有一個熱圖項目的數組,我將用它來繪製徑向漸變到位圖上,每個都有一個lat/long。我在遇到以下兩項任務時遇到了問題:

  1. 如何確定由x,y和zoom表示的圖塊是否包含熱圖項目的緯度/經度?
  2. 如何將熱圖項目的lat/long轉換爲位圖的x/y座標。

謝謝你的幫忙!

UPDATE

得益於以下MaciejGórski的回答,和馬辛的實現我能得到我的問題回答了上半年,但我仍然需要與第二部分幫助。爲了澄清,我需要一個函數來返回指定經緯度的瓦片的x/y座標。我已經嘗試顛倒了MaciejGórski和Marcin的答案,沒有運氣的計算。

public static Point fromLatLng(LatLng latlng, int zoom){ 
    int noTiles = (1 << zoom); 
    double longitudeSpan = 360.0/noTiles; 
    double mercator = fromLatitude(latlng.latitude); 
    int y = ((int)(mercator/360 * noTiles)) + 180; 
    int x = (int)(latlng.longitude/longitudeSpan) + 180; 
    return new Point(x, y); 
} 

任何幫助表示讚賞!

+0

您是否發現將LatLng Z轉換爲XY的方法?我發現了一個功能,部分工作,但它給了我錯誤的y。 –

回答

8

在縮放級別0上,只有一個圖塊(x = 0,y = 0)。在下一個縮放級別中,瓦片數量增加了四倍(在x和y上加倍)。

這意味着在縮放水平W,x可能是範圍內的值< 0,1 < < W)。

從文檔:

瓦片的座標被從圖的左上(西北)角測量。在縮放級別N處,瓦片座標的x值範圍從0到2N-1並且從西向東增加,並且y值從0到2N-1範圍並且從北向南增加。

您可以使用簡單的計算來實現此目的。

對於經度,這是簡單的:

double longitudeMin = (((double) x)/(1 << zoom)) * 360 - 180; 
double longitudeMax = (((double) x + 1)/(1 << zoom)) * 360 - 180; 
longitudeMax = Double.longBitsToDouble(Double.doubleToLongBits(longitudeMax) - 1); // adjust 

這裏x被第一縮放成< 0,1),然後進入< -180,180)。

最大值被調整,所以它不會與下一個區域重疊。你可以跳過這個。

由於Google地圖使用墨卡託投影,因此對於緯度來說會更困難。

首先縮放y,就像它在範圍< -180,180)。請注意,這些值需要顛倒。

double mercatorMax = 180 - (((double) y)/(1 << zoom)) * 360; 
double mercatorMin = 180 - (((double) y + 1)/(1 << zoom)) * 360; 

現在你用一個神奇的功能,做墨卡託投影(從SphericalMercator.java):

public static double toLatitude(double mercator) { 
    double radians = Math.atan(Math.exp(Math.toRadians(mercator))); 
    return Math.toDegrees(2 * radians) - 90; 
} 

latitudeMax = SphericalMercator.toLatitude(mercatorMax); 
latitudeMin = SphericalMercator.toLatitude(mercatorMin); 
latitudeMin = Double.longBitsToDouble(Double.doubleToLongBits(latitudeMin) + 1); 

這是從內存類型和以任何方式並沒有進行測試,所以如果有錯誤,請發表評論,我會解決它。

+1

何李廢話!我有點期待內置的功能,我不知道。如果需要這麼多的代碼,那麼TileOverlay可能不是在地圖上繪製的正確方法。 Google Maps Javascript v3具有內置的功能,iOS MKOverlayView也是如此。 – azcoastal

+0

在審查你的答案時,我注意到你正在計算經度從「x」以及緯度。這是一個錯字嗎?我猜你的意思是:double mercatorMax = 180 - (((double)y)/(1 << zoom))* 360;另外,是否有類似的方式來將經緯度轉換爲x/y? – azcoastal

+0

@azcoastal是的。這是一個複製粘貼錯誤。我修正了這個問題。對於經緯度,您只需按相反順序調用所有內容,從我在答案中的鏈接下的「SphericalMercator.fromLatitude」開始。實際上並不是那麼多的代碼。 –

7

MaciejGórski,如果你不介意的話(如果你這樣做,我會刪除這個帖子)我編譯你的代碼就可以使用方法:

private LatLngBounds boundsOfTile(int x, int y, int zoom) { 
    int noTiles = (1 << zoom); 
    double longitudeSpan = 360.0/noTiles; 
    double longitudeMin = -180.0 + x * longitudeSpan; 

    double mercatorMax = 180 - (((double) y)/noTiles) * 360; 
    double mercatorMin = 180 - (((double) y + 1)/noTiles) * 360; 
    double latitudeMax = toLatitude(mercatorMax); 
    double latitudeMin = toLatitude(mercatorMin); 

    LatLngBounds bounds = new LatLngBounds(new LatLng(latitudeMin, longitudeMin), new LatLng(latitudeMax, longitudeMin + longitudeSpan)); 
    return bounds; 
} 
+1

這很好。代碼是否返回正確的值?我還沒有機會測試它。 –

+0

據我所知:是的,它:) :) – marcin

+0

@marcin,我可能有這個倒退,所以糾正我,如果我錯了...由於LatLngBounds的構造函數尋找西南角作爲第一個參數,應該作爲第一參數是'新LatLng(latitudeMax,longitudeMin)',然後是'new LatLng(latitudeMin,longitudeMin + longitudeSpan)'作爲第二參數?我在想x&y代表瓷磚的西北角。 – azcoastal

9

這爲我工作:

double n = Math.pow(2, zoom); 
double longitudeMin = x/n * 360 -180; 
double lat_rad = Math.atan(Math.sinh(Math.PI * (1 - 2 * y/n))); 
double latitudeMin = lat_rad * 180/Math.PI; 

double longitudeMax = (x + 1)/n * 360 -180; 
lat_rad = Math.atan(Math.sinh(Math.PI * (1 - 2 * (y + 1)/n))); 
double latitudeMax = lat_rad * 180/Math.PI; 

參考文獻: http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames

+1

感謝您提供鏈接,該鏈接提供代碼片段,以便在大多數通用語言中執行這兩種操作! –

+0

'^ 1'謝謝你,從過去2天開始搜索:) –

相關問題