TileProvider 方法 getTile - 需要将 x 和 y 转换为 lat/long

Posted

技术标签:

【中文标题】TileProvider 方法 getTile - 需要将 x 和 y 转换为 lat/long【英文标题】:TileProvider method getTile - need to translate x and y to lat/long 【发布时间】:2013-05-28 20:26:33 【问题描述】:

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

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

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

    如何确定由 x、y 和缩放表示的图块是否包含热图项的纬度/经度? 如何将热图项的纬度/经度转换为位图的 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);

感谢任何帮助!

【问题讨论】:

您找到将 LatLng Z 转换为 X Y 的方法了吗?我找到了一个部分有效的功能,但它给了我错误的 y。 【参考方案1】:

这对我有用:

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

【讨论】:

感谢您提供的链接,它提供代码 sn-ps 以在最常用的语言中执行这两种操作!【参考方案2】:

在缩放级别 0 上,只有一个图块 (x=0,y=0)。在下一个缩放级别上,图块数量翻了两番(x 和 y 翻倍)。

这意味着在缩放级别W 上,x 可能是

来自文档:

图块的坐标是从地图的左上角(西北)测量的。在缩放级别 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首先缩放为

调整最大值,使其不与下一个区域重叠。你可以跳过这个。

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

首先你缩放 y 就像它在

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);

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

【讨论】:

何李废话!我有点期待一个我不知道的内置功能。如果需要这么多代码,那么 TileOverlay 可能不是在地图上绘制的正确方法。 Google Maps javascript v3 已内置此功能,iOS MKOverlayView 也是如此。 在查看您的答案时,我注意到您正在计算“x”的经度以及纬度。那是错字吗?我猜你的意思是: double mercatorMax = 180 - (((double) y) / (1 @azcoastal 是的。这是一个复制粘贴错误。我已经解决了。对于 lat/lng,您只需以相反的顺序调用所有内容,从我输入答案的链接下的 SphericalMercator.fromLatitude 开始。实际上并没有那么多代码。 我不想问,但您介意展示一下您所说的“逆序”是什么意思吗?在您的帮助下,我应该能够迅速扭转局面并接受答案。 @azcoastal 这意味着如果你想从y = x + 1计算x,你把x放在左边,把其他的都放在右边:-x = -y + 1; x = y - 1 并从原始代码的最后一行到第一行执行此操作。【参考方案3】:

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;

【讨论】:

没关系。代码是否返回正确的值?我还没有机会测试它。 据我所知:是的 :) @marcin,我可能有这个倒退,所以如果我错了请纠正我......因为 LatLngBounds 的构造函数正在寻找西南角作为第一个参数,它应该是'new LatLng (latitudeMax, longitudeMin)' 作为第一个参数,然后 'new LatLng(latitudeMin, longitudeMin + longitudeSpan)' 作为第二个参数?我在想 x & y 代表瓷砖的西北角。 纬度(以度为单位)从西到东,从南到北增加 - 所以西南点将是new LatLng(latitudeMin, longitudeMin),东北点将是new LatLng(latitudeMax, longitudeMax),其中longitudeMax=longitudeMin+longitudeSpan @azcoastal x &amp; y represent the NW corner of the tile No. x, y & zoom 代表整个图块。它就像一个标识符。

以上是关于TileProvider 方法 getTile - 需要将 x 和 y 转换为 lat/long的主要内容,如果未能解决你的问题,请参考以下文章

在 android 中扩展 google map api v2 的离线矢量 TileProvider

如何用deepearth加载百度瓦片地图

求个Java实现,3维度高效cache的思路

天地图瓦片服务地址

cesium加载geoserver 发布的wmts服务

影像加载篇:WebMapTileServiceImageryProvider