Leaflet地图开发入门难?GIS研习社推荐这份源码级教程(附:API速查表)

编程与开发
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

Leaflet地图开发入门难?GIS研习社推荐这份源码级教程(附:API速查表)这篇文章面向刚开始做 WebGIS 的同学和 GIS 工程师,目标不是泛泛介绍 Leaflet,而是把一个最小可运行地图、常用 API、图层加载、事件交互和调试思路串起来,让你能快速判断:Leaflet 到底怎么用、代码应该从哪里写、遇到地图不显示该怎么查。

引言:为什么很多人觉得 Leaflet 地图开发入门难

Leaflet 是一个轻量级 JavaScript 地图库,常用于二维 WebGIS 项目,例如底图展示、点线面叠加、GeoJSON 数据浏览、移动端地图交互等。它的 API 很简洁,但对 GIS 初学者来说,真正的难点往往不在“调用一个函数”,而在下面几个问题:

  • 不知道地图容器、地图对象、底图图层之间是什么关系。
  • 经纬度顺序、坐标系和切片地址容易混淆。
  • 能显示底图,但 GeoJSON、Marker、Popup、事件交互不知道如何组织。
  • 复制示例代码可以运行,一旦换成自己的数据就报错或空白。
  • 不知道如何查 Leaflet API 文档,也不知道哪些方法最常用。

所以,学习 Leaflet地图开发入门,建议不要一开始就堆复杂插件,而是从源码结构理解它的核心对象:Map、Layer、TileLayer、Marker、GeoJSON、Control、Event。只要这几个概念清楚,后面做 WebGIS 页面会顺很多。

Leaflet地图开发入门与Leaflet API速查表核心结构图
Leaflet 地图开发的核心关系:容器负责显示,Map 负责管理,Layer 负责承载底图和业务数据,Event 负责交互。

背景:Leaflet 适合解决什么 GIS 开发问题

在 WebGIS 技术选型中,Leaflet 常被用于“轻量、快速、二维地图为主”的场景。它不追求三维地球和复杂渲染,而是专注于浏览器中的交互式二维地图。

适合使用 Leaflet 的场景

  • 展示在线瓦片底图,例如 OpenStreetMap、天地图、XYZ 切片服务。
  • 叠加点、线、面、GeoJSON、WMS 等常见 GIS 数据。
  • 制作业务地图,例如门店分布、巡检点位、项目范围、管线简图。
  • 移动端 H5 地图页面,需要较轻的库体积和良好的触摸交互。
  • 教学、原型验证、小型 WebGIS 项目。

不太适合只用 Leaflet 的场景

  • 大规模矢量瓦片渲染,需要高性能 WebGL 表达。
  • 三维地球、倾斜摄影、BIM 或海量三维模型。
  • 复杂桌面级 GIS 编辑和拓扑分析。
  • 需要完整空间数据库管理能力的系统。

如果你的任务是“把 GIS 数据快速发布到网页上,并提供基本浏览、点击、查询、弹窗、图层控制”,Leaflet地图开发入门非常适合作为第一站。

原理:从源码级理解 Leaflet 的几个核心对象

学习 Leaflet 不建议只背代码。更好的方式是理解每一行代码背后的对象关系。一个 Leaflet 页面通常由 HTML 容器、地图对象、图层对象和交互事件组成。

1. 地图容器:地图显示在哪里

Leaflet 地图必须挂载到一个 HTML 元素上,常见写法是给页面准备一个带有固定高度的 div。很多“地图不显示”的问题,本质上就是容器没有高度。

<div id="map" style="height: 500px;"></div>

这里的 map 只是一个容器 id,并不是 Leaflet 地图对象本身。

2. Map 对象:地图的总控制器

L.map() 会创建地图对象,它负责管理中心点、缩放级别、图层、控件和事件。

const map = L.map('map').setView([31.2304, 121.4737], 11);

注意 Leaflet 的坐标数组顺序通常是 [纬度, 经度],也就是 [lat, lng]。这和很多 GIS 数据中常见的 [x, y][经度, 纬度] 表达不同,是 Leaflet 入门阶段最常见的坑之一。

3. TileLayer:底图从哪里来

底图一般通过 L.tileLayer() 加载 XYZ 瓦片服务。瓦片地址中的 {z}{x}{y} 分别代表缩放级别、列号和行号。

L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
  maxZoom: 19,
  attribution: '© OpenStreetMap contributors'
}).addTo(map);

如果底图不显示,优先检查瓦片地址是否可访问、网络是否被限制、浏览器控制台是否有跨域或 404 报错。

4. Layer:所有能放到地图上的内容

