转载:http://blog.csdn.net/wudiazu/article/details/76597294
本文将介绍瓦片坐标相关知识,并提供高德地图、百度地图、谷歌地图的经纬度坐标与瓦片坐标的相互转换方法和类库。
互联网地图服务商的在线地图都通过瓦片的方式提供,称为瓦片地图服务。最常见的地图瓦片是图片格式的,现在有的地图服务商也提供了矢量的瓦片数据,然后在用户端使用Canvas渲染成图片,如node-canvas实现百度地图个性化底图绘制。
在进行地图开发时,为获取特定经纬度所在区域的瓦片和获取瓦片上像素点对应的经纬度,经常需要进行经纬度坐标与瓦片坐标、像素坐标的相互转换。本文将介绍瓦片坐标相关知识,并提供高德地图、百度地图、谷歌地图的经纬度坐标与瓦片坐标的相互转换方法和转换类库--tile-lnglat-transform。
国际标准的经纬度坐标是WGS84,Open Street Map、外国版的Google Map都是采用WGS84;高德地图使用的坐标系是GCJ-02;百度地图使用的坐标系是BD-09。高德地图和百度地图都提供了在线的单向坐标转换接口,将其他坐标系换化到自己的坐标系,但这种转换受限于http url请求字段长度和网络请求延迟,批量处理并不实用。离线相互转换可以通过开源JavaScript库coordtransform实现,误差在10米左右。
虽然各地图服务商经纬度坐标系不同,但某一互联网地图的经纬度坐标与瓦片坐标相互转换只与该地图商的墨卡托投影和瓦片编号的定义有关,跟地图商采用的大地坐标系标准无关。
使用经纬度表示位置的大地坐标系虽然可以描述地球上点的位置,但是对于地图地理数据在二维平面内展示的场景,需要通过投影的方式将三维空间中的点映射到二维空间中。地图投影需要建立地球表面点与投影平面点的一一对应关系,在互联网地图中常使用墨卡托投影。墨卡托投影是荷兰地理学家墨卡托于1569年提出的一种地球投影方法,该方法是圆柱投影的一种。投影的更多内容,可以查看地图投影的N种姿势。
墨卡托投影示意图.jpg
据我了解,各大地图服务商都采用了Web Mercator进行投影,瓦片坐标系的不同主要是投影截取的地球范围不同、瓦片坐标起点不同。
墨卡托投影并不是一种坐标系,而是为了在二维平面上展示三维地球而进行的一种空间映射。所以在GIS地图和互联网地图中,虽然用户看到的地图经过了墨卡托投影,但依然使用经纬度坐标来表示地球上点的位置。
在地图绘制和地图可视化时,就需要将地图数据使用投影的方式来呈现。
具有唯一的瓦片等级(Level)和瓦片坐标编号(tileX, tileY)。
瓦片等级越高,组成世界地图的瓦片数越多,可以展示的地图越详细。
某一瓦片等级地图的瓦片是由低一级的各瓦片切割成的4个瓦片组成,形成了瓦片金字塔。
如上图所示,此时X方向和Y方向各有4个瓦片编号,总瓦片数为16。中国大概位于高德瓦片坐标的(3,1)中。
从高德地图坐标转换图解中可以看出,高德地图的坐标转换具有以下特点:
所有坐标转换都在某一瓦片等级下进行,不同瓦片等级下的转换结果不同。
瓦片像素坐标需要结合其瓦片坐标才能得到该像素坐标的经纬度坐标。
经纬度坐标(lng, lat)转瓦片坐标(tileX, tileY):
经纬度坐标(lng, lat)转像素坐标(pixelX, pixelY)
瓦片(tileX, tileY)的像素坐标(pixelX, pixelY)转经纬度坐标(lng, lat)
从百度地图坐标转换图解中可以看出,百度地图的坐标转换具有以下特点:
百度经纬度坐标与百度平面坐标可以直接相互转换,并且与瓦片地图等级无关。
经纬度坐标需要先转换为平面坐标,然后才能在某一瓦片等级下转换为瓦片坐标和瓦片像素坐标。
瓦片像素坐标需要结合其瓦片坐标才能得到该像素坐标的平面坐标,然后再转换为经纬度坐标。
方法参考:百度地图API详解之地图坐标系统
发现百度javascript API的一个bug:百度JavaScript API中经纬度坐标转瓦片坐标bug
经纬度坐标(lng, lat)转平面坐标(pointX, pointY)
百度经纬度坐标与百度平面坐标的相互转换,并没有公开的公式,需要通过百度地图的API实现。
主要代码为:
// Bmap为百度JavaScript API V2.0的地图对象
lnglatToPoint(longitude, latitude) {
let projection = new BMap.MercatorProjection();
let lnglat = new BMap.Point(longitude, latitude);
let point = projection.lngLatToPoint(lnglat);
return {
pointX: point.x,
pointY: point.y
};
}
平面坐标(pointX, pointY)转经纬度坐标(lng, lat)
也需要通过百度地图的API实现。
主要代码为:
pointToLnglat(pointX, pointY) {
let projection = new BMap.MercatorProjection();
let point = new BMap.Pixel(pointX, pointY);
let lnglat = projection.pointToLngLat(point);
return {
lng: lnglat.lng,
lat: lnglat.lat
};
}
平面坐标(pointX, pointY)转瓦片坐标(tileX, tileY)
平面坐标(pointX, pointY)转像素坐标(pixelX, pixelY)
瓦片(tileX, tileY)的像素坐标(pixelX, pixelY)转平面坐标(pointX, pointY)
经纬度坐标与瓦片坐标、像素坐标的相互转换,以平面坐标为中间量进行转换。
百度地图JavaScript的代码非常奇葩,非常迷惑:
经纬度类是Point,平面坐标类是Pixel。
经纬度转平面坐标是lngLatToPoint,接收一个Point对象,返回一个Pixel对象。
平面坐标转经纬度坐标是在pointToLngLat,接收Pixel对象,返回一个Point对象。
WTF!
本文笔者根据前文介绍的经纬度坐标与瓦片坐标、像素坐标相互转换规则,编写了一个javascript类库--tile-lnglat-transform,提供了高德地图、百度地图、谷歌地图的经纬度坐标与瓦片坐标的相互转换。该模块是使用了UMD模块打包方式,可以在node和broswer中使用。
类库地址:https://github.com/CntChen/tile-lnglat-transform
该类库的详细信息及使用方法请在项目主页中查看。
虽然最小的瓦片等级是0,但是部分地图并不提供0级或其他较小瓦片等级的地图,因为此时的世界地图将会很小,不能铺满用户设备窗口。
百度图片瓦片的层级是[3~18]
http://online1.map.bdimg.com/onlinelabel/?qt=tile&x=49310&y=10242&z=18
百度主页的层级是[3~19]
高德图片瓦片的层级是[1~19]
http://wprd03.is.autonavi.com/appmaptile?style=7&x=427289&y=227618&z=19
高德地图官网介绍的高德地图层级:
获取当前地图缩放级别,在PC上,默认取值范围为[3,18];在移动设备上,默认取值范围为[3-19]
谷歌地图瓦片层级是[0~21]
http://mt2.google.cn/vt/lyrs=m@167000000&hl=zh-CN&gl=cn&x=1709157&y=910472&z=21&s=Galil
高德地图、谷歌地图的瓦片坐标起点在左上角,像素坐标(pixelX, pixelY)在瓦片中的起点为左上角。
百度地图中,像素坐标(pixelX, pixelY)的起点为左下角。
天地图的切片规则是这样的,l=1时,整幅地图(全球地图)被切为两片,如图(l=1):
https://en.wikipedia.org/wiki/Tile_Map_Service
node-canvas实现百度地图个性化底图绘制
http://www.cnblogs.com/well1010/articles/baidu-map-node-canvas.html
tile-lnglat-transform
https://github.com/CntChen/tile-lnglat-transform
coordtransform
https://github.com/wandergis/coordtransform
地图投影的N种姿势
http://blog.sina.com.cn/s/blog_517eed9f0102w4rm.html
Web Mercator
https://en.wikipedia.org/wiki/Web_Mercator
Slippy map tilenames
http://wiki.openstreetmap.org/wiki/Slippy_map_tilenames
百度地图API详解之地图坐标系统
http://www.cnblogs.com/jz1108/archive/2011/07/02/2095376.html
百度JavaScript API中经纬度坐标转瓦片坐标bug
http://cntchen.github.io/2016/05/09/百度JavaScirpt%20%20API中经纬度坐标转瓦片坐标bug/
高德地图层级