背景
互联⽹地图服务商的在线地图都通过⽡⽚的⽅式提供,称为。最常见的地图⽡⽚是图⽚格式的,现在有的地图服务商也提供了⽮量的⽡⽚数据,然后在⽤户端使⽤Canvas渲染成图⽚,如。
在进⾏地图开发时,为获取特定经纬度所在区域的⽡⽚和获取⽡⽚上像素点对应的经纬度,经常需要进⾏经纬度坐标与⽡⽚坐标、像素坐标的相互转换。本⽂将介绍⽡⽚坐标相关知识,并提供⾼德地图、百度地图、⾕歌地图的经纬度坐标与⽡⽚坐标的相互转换⽅法和转换类
库–。
主要经纬度坐标系
国际标准的经纬度坐标是WGS84,Open Street Map、外国版的Google Map都是采⽤WGS84;⾼德地图使⽤的坐标系是GCJ-02;百度地图使⽤的坐标系是BD-09。⾼德地图和百度地图都提供了在线的单向坐标转换接⼝,将其他坐标系换化到⾃⼰的坐标系,但这种转换受限于http url 请求字段长度和⽹络请求延迟,批量处理并不实⽤。离线相互转换可以通过开源JavaScript库实现,误差在10⽶左右。
虽然各地图服务商经纬度坐标系不同,但某⼀互联⽹地图的经纬度坐标与⽡⽚坐标相互转换只与该地图商的墨卡托投影和⽡⽚编号的定义有关,跟地图商采⽤的⼤地坐标系标准⽆关。
墨卡托投影
使⽤经纬度表⽰位置的⼤地坐标系虽然可以描述地球上点的位置,但是对于地图地理数据在⼆维平⾯内展⽰的场景,需要通过投影的⽅式将三维空间中的点映射到⼆维空间中。地图投影需要建⽴地球表⾯点与投影平⾯点的⼀⼀对应关系,在互联⽹地图中常使⽤墨卡托投影。墨卡托投影是荷兰地理学家墨卡托于1569年提出的⼀种地球投影⽅法,该⽅法是圆柱投影的⼀种。投影的更多内容,可以查看。
孙红雷的资料据我了解,各⼤地图服务商都采⽤了进⾏投影,⽡⽚坐标系的不同主要是投影截取的地球范围不同、⽡⽚坐标起点不同。
值得注意的是:
墨卡托投影并不是⼀种坐标系,⽽是为了在⼆维平⾯上展⽰三维地球⽽进⾏的⼀种空间映射。所以在GIS地图和互联⽹地图中,虽然⽤户看到的地图经过了墨卡托投影,但依然使⽤经纬度坐标来表⽰地球上点的位置。
在地图绘制和地图可视化时,就需要将地图数据使⽤投影的⽅式来呈现。
⽡⽚切割和⽡⽚坐标
对于经过墨卡托投影为平⾯的世界地图,在不同的地图分辨率(整个世界地图的像素⼤⼩)下,通过切割的⽅式将世界地图划分为像素为$256\times256$的地图单元,划分成的每⼀块地图单元称为地图⽡⽚。
地图⽡⽚具有以下特点:
具有唯⼀的⽡⽚等级(Level)和⽡⽚坐标编号(tileX, tileY)。
⽡⽚分辨率为256$\times$256。
最⼩的地图等级是0,此时世界地图只由⼀张⽡⽚组成。
⽡⽚等级越⾼,组成世界地图的⽡⽚数越多,可以展⽰的地图越详细。
某⼀⽡⽚等级地图的⽡⽚是由低⼀级的各⽡⽚切割成的4个⽡⽚组成,形成了⽡⽚⾦字塔。
⾼德地图⽡⽚坐标
坐标系定义
⾼德地图⽡⽚坐标与Google Map、Open Street Map相同。⾼德地图的墨卡托投影截取了纬度(约85.05ºS, 约85.05ºN)之间部分的地球,使得投影后的平⾯地图⽔平⽅向和垂直⽅向长度相等。将墨卡托投影地图的左上⾓作为⽡⽚坐标系起点,往左⽅向为X轴,X轴与北纬85.05º重合且⽅向向左;往下⽅向为Y轴,Y轴与东经180º(亦为西经180º)重合且⽅向向下。⽡⽚坐标最⼩等级为0级,此时平⾯地图是⼀个像素为256*256的⽡⽚。在某⼀⽡⽚层级Level下,⽡⽚坐标的X轴和Y轴各有$2^{Level}$个⽡⽚编号,⽡⽚地图上的⽡⽚总数为
$2^{Level}\times2^{Level}$。
如上图所⽰,此时X⽅向和Y⽅向各有4个⽡⽚编号,总⽡⽚数为16。中国⼤概位于⾼德⽡⽚坐标的(3,1)中。
坐标转换图解
从⾼德地图坐标转换图解中可以看出,⾼德地图的坐标转换具有以下特点:
所有坐标转换都在某⼀⽡⽚等级下进⾏,不同⽡⽚等级下的转换结果不同。
经纬度坐标可以直接转换为⽡⽚坐标和⽡⽚像素坐标。
染发的颜⽡⽚像素坐标需要结合其⽡⽚坐标才能得到该像素坐标的经纬度坐标。
坐标转换公式
⽅法参考:
经纬度坐标(lng, lat)转⽡⽚坐标(tileX, tileY):
$$tileX=\lfloor\frac{lng + 180}{360}\times{2^{Level}}\rfloor$$
$$tileY=\lfloor{(\frac{1}{2}-\frac{\ln(\tan(lat\times\pi/180)+\sec(lat\times\pi/180))}{2\timesπ}})\times{2^{Level}}\rfloor$$经纬度坐标(lng, lat)转像素坐标(pixelX, pixelY)
$$pixelX=\lfloor\frac{lng + 180}{360}\times{2^{Level}}\times256\%256\rfloor$$
$$pixelY=\lfloor{(1-\frac{\ln(\tan(lat\times\pi/180)+\sec(lat\times\pi/180))}{2\timesπ}})\times{2^{Level}}\times256\%256\rfloor$$⽡⽚(tileX, tileY)的像素坐标(pixelX, pixelY)转经纬度坐标(lng, lat)
$$lng=\frac{tileX+\frac{pixelX}{256}}{2^{Level}}\times360-180$$
$$lat=\arctan({\sinh({\pi-2\times\pi\times\frac{tileY+\frac{pixelY}{256}}{2^{Level}}})})\times\frac{180}{\pi}$$
百度地图⽡⽚坐标
坐标系定义
百度地图的⽡⽚坐标系定义与⾼德地图并不相同,其墨卡托投影的参数也不同。百度地图⽡⽚坐标以墨卡托投影地图中⾚道与0º经线相交位置为原点,沿着⾚道往左⽅向为X轴,沿着0º经线向上⽅向为Y轴。
百度⽡⽚坐标定义了另⼀种⼆维坐标系,称为百度平⾯坐标系。百度平⾯坐标系的坐标原点与百度⽡⽚坐标原点相同,以⽡⽚等级18级为基准,规定18级时百度平⾯坐标的⼀个单位等于屏幕上的⼀个像素。平⾯坐标与地图所展⽰的级别没有关系,也就是说在1级和18级下,同⼀个经纬度坐标的百度平⾯坐标都是⼀致的。
此时X⽅向和Y⽅向各有4个⽡⽚编号,但是外围的某些⽡⽚只有部分区域有地图或完全没有地图。没有地图的区域也可以认为其⽡⽚是⽆效的,即百度地图中X⽅向或Y⽅向的有效⽡⽚不⼀定达到$2^{Level}$个。
如何做数据分析表中国⼤概位于百度⽡⽚坐标的(0,0)中。
坐标转换图解马思纯整容前后
从百度地图坐标转换图解中可以看出,百度地图的坐标转换具有以下特点:
百度经纬度坐标与百度平⾯坐标可以直接相互转换,并且与⽡⽚地图等级⽆关。
经纬度坐标需要先转换为平⾯坐标,然后才能在某⼀⽡⽚等级下转换为⽡⽚坐标和⽡⽚像素坐标。
⽡⽚像素坐标需要结合其⽡⽚坐标才能得到该像素坐标的平⾯坐标,然后再转换为经纬度坐标。
坐标转换公式
⽅法参考:
发现百度JavaScript API的⼀个bug:
经纬度坐标(lng, lat)转平⾯坐标(pointX, pointY)
百度经纬度坐标与百度平⾯坐标的相互转换,并没有公开的公式,需要通过百度地图的API实现。
主要代码为:
1 2 3 4 5 6 7 8 9// 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
10 11}; }
平⾯坐标(pointX, pointY)转经纬度坐标(lng, lat)也需要通过百度地图的API实现。
主要代码为:
1 2 3 4 5 6 7 8 9 10pointToLnglat(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)
$$tileX=\lfloor\frac{pointX\times2^{Level-18}}{256}\rfloor$$
$$tileY=\lfloor\frac{pointY\times2^{Level-18}}{256}\rfloor$$
平⾯坐标(pointX, pointY)转像素坐标(pixelX, pixelY)
$$pixelX=\lfloor{pointX\times2^{Level-18}-\lfloor\frac{pointX\times2^{Level-18}}{256}\rfloor\times256}\rfloor$$
$$pixelY=\lfloor{pointY\times2^{Level-18}-{\lfloor\frac{pointY\times2^{Level-18}}{256}\rfloor\times256}}\rfloor$$⽡⽚(tileX, tileY)的像素坐标(pixelX, pixelY)转平⾯坐标(pointX, pointY)
$$pointX=\frac{tileX\times256+pixelX}{2^{Level-18}}$$
$$pointY=\frac{tileY\times256+pixelY}{2^{Level-18}}$$
经纬度坐标与⽡⽚坐标、像素坐标的相互转换,以平⾯坐标为中间量进⾏转换。
吐槽
百度地图JavaScript的代码⾮常奇葩,⾮常迷惑:
经纬度类是Point,平⾯坐标类是Pixel。
经纬度转平⾯坐标是lngLatToPoint,接收⼀个Point对象,返回⼀个Pixel对象。
平⾯坐标转经纬度坐标是在pointToLngLat,接收Pixel对象,返回⼀个Point对象。
WTF!
转换类库
⽡⽚地图等级范围
⽡⽚地图等级范围反映了地图可缩放的程度。
买什么空调好虽然最⼩的⽡⽚等级是0,但是部分地图并不提供0级或其他较⼩⽡⽚等级的地图,因为此时的世界地图将会很⼩,不能铺满⽤户设备窗⼝。
经过实际测试,各地图服务商的⽡⽚等级和测试链接如下:
百度图⽚⽡⽚的层级是[3~18]
百度主页的层级是[3~19]
⾼德图⽚⽡⽚的层级是[1~19]
⾼德地图官⽹介绍的:
获取当前地图缩放级别,在PC上,默认取值范围为[3,18];在移动设备上,默认取值范围为[3-19]
需注意的问题
佛牌禁忌⽡⽚像素坐标的起始点
⾼德地图、⾕歌地图的⽡⽚坐标起点在左上⾓,像素坐标(pixelX, pixelY)在⽡⽚中的起点为左上⾓。
百度地图中,像素坐标(pixelX, pixelY)的起点为左下⾓。
参考资料
⽡⽚地图服务
node-canvas实现百度地图个性化底图绘制
tile-lnglat-transform
coordtransform
地图投影的N种姿势
Web Mercator
Slippy map tilenames
百度地图API详解之地图坐标系统
百度JavaScript API中经纬度坐标转⽡⽚坐标bug ⾼德地图层级
完
发布评论