问题难点
二维地图上绘制marker图标,在地图缩放时改变图标大小,并且要求尽可能贴近实际尺寸(米)。
大家好,我是webber老丁。今天给大家介绍如何根据缩放比例实时调整地图上的图标大小,听起来很简单,只要监听地图的缩放事件,改一改尺寸不就完了? 是的,那么问题来了,怎么根据地图层级来控制尺寸呢,两者的变化关系是什么,尤其是要精确到米的情况下?
以一辆长度为5米,宽度2米的汽车为例,放到地图上并且时刻保持这个尺寸。
解决思路
1、认识地图
首先要简单了解一些地图的缩放原理,leaflet默认采用球面墨卡托投影(L.CRS.EPSG3857), 假设缩放层级zoom为0,世界地图为 256 x 256像素的正方形,当进入到缩放级别1时,宽高加倍,变成四个256x256像素 的图像,所以可以说世界高度、宽度都是像素.
2、上公式
在墨卡托投影中,地图的缩放层级 z 与像素和米的换算关系为:
其中:
- z 是缩放层级(zoom level)。
- 像素(px)是地图上的距离。
- 纬度(latitude)是当前地图中心的纬度(以弧度为单位)。
- 156543.03392 是 Web Mercator 投影在赤道处的每像素米数(zoom level 0)。 计算说明: 每像素米数=地图宽度 / 赤道周长 = 40075016.68 / 256 ≈ 156543.03392 米/像素
3、重置图标尺寸
根据公示,可以计算出宽5米,在层级为zoom, 纬度位置为 lat时候,像素尺寸为: 5 * Math.pow(2, zoom) / 156543.03392 / (lat * Math.PI / 180);
示例代码如下:
//一辆长度为5米,宽度2米的汽车
//初始化图标
let position = [39.898854,116.389860];
let marker = L.marker(position, {
icon: L.icon({
iconUrl: './car.png',
iconSize: [10, 25]
}),
});
/**
meter: 目标尺寸 米
lat: 位置中心纬度
*/
function resetMarker(meter,lat){
let zoom = map.getZoom();
let car_h = meter * Math.pow(2, zoom) / 156543.03392 / (lat * Math.PI / 180);
if(marker){
marker.setIcon(
L.icon({
iconUrl: './car.png',
iconSize: [car_h * 5 / 2, car_h]
})
);
}
}
map.on('zoom',()=>{
resetMarker(5,39.898854);
});
总结
以上就是按实际尺寸调整图标大小的过程,当然误差肯定会有,但相对来说完全可以满足需求。
另外注意投影方式不同,公式会有不同,例如:如果设置了crs为EPSG:4326,原理上讲 zoom为0时候,是256像素 x 2 构成了整个世界地图,所以你可以想想公式会有啥变化?
感谢阅读。