Leaflet 地图可视化:轻量交互地图库
FreeGuideOnline
最新
2026-06-18
Leaflet 地图可视化入门
Leaflet 是构建交互式地图的首选开源 JavaScript 库。它设计简洁、性能优越、兼容性好,拥有丰富的插件生态,能以极少的代码实现功能强大的 Web 地图应用。本教程将带你从零开始,掌握 Leaflet 的核心概念与常用操作。
环境准备与第一个地图
直接在 HTML 文件中引入 Leaflet 的 CSS 和 JS 文件即可开始使用,无需安装任何工具。
<!DOCTYPE html>
<html>
<head>
<title>Leaflet 快速开始</title>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Leaflet CSS -->
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<!-- Leaflet JS -->
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>
<style>
#map { height: 500px; }
</style>
</head>
<body>
<div id="map"></div>
<script>
// 初始化地图,设置中心点与缩放级别
const map = L.map('map').setView([39.9042, 116.4074], 12);
// 添加一个瓦片图层(底图)
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
</script>
</body>
</html>
关键点解析:
L.map('map')将id="map"的 div 元素转化为地图容器,务必为该元素设置明确的高度。setView([纬度, 经度], 缩放级别)设定地图初始视图。缩放级别通常为 1(全球)到 18(街道级)。L.tileLayer加载栅格瓦片底图。示例使用 OpenStreetMap 的免费底图,attribution是必须的版权声明。
底图选择与定制
底图决定了地图的可视化风格。除了经典的 OpenStreetMap,你也可以通过瓦片 URL 模板接入其他风格的底图。
常用免费底图示例:
// 1. CartoDB 浅色底图
const cartoLight = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OSM</a> contributors © <a href="https://carto.com/attributions">CARTO</a>'
});
// 2. Stamen 水彩风格底图
const stamenWatercolor = L.tileLayer('https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}.jpg', {
attribution: 'Map tiles by <a href="http://stamen.com">Stamen Design</a>, under <a href="http://creativecommons.org/licenses/by/3.0">CC BY 3.0</a>. Data by <a href="http://openstreetmap.org">OpenStreetMap</a>.'
});
// 3. 高德地图中文注记底图(需遵守其使用条款)
const gaodeNormal = L.tileLayer('https://webrd0{s}.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x={x}&y={y}&z={z}', {
subdomains: '1234',
attribution: '© 高德地图'
});
底图切换控制: 使用 L.control.layers 可以让用户自行切换底图。
const baseMaps = {
"OpenStreetMap": osmLayer,
"浅色地图": cartoLight,
"水彩地图": stamenWatercolor
};
L.control.layers(baseMaps).addTo(map);
绘制标记与信息窗
标记(Marker)是地图上最常用的元素,可附加弹出窗口(Popup)或固定信息窗(Tooltip)。
// 自定义图标的标记
const customIcon = L.icon({
iconUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-icon.png',
shadowUrl: 'https://unpkg.com/leaflet@1.9.4/dist/images/marker-shadow.png',
iconSize: [25, 41], // 图标尺寸
iconAnchor: [12, 41], // 图标锚点(图标尖端对应坐标的位置)
popupAnchor: [1, -34], // 弹出窗口相对锚点的偏移
});
const marker = L.marker([39.915, 116.404], { icon: customIcon }).addTo(map);
// 绑定弹出窗口
marker.bindPopup("<b>故宫博物院</b><br>开放时间:8:30-17:00").openPopup();
// 绑定工具提示(鼠标悬停显示)
marker.bindTooltip("北京市东城区景山前街4号", { direction: 'top' });
动态添加多个标记: 使用循环处理坐标数组。
const locations = [
{ lat: 39.9042, lng: 116.4074, name: "天安门" },
{ lat: 39.915, lng: 116.404, name: "故宫" },
{ lat: 39.92, lng: 116.39, name: "北海公园" }
];
locations.forEach(loc => {
L.marker([loc.lat, loc.lng])
.addTo(map)
.bindPopup(`<b>${loc.name}</b>`);
});
矢量图形覆盖物
除了点标记,Leaflet 还支持折线、多边形、圆形等矢量图形,可用于路径规划、区域高亮等场景。
// 折线
const polyline = L.polyline([
[39.90, 116.39],
[39.91, 116.40],
[39.92, 116.42]
], { color: 'red', weight: 5 }).addTo(map);
// 多边形
const polygon = L.polygon([
[39.90, 116.39],
[39.92, 116.38],
[39.93, 116.41],
[39.91, 116.42]
], { color: 'blue', fillColor: '#30a0ff', fillOpacity: 0.3 }).addTo(map);
// 圆形
const circle = L.circle([39.92, 116.40], {
radius: 800, // 单位:米
color: 'green',
fillColor: '#a0f0a0',
fillOpacity: 0.4
}).addTo(map);
为图形绑定事件:
polygon.on('click', function(e) {
alert('你点击了多边形区域');
});
交互与插件集成
用户位置定位
map.locate({ setView: true, maxZoom: 16 });
map.on('locationfound', (e) => {
const radius = e.accuracy / 2;
L.marker(e.latlng).addTo(map)
.bindPopup(`您当前位置,精度约 ${Math.round(radius)} 米`).openPopup();
L.circle(e.latlng, radius).addTo(map);
});
map.on('locationerror', () => {
alert("无法获取位置");
});
绘图工具插件
Leaflet.draw 插件允许用户在地图上手动画点、线、面。
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/leaflet.draw/1.0.4/leaflet.draw.js"></script>
// 添加绘图控件
const drawnItems = new L.FeatureGroup();
map.addLayer(drawnItems);
const drawControl = new L.Control.Draw({
edit: { featureGroup: drawnItems },
draw: {
polygon: true,
polyline: true,
rectangle: true,
circle: false,
marker: true
}
});
map.addControl(drawControl);
// 监听创建完成的图形
map.on(L.Draw.Event.CREATED, (e) => {
const layer = e.layer;
drawnItems.addLayer(layer);
console.log('新图形已添加:', layer.toGeoJSON());
});
热力图可视化
使用 Leaflet.heat 插件展示密度热力。
<script src="https://unpkg.com/leaflet.heat/dist/leaflet-heat.js"></script>
const heatPoints = [
[39.92, 116.40, 0.8], // [lat, lng, intensity]
[39.91, 116.41, 0.5],
[39.93, 116.39, 0.9]
];
const heat = L.heatLayer(heatPoints, {
radius: 30,
blur: 15,
maxZoom: 17
}).addTo(map);
数据加载与 GeoJSON
GeoJSON 是地理信息数据交换的标准格式,Leaflet 原生支持加载和渲染 GeoJSON 数据。
const geojsonFeature = {
type: "Feature",
properties: { name: "示例区域", popupContent: "这是一个多边形" },
geometry: {
type: "Polygon",
coordinates: [[
[116.38, 39.90], [116.42, 39.91], [116.41, 39.93], [116.38, 39.90]
]]
}
};
L.geoJSON(geojsonFeature, {
style: { color: "#ff7800", weight: 3, fillOpacity: 0.2 },
onEachFeature: (feature, layer) => {
if (feature.properties && feature.properties.popupContent) {
layer.bindPopup(feature.properties.popupContent);
}
}
}).addTo(map);
从外部文件加载:
fetch('data/china.geojson')
.then(response => response.json())
.then(data => {
L.geoJSON(data, {
style: () => ({ color: '#3388ff', weight: 1, fillOpacity: 0.1 })
}).addTo(map);
});
地图控件优化
自定义控件位置
// 将缩放控件移到右下角
map.zoomControl.setPosition('bottomright');
// 或完全移除并重建
map.removeControl(map.zoomControl);
L.control.zoom({ position: 'bottomleft' }).addTo(map);
比例尺与全屏控件
L.control.scale({ metric: true, imperial: false }).addTo(map);
// 全屏控件需要插件:leaflet-fullscreen
// <script src="https://unpkg.com/leaflet-fullscreen/dist/Leaflet.fullscreen.min.js"></script>
// <link rel="stylesheet" href="leaflet-fullscreen.css" />
// L.control.fullscreen().addTo(map);
属性控件样式调整
覆盖默认样式让信息栏更美观:
.leaflet-control-attribution {
background: rgba(255, 255, 255, 0.8);
padding: 2px 8px;
border-radius: 4px;
font-size: 11px;
}
响应式与移动端适配
Leaflet 本身对移动端友好,但需要做好视口和触摸事件的适配。
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
- 地图容器使用百分比或视口单位高度,避免固定像素。
- 使用
L.Browser.mobile判断移动端,提供特定交互逻辑。 - 建议将图标适当增大,触摸目标至少 44x44px。
常见问题与调试
| 现象 | 可能原因 | 解决方法 |
|---|---|---|
| 地图不显示 | 容器高度为0 | 给 #map 设置 height: 400px; |
| 瓦片加载失败 | 跨域或 URL 错误 | 检查网络,使用 HTTPS 瓦片源 |
| 标记图标丢失 | 图标路径错误或未配置图标 | 确保图标文件路径正确,或使用 CDN 绝对路径 |
| 缩放时性能卡顿 | 同时渲染大量复杂矢量图形 | 使用 preferCanvas: true 选项,或使用聚类插件 Leaflet.markercluster |
进阶学习路径
掌握以上基础后,可以深入探索这些方向:
- 性能优化:使用 Canvas 渲染器、标记聚类、矢量瓦片。
- 框架集成:结合 React(react-leaflet)、Vue(vue2-leaflet)进行组件化开发。
- 离线地图:将瓦片打包为 MBTiles 或使用本地 TileServer。
- 空间分析:结合 Turf.js 进行缓冲区分析、距离计算等空间操作。
Leaflet 的简洁与强大让地图集成不再困难,只需少量代码即可构建出专业级的地理信息可视化应用。动手实践时,善用官方文档和丰富的插件库,你的地图应用将拥有无限可能。