在 Leaflet 中,Marker、Polyline、Polygon、GeoJSON、TileLayer 都可以看作图层。它们通过 addTo(map) 加到地图上,也可以通过 map.removeLayer(layer) 移除。

5. Event:地图交互的入口

Leaflet 的交互非常依赖事件。例如点击地图获取坐标、点击点位弹出属性、拖动结束后重新请求数据,都可以通过事件实现。

map.on('click', function (e) {
  console.log('点击坐标:', e.latlng.lat, e.latlng.lng);
});

步骤:从零写一个 Leaflet 地图开发入门示例

下面给出一个可直接运行的最小示例。你可以保存为 index.html,用浏览器打开测试。正式项目中建议使用本地构建工具或模块化方式管理代码,但入门阶段先用 CDN 更直观。

步骤 1:准备 HTML 页面和 Leaflet 资源

<!DOCTYPE html>
<html lang="zh-CN">
<head>
  <meta charset="UTF-8">
  <title>Leaflet 地图开发入门示例</title>
  <link
    rel="stylesheet"
    href="https://unpkg.com/leaflet/dist/leaflet.css"
  />
  <style>
    html, body {
      margin: 0;
      padding: 0;
      height: 100%;
    }
    #map {
      width: 100%;
      height: 100vh;
    }
  </style>
</head>
<body>
  <div id="map"></div>

  <script src="https://unpkg.com/leaflet/dist/leaflet.js"></script>
  <script>
    // 后续 Leaflet 代码写在这里
  </script>
</body>
</html>

这里最重要的是 #map 必须有宽高。只引入 JS 不引入 CSS,或者容器高度为 0,都会导致地图区域显示异常。

步骤 2:初始化地图对象

const map = L.map('map', {
  center: [31.2304, 121.4737],
  zoom: 11
});

这段代码创建一个地图对象,并把初始视图定位到上海附近。你也可以使用 setView() 写法:

const map = L.map('map').setView([31.2304, 121.4737], 11);

步骤 3:添加在线瓦片底图

const osmLayer = L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
  maxZoom: 19,
  attribution: '© OpenStreetMap contributors'
});

osmLayer.addTo(map);

这一步完成后,页面应该能看到底图。如果看不到,先不要继续写业务代码,先打开浏览器开发者工具检查网络请求。

步骤 4:添加一个 Marker 点位和 Popup

const marker = L.marker([31.2304, 121.4737]).addTo(map);

marker.bindPopup('<strong>上海市中心附近</strong><br>这是一个 Leaflet Marker 示例。');

bindPopup() 会给图层绑定弹窗。默认情况下,点击 Marker 会打开 Popup。业务系统中通常会把名称、类型、状态、更新时间等属性放到 Popup 中。

步骤 5:添加线和面

const route = L.polyline([
  [31.20, 121.40],
  [31.23, 121.47],
  [31.26, 121.52]
], {
  color: '#0078ff',
  weight: 4
}).addTo(map);

const area = L.polygon([
  [31.18, 121.42],
  [31.18, 121.55],
  [31.28, 121.55],
  [31.28, 121.42]
], {
  color: '#00a86b',
  fillColor: '#00a86b',
  fillOpacity: 0.2
}).addTo(map);

线和面的坐标同样使用 [lat, lng]。如果你的原始数据来自 GeoJSON,要注意 GeoJSON 坐标是 [经度, 纬度],Leaflet 在加载 GeoJSON 时会自动处理,但你手写数组时需要自己确认顺序。

步骤 6:加载 GeoJSON 数据

Leaflet 对 GeoJSON 支持很好,适合加载小到中等规模的业务矢量数据。下面是一个内联 GeoJSON 示例:

const geojsonData = {
  "type": "FeatureCollection",
  "features": [
    {
      "type": "Feature",
      "properties": {
        "name": "示例范围",
        "type": "项目区"
      },
      "geometry": {
        "type": "Polygon",
        "coordinates": [[
          [121.40, 31.18],
          [121.55, 31.18],
          [121.55, 31.28],
          [121.40, 31.28],
          [121.40, 31.18]
        ]]
      }
    }
  ]
};

const geojsonLayer = L.geoJSON(geojsonData, {
  style: function (feature) {
    return {
      color: '#ff6600',
      weight: 2,
      fillOpacity: 0.15
    };
  },
  onEachFeature: function (feature, layer) {
    layer.bindPopup(
      '名称:' + feature.properties.name + '<br>' +
      '类型:' + feature.properties.type
    );
  }
}).addTo(map);

