我正在将 iOS 应用程序移植到 Android,并使用
Google Maps Android API v2.
该应用程序需要在地图上绘制热图叠加层。
到目前为止,看起来最好的选择是使用
TileOverlay
并实现自定义 TileProvider
。 在方法getTile
中,我的方法给定了x、y和缩放,需要返回Tile
形式的位图。 到目前为止,一切都很好。
我有一组热图项目,我将使用它们将径向渐变绘制到位图上,每个热图项目都有一个纬度/经度。 我在执行以下两项任务时遇到问题:
谢谢您的帮助!
更新
感谢下面 MaciejGórski 的回答以及 marcin 的实现,我能够回答问题的第一部分,但我仍然需要第二部分的帮助。 为了澄清这一点,我需要一个函数来返回指定纬度/经度的图块的 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);
}
如有任何帮助,我们将不胜感激!
这对我有用:
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
在缩放级别 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), then into <-180,180).
调整最大值,使其不与下一个区域重叠。你可以跳过这个。
对于纬度来说,这会有点困难,因为谷歌地图使用墨卡托投影。
首先你缩放 y 就像它在范围内一样 <-180,180). Note that the values need to be reversed.
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);
这是凭记忆输入的,没有经过任何测试,所以如果有错误,请发表评论,我会修复它。
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;
}
科特林
private fun boundsOfTile(x: Int, y: Int, zoom: Int): LatLngBounds {
val noTiles = (1 shl zoom)
val longitudeSpan = 360.0 / noTiles
val longitudeMin = -180.0 + x * longitudeSpan
val mercatorMax = 180 - ((y.toDouble()) / noTiles) * 360
val mercatorMin = 180 - ((y.toDouble() + 1) / noTiles) * 360
val latitudeMax: Double = toLatitude(mercatorMax)
val latitudeMin: Double = toLatitude(mercatorMin)
val bounds = LatLngBounds(
LatLng(latitudeMin, longitudeMin),
LatLng(latitudeMax, longitudeMin + longitudeSpan)
)
return bounds
}
fun toLatitude(mercator: Double): Double {
val radians = atan(exp(mercator.toRadians()))
return (2 * radians).toDegrees() - 90
}
private fun Double.toRadians(): Double = Math.toRadians(this)
private fun Double.toDegrees(): Double = Math.toDegrees(this)
从其他答案转换并在 lat 52、lon 5 左右进行测试。
来自 2024 年的感谢和问候,@Marcin,@MaciejGórski