相关文章:
从 0 到 1 快速实现海外地图接入(Leaflet + OpenStreetMap)
10000+ 点位轻松展示,使用 Leaflet 实现地图海量标记点聚类
解决 Leaflet 高频交互下报错问题,社区、AI、源码三重排查
随着全球化业务发展,我们需要为海外用户提供地图服务。经过技术调研发现:
基于以上考量,我们选择leaflet + OpenStreetMap技术方案:
leaflet 是一个开源的 JavaScript 库,用于创建交互式、移动友好的网络地图。它轻量级(约 39KB 的 JS 代码),但功能强大,支持大多数桌面和移动平台。
OpenStreetMap(OSM) 是一个由用户社区创建和维护的免费可编辑的世界地图,常被称为"地理维基百科"。
说到 OpenStreetMap,就不得不说一下地图瓦片。
地图瓦片是将地图分割成小的正方形图片(通常是 256×256 或 512×512 像素),按不同缩放级别组织起来的系统。这种技术允许:
瓦片通常使用(x,y,z)坐标标识:
leaflet 是一个地图显示库,而 OpenStreetMap 是地图数据源之一,也就是瓦片源。Leaflet 可以显示来自 OpenStreetMap 的地图瓦片,也可以显示其他来源的地图数据。OpenStreetMap 数据可以被多种客户端(包括 Leaflet)使用来渲染地图。
除了 OpenStreetMap,Leaflet 的灵活性使其能够兼容多种瓦片源,包括:
具体接入方式需参考各服务的 API 文档或对应 leaflet 插件说明,本篇文章不做过多赘述!
接下来,我们将介绍如何借助 Tare 来实现 leaflet + OpenStreetMap 地图,来从 0 到 1 构建一个简单的地图应用程序。
接下来,我借助 Trae IDE 提供的内置智能体 Builder,来帮助我从 0 到 1 开发一个完整的项目。根据我发出的需求, Builder 会调用不同的工具,包括运行命令的工具,来有效地处理我的需求。
在这里,我取消了 Builder 智能体的执行命令,我手动安装必要库 leaflet,最新版本为 1.9.4
|
1 |
pnpm install leaflet |
还需要添加 types 支持,不然 ts 项目会报类型文件错误
|
1 |
pnpm install @types/leaflet |
安装完 leaflet 之后,我们就可以,创建一个地图组件。
|
1 2 |
import L from 'leaflet' import 'leaflet/dist/leaflet.css' |
|
1 |
<div id="map" style="width: 100%; height: 100%"></div> |
以 London, UK 坐标为中心,设置当前缩放级别为 5
|
1 |
const map = L.map('map').setView([51.505, -0.09], 5) |
设置瓦片数据源,并将地图最大支持的放大级别设置为 19
|
1 2 3 4 5 |
L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', maxZoom: 19 }).addTo(map) |
|
1 |
L.marker([51.5, -0.09]).addTo(map) |
|
1 |
marker.bindPopup('London, UK').openPopup() |
完整的代码如下所示:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
<script lang="ts" setup> import { onMounted } from 'vue'; import L from 'leaflet'; import 'leaflet/dist/leaflet.css'; onMounted(() => { const map = L.map('map').setView([51.505, -0.09], 5); L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', maxZoom: 19, }).addTo(map); // 添加标记点位 L.marker([51.5, -0.09]).addTo(map).bindPopup('London, UK').openPopup(); }); </script> <template> <div id="map" style="width: 100%; height: 500px"></div> </template> |
这样,通过以上的代码,一个以London, UK 坐标为中心地图雏形就出来了。
将地图放大级别调整为 13,如下图所示,放大可至街道级别:
|
1 |
map.setView([51.505, -0.09], 13) |
已有项目中支持暗黑模式,所以地图也要支持暗黑模式,问一下 Trae,如何实现暗黑模式?
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 |
<script lang="ts" setup> import { onMounted, ref } from 'vue' import L from 'leaflet' import 'leaflet/dist/leaflet.css' const darkMode = ref(true) onMounted(() => { const map = L.map('map').setView([51.505, -0.09], 13) // 浅色图层 const lightLayer = L.tileLayer( 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', maxZoom: 19 } ) // 深色图层 const darkLayer = L.tileLayer( 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', maxZoom: 19 } ) // 根据darkMode适配主题 darkMode.value ? darkLayer.addTo(map) : lightLayer.addTo(map) // 添加标记 L.marker([51.5, -0.09]).addTo(map).bindPopup('London, UK').openPopup() }) </script> <template> <div id="map" style="width: 100%; height: 100%"></div> </template> |
偶发性报错,因为 leaflet 地图容器被重复初始化导致的。我们需要优化一下流程,确保地图只在组件挂载时初始化一次,并在组件卸载时正确销毁地图实例。
通过添加销毁逻辑和使用 ref 来跟踪地图实例状态来解决这个问题。
最终的代码如下所示:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 |
<script lang="ts" setup> import { onMounted, onUnmounted, ref } from 'vue' import { usePreferences } from '@vben/preferences' import L from 'leaflet' import 'leaflet/dist/leaflet.css' const { isDark } = usePreferences() const darkMode = ref(isDark.value) const mapInstance = ref<L.Map | null>(null) onMounted(() => { if (mapInstance.value) { mapInstance.value.remove() } mapInstance.value = L.map('map').setView([51.505, -0.09], 5) // 浅色图层 const lightLayer = L.tileLayer( 'https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', maxZoom: 19 } ) // 深色图层 const darkLayer = L.tileLayer( 'https://{s}.basemaps.cartocdn.com/dark_all/{z}/{x}/{y}{r}.png', { attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors', maxZoom: 19 } ) darkMode.value ? darkLayer.addTo(mapInstance.value as L.Map) : lightLayer.addTo(mapInstance.value as L.Map) // 添加标记 L.marker([51.5, -0.09]) .addTo(mapInstance.value as L.Map) .bindPopup('London, UK') .openPopup() }) onUnmounted(() => { if (mapInstance.value) { mapInstance.value.remove() mapInstance.value = null } }) </script> <template> <div id="map" style="width: 100%; height: 100%"></div> </template> |
非常简单,以上代码,简洁版的地图组件就完成了。接下来要实现更多的功能完善,比如:添加标记组、连线、轨迹等功能。
注意:要使用 🪜 才能正确渲染地图哦!
leaflet + OpenStreetMap 是一个强大的组合,它提供了一个简单、灵活且高效的方式来展示全球地图。通过使用 leaflet 提供的地图显示功能,我们可以轻松地将 OpenStreetMap 的地图数据集成到我们的应用程序中。
而 OpenStreetMap 是免费的,同时覆盖了全球的大部分地区,包括大陆、海洋、南极洲等,还支持多种语言、暗黑模式等。
总之,瓦片技术是现代网络地图高效运行的基础,Leaflet 和 OpenStreetMap 的结合提供了一个强大而免费的地图解决方案。