01-用 Leafletjs 10 分钟搭一张水利一张图(Vue3 + Vite 实战)

TL;DR:智慧水利平台第一页那张地图,本质是「底图 + 线状河流 + 点状工程 + 面状范围」四层叠加。本文基于 Leaflet 1.9.4 + Vue3 + Vite,用深圳 mock 数据跑通最小可运行版本。


水利一张图是什么

把物理世界的水利对象按空间位置画到电子地图上,三类要素:

flowchart TD A[水利一张图] --> B[线状要素<br/>河流/堤防/渠道] A --> C[点状要素<br/>水库/水闸/雨量站] A --> D[面状要素<br/>蓄滞洪区/管理范围] B --> B1[L.polyline] C --> C1[L.marker / L.circleMarker] D --> D1[L.polygon]
类型 业务对象 Leaflet API
线状 河流、堤防、渠道 L.polyline
点状 水库、水闸、雨量站、水位站 L.marker / L.circleMarker
面状 蓄滞洪区、河湖管理范围 L.polygon

💡 选型 :正式项目常用 SuperMap / MapGIS / OpenLayers,但展示型一张图、轻量大屏、H5 移动端,Leaflet 足够------gzip 后 ~40KB,开源免费,手势原生。


第一步:起一张底图

纯 HTML 版本(CDN)

map.html

html 复制代码
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.4/dist/leaflet.css" />
<div id="map" style="height: 600px"></div>
<script src="https://unpkg.com/leaflet@1.9.4/dist/leaflet.js"></script>

init.js

javascript 复制代码
// 中心 [纬度, 经度],缩放级别 11(深圳市民中心)
const map = L.map('map').setView([22.5431, 114.0579], 11)

// 深色底图:国内推荐 ArcGIS,OSM 加载慢
L.tileLayer(
  'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}',
  { attribution: '&copy; Esri' }
).addTo(map)

⚠️ 国内瓦片坑:不要用 OSM,加载慢且偶尔超时。ArcGIS Dark Gray 在国内可访问,配色也适合大屏。

Vue3 + Vite 版本

bash 复制代码
npm create vite@latest leafletjs-demo -- --template vue
cd leafletjs-demo
npm install leaflet

src/main.js

javascript 复制代码
import { createApp } from 'vue'
import App from './App.vue'
import 'leaflet/dist/leaflet.css'

createApp(App).mount('#app')

src/views/Demo01.vue

vue 复制代码
<template>
  <div ref="el" class="demo-map"></div>
</template>

<script setup>
import { ref, onMounted, onUnmounted } from 'vue'
import L from 'leaflet'

const el = ref(null)
let map = null

onMounted(() => {
  map = L.map(el.value).setView([22.5431, 114.0579], 11)
  L.tileLayer(
    'https://server.arcgisonline.com/ArcGIS/rest/services/Canvas/World_Dark_Gray_Base/MapServer/tile/{z}/{y}/{x}',
    { attribution: '&copy; Esri' }
  ).addTo(map)
})

onUnmounted(() => map?.remove())
</script>

<style scoped>
.demo-map { position: absolute; inset: 0; }
</style>

⚠️ flex 布局坑 :父容器是 flex 时,.demo-map { height: 100% } 会因为父链高度断裂而塌成 0,地图不显示。改成 position: absolute; inset: 0 最稳。


第二步:画水利要素

河流(Polyline)

javascript 复制代码
// 深圳河(示意坐标)
L.polyline([
  [22.528, 114.045], [22.522, 114.062], [22.515, 114.078],
  [22.508, 114.095], [22.500, 114.112]
], { color: '#1976d2', weight: 3 })
  .addTo(map)
  .bindTooltip('深圳河')

水库(Marker + 自定义图标)

用 divIcon 画水坝 SVG,比默认蓝点专业:

javascript 复制代码
const reservoirIcon = L.divIcon({
  html: `<svg width="22" height="22" viewBox="0 0 22 22">
    <rect x="3" y="7" width="16" height="10" fill="#01579b" stroke="#fff" stroke-width="1.5" rx="2"/>
    <path d="M6 7 L9 3 L13 3 L16 7" fill="none" stroke="#fff" stroke-width="1.5"/>
  </svg>`,
  iconSize: [22, 22],
  iconAnchor: [11, 11],
  className: ''
})

L.marker([22.584, 114.148], { icon: reservoirIcon })
  .addTo(map)
  .bindPopup('<b>深圳水库</b><br/>中型 · 罗湖区')

点击读经纬度

javascript 复制代码
map.on('click', (e) => {
  const { lat, lng } = e.latlng
  console.log(`纬度 ${lat.toFixed(6)},经度 ${lng.toFixed(6)}`)
})

效果预览


⚠️ 三个必踩的坑

1. 地图容器没高度。 Leaflet 容器不会自己撑高,父链任何一环没显式高度就是空白。flex 布局下尤其常见,用 position: absolute; inset: 0 解决。

2. 坐标顺序写反。 Leaflet 所有点坐标都是 [纬度, 经度],但 GeoJSON、CAD 成果给的是 [经度, 纬度]。写反的结果是点全跑非洲去了。

3. 坐标系偏移。 Leaflet 原生只认 Web 墨卡托(EPSG:3857),国内正式数据是 CGCS2000 / GCJ-02,直接叠点位会偏移几百米。下一篇专门讲。


小结

要素 API 一句话
底图 L.tileLayer 瓦片 URL 模板 {z}/{x}/{y}
线状 L.polyline 河流堤防
点状 L.marker 水库测站
交互 bindPopup / bindTooltip 挂业务信息

📦 本文是《智慧水利 GIS 实战》系列第 1 篇,完整 Vue3 demo(10 个场景)见仓库 leafletjs-demo/ 目录,npm run dev 即可跑。

相关推荐
小码哥_常5 小时前
一套自己部署的 IM,客户端是同一份 Flutter
前端·后端
蜗牛攻城狮7 小时前
微前端四大方案选型对比与最佳实践:qiankun、micro-app、wujie、Module Federation
前端·微前端·qiankun·前端架构·micro-app·wujie
她说可以呀8 小时前
Vue插槽
前端·javascript·vue.js
qq_448011169 小时前
Vue + Jinja2两套模版与法冲突造成undefinedError
前端·javascript·vue.js
aixingpan9 小时前
aixingpan.cn API开发文档:前端查询夏令时和时区接口指南
前端·php
2601_958352909 小时前
23.5×19mm 换掉一整条音频链?|A-68 集成了什么:ADC / DAC / 24.576MHz 时钟 / AEC / 波束降噪,主控仅需 6 根线
前端·音视频·ai降噪·回声消除·语音模块
志尊宝10 小时前
AI原生前端开发(039):AI辅助的Nuxt 4全栈开发
前端·vue.js·ai-native
FRing0311 小时前
JavaScript入门到精通三十二(深拷贝、浅拷贝、递归函数)
开发语言·前端·javascript
excel11 小时前
双 Token 与前端使用 Crypto 公私钥鉴权换 Token,哪种方案更好?
前端·后端
外收内放11 小时前
Python基础语法练习题(73)
java·前端·python