这里要特别注意:GeoJSON 标准坐标顺序是 [经度, 纬度],也就是 [lng, lat]。不要因为 Leaflet Marker 使用 [lat, lng],就把 GeoJSON 坐标也反过来。

步骤 7:添加图层控制器

图层控制器适合给用户切换底图和业务图层。

const baseMaps = {
  "OpenStreetMap": osmLayer
};

const overlayMaps = {
  "点位": marker,
  "路线": route,
  "范围": area,
  "GeoJSON图层": geojsonLayer
};

L.control.layers(baseMaps, overlayMaps).addTo(map);

到这里,一个 Leaflet地图开发入门示例已经具备底图、点、线、面、GeoJSON、弹窗和图层控制功能。

步骤:Leaflet API 速查表

下面这份 Leaflet API速查表适合入门开发时放在手边。它不是完整文档,而是日常项目中最常用的对象和方法。

类别 常用 API 用途 示例
地图初始化 L.map() 创建地图对象 L.map('map')
设置视图 setView() 设置中心点和缩放级别 map.setView([31.23,121.47],11)
瓦片底图 L.tileLayer() 加载 XYZ 瓦片 L.tileLayer(url, options)
点标记 L.marker() 添加点位 L.marker([lat,lng])
线 L.polyline() 绘制线要素 L.polyline(coords)
L.polygon() 绘制面要素 L.polygon(coords)
L.circle() 绘制半径范围 L.circle([lat,lng], {radius: 500})
GeoJSON L.geoJSON() 加载 GeoJSON 数据 L.geoJSON(data, options)
弹窗 bindPopup() 给图层绑定弹窗 layer.bindPopup('内容')
提示 bindTooltip() 给图层绑定悬浮提示 layer.bindTooltip('名称')
事件 on() 监听点击、移动等事件 map.on('click', fn)
添加图层 addTo() 把图层加入地图 layer.addTo(map)
移除图层 removeLayer() 从地图移除图层 map.removeLayer(layer)
缩放至范围 fitBounds() 根据范围自动缩放 map.fitBounds(layer.getBounds())
图层控制 L.control.layers() 添加图层切换控件 L.control.layers(base, overlay)

如果只记一条学习路径,建议按这个顺序掌握:L.map → L.tileLayer → L.marker → L.geoJSON → bindPopup → on → L.control.layers

常见坑:Leaflet 地图不显示、坐标错位、图层加载失败怎么查

坑 1:地图容器没有高度

症状是页面空白,但控制台没有明显报错。原因通常是 #map 的高度为 0。

#map {
  height: 500px;
}

如果使用 height: 100%,要确保 htmlbody 和父容器都有高度。

坑 2:Leaflet CSS 没有引入

只引入 leaflet.js 不引入 leaflet.css,地图控件、Marker 图标和部分布局会异常。入门示例中 CSS 和 JS 都必须引入。

坑 3:经纬度顺序写反

Leaflet 的 Marker、Polyline、Polygon 手写坐标一般是 [lat, lng],GeoJSON 标准坐标是 [lng, lat]。如果点跑到海里、范围跑到国外,优先检查坐标顺序。

坑 4:坐标系不是 WGS84 经纬度

Leaflet 默认适合显示 Web 墨卡托瓦片底图上的经纬度数据。很多国内 GIS 数据可能是 CGCS2000 投影坐标、高斯投影坐标或地方坐标。如果直接当经纬度加载,肯定会错位。

处理建议:

  • 先在 QGIS 或 ArcGIS Pro 中确认数据坐标系。
  • 如果是投影坐标,先转换为 WGS84 经纬度或与底图匹配的坐标体系。
  • 不要只修改坐标系名称,必须真正重投影坐标值。

坑 5:瓦片地址不可访问或跨域受限

如果底图不显示,打开浏览器开发者工具的 Network 面板,查看瓦片请求是否为 200。如果出现 404、403、跨域错误或证书错误,需要更换服务地址或配置服务器。

坑 6:GeoJSON 文件太大导致页面卡顿

Leaflet 可以加载 GeoJSON,但不适合一次性渲染特别大的原始矢量文件。如果数据量较大,建议:

  • 在 QGIS 中先简化几何。
  • 按行政区、网格或视图范围分片加载。
  • 使用矢量瓦片方案。
  • 考虑 OpenLayers、MapLibre GL JS 或后端切片服务。

方法比较:Leaflet、OpenLayers、MapLibre GL JS 怎么选

WebGIS 入门时,经常会在 Leaflet、OpenLayers 和 MapLibre GL JS 之间纠结。三者都能做地图,但适用重点不同。

