17

我正在将一个 iOS 应用程序移植到 Android,并使用Google Maps Android API v2. 该应用程序需要在地图上绘制一个热图叠加层。

到目前为止,看起来最好的选择是使用 aTileOverlay并实现一个 custom TileProvider。在方法getTile中,我的方法给定了x、y和zoom,需要返回a形式的位图Tile。到目前为止,一切都很好。

我有一组热图项目,我将使用它们在位图​​上绘制径向渐变,每个都有一个纬度/经度。我在执行以下两项任务时遇到问题:

  1. 如何确定由 x、y 和缩放表示的图块是否包含热图项目的纬度/经度?
  2. 如何将热图项目的纬度/经度转换为位图的 x/y 坐标。

谢谢您的帮助!

更新

感谢下面 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);
}

任何帮助表示赞赏!

4

3 回答 3

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

于 2013-10-01T12:27:15.753 回答
9

在缩放级别 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)。

最大值被调整,因此它不会与下一个区域重叠。你可以跳过这个。

对于纬度,这会有点困难,因为谷歌地图使用墨卡托投影。

首先你缩放 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);

这是从记忆中输入的,没有经过任何测试,所以如果那里有错误,请发表评论,我会修复它。

于 2013-06-04T17:10:28.303 回答
8

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;
}
于 2013-06-05T09:41:02.017 回答