【地图篇】leafletJs 图标按实际尺寸缩放

328 阅读2分钟

问题难点

二维地图上绘制marker图标,在地图缩放时改变图标大小,并且要求尽可能贴近实际尺寸(米)。


大家好,我是webber老丁。今天给大家介绍如何根据缩放比例实时调整地图上的图标大小,听起来很简单,只要监听地图的缩放事件,改一改尺寸不就完了? 是的,那么问题来了,怎么根据地图层级来控制尺寸呢,两者的变化关系是什么,尤其是要精确到米的情况下?

以一辆长度为5米,宽度2米的汽车为例,放到地图上并且时刻保持这个尺寸。

解决思路

1、认识地图

首先要简单了解一些地图的缩放原理,leaflet默认采用球面墨卡托投影(L.CRS.EPSG3857), 假设缩放层级zoom为0,世界地图为 256 x 256像素的正方形,当进入到缩放级别1时,宽高加倍,变成四个256x256像素 的图像,所以可以说世界高度、宽度都是2562zoom256 * 2^{zoom}像素.

img_v3_02kc_981b2486-c127-4465-a27a-39527c50c06g.jpg

2、上公式

在墨卡托投影中,地图的缩放层级 z 与像素和米的换算关系为:

1742204665676.png

其中:

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

20250317182912_rec_.gif

总结

以上就是按实际尺寸调整图标大小的过程,当然误差肯定会有,但相对来说完全可以满足需求。

另外注意投影方式不同,公式会有不同,例如:如果设置了crs为EPSG:4326,原理上讲 zoom为0时候,是256像素 x 2 构成了整个世界地图,所以你可以想想公式会有啥变化?

感谢阅读。