工具 优势 局限 适合场景
Leaflet 轻量、API 简洁、学习成本低、插件丰富 复杂投影和大规模矢量渲染能力有限 入门教学、小型业务地图、移动端 H5 地图
OpenLayers GIS 能力更完整,支持更多数据源和投影场景 API 相对复杂,入门成本更高 专业 WebGIS、复杂图层管理、投影要求较高的项目
MapLibre GL JS WebGL 渲染强,适合矢量瓦片和样式化地图 对数据切片和样式规范要求更高 高性能矢量瓦片、互联网地图、动态样式渲染

如果你的目标是完成第一个 WebGIS 页面,Leaflet地图开发入门更友好;如果项目后续涉及大量投影、复杂 OGC 服务和专业 GIS 交互,可以进一步学习 OpenLayers;如果重点是矢量瓦片和高性能前端渲染,再考虑 MapLibre GL JS。

检查清单:写 Leaflet 项目前先确认这些事

  • 页面是否正确引入 leaflet.cssleaflet.js
  • 地图容器是否有明确宽度和高度。
  • 底图瓦片地址是否能在浏览器中正常访问。
  • 手写坐标是否使用 [lat, lng]
  • GeoJSON 坐标是否保持标准 [lng, lat]
  • 业务数据坐标系是否已经转换到适合网页显示的经纬度或匹配坐标系。
  • 是否用开发者工具检查 Network 请求和 Console 报错。
  • 大 GeoJSON 是否做过简化、切片或按范围加载。
  • 是否把图层对象保存为变量,方便后续移除、更新和控制显示。
  • 是否把初始化地图、加载底图、加载业务图层、绑定事件拆成清晰函数。

FAQ:Leaflet 地图开发入门常见问题

1. Leaflet 需要会 GIS 才能学吗?

不一定需要很深的 GIS 理论,但至少要理解经纬度、坐标系、图层、点线面、GeoJSON 这些基础概念。否则地图能显示,但数据为什么错位、为什么变形、为什么加载慢会很难排查。

2. Leaflet 和 ArcGIS API for JavaScript 有什么区别?

Leaflet 更轻量,适合通用二维地图和开源 WebGIS 项目。ArcGIS API for JavaScript 与 ArcGIS 平台结合更紧密,适合使用 ArcGIS Server、Portal、SceneView、FeatureLayer 等 Esri 生态能力的项目。

3. Leaflet 可以加载 Shapefile 吗?

Leaflet 原生不直接加载 Shapefile。通常做法是先用 QGIS、ArcGIS Pro、ogr2ogr 或后端服务把 Shapefile 转成 GeoJSON、矢量瓦片、WMS、WFS 等 Web 友好的格式,再由 Leaflet 加载。

4. Leaflet 加载 GeoJSON 很慢怎么办?

先检查 GeoJSON 文件大小、要素数量和几何复杂度。可以在 QGIS 中做几何简化、字段精简和范围裁剪。如果数据量仍然很大,建议改用矢量瓦片或后端按范围分页返回。

5. Leaflet 的坐标为什么经常写反?

因为 Leaflet 的很多手写坐标 API 使用 [lat, lng],而 GeoJSON、数据库和多数 GIS 坐标表达常用 [x, y],也就是 [lng, lat]。入门阶段建议在变量名中明确写 latlng,不要只写 xy

6. Leaflet 能做离线地图吗?

可以,但需要提前准备离线瓦片或本地地图服务。常见方案包括本地 XYZ 瓦片目录、MBTiles 服务、局域网瓦片服务器等。需要注意瓦片版权、存储体积和访问路径配置。

7. Leaflet 适合做生产项目吗?

适合。很多生产级 WebGIS 项目都使用 Leaflet。但是否合适取决于需求:如果只是二维地图浏览、点线面叠加、弹窗查询、图层控制,Leaflet 很合适;如果需要复杂投影、高性能海量矢量和三维表达,就要谨慎评估。

结论:用源码级思路学习 Leaflet,入门会更快

Leaflet地图开发入门并不难,难的是一开始没有把对象关系理清。你只要记住:HTML 容器负责承载地图,L.map 创建地图对象,L.tileLayer 加载底图,Marker、Polyline、Polygon、GeoJSON 都是图层,事件和 Popup 负责交互,就能读懂大多数基础示例。

建议学习时按照“先跑通最小示例,再加点线面,再加 GeoJSON,再加事件和图层控制”的顺序推进。遇到问题先查容器高度、资源引入、瓦片请求、坐标顺序和坐标系。把这套排查流程掌握后,再去看插件和复杂框架,WebGIS 开发效率会明显提高。