大家好,欢迎来到IT知识分享网。
vue开发的项目要做海外版本,但是国内百度、高德等地图咔咔收费,只能找国外版本地图,对比参考了Mapbox、Leaflet、echarts等多种实现,最终选择了Leaflet开发,简单记录一下。效果如下图:
划重点:Leaflet必须翻墙(代理)使用外网才能正常渲染使用
一、Leaflet简介
Leaflet是一个轻量级的Web地图库,它支持多种平台和浏览器,并具有广泛的功能、良好的性能以及易用性。Leaflet的主要特点包括:
- 轻量级:Leaflet的文件大小较小,加载速度快,特别适合在移动设备和低带宽环境下使用。
- 易用性:Leaflet提供了简单直观的API,使开发者能够快速上手并创建交互式地图。
- 可定制性:Leaflet支持自定义图层、标记样式和交互行为,开发者可以根据自己的需求进行定制。
- 跨平台兼容性:Leaflet可以在各种现代浏览器和移动设备上运行,并且与多种前端框架(如React、Vue等)兼容。
二、Leaflet简单教程
Leaflet 是一个开源的 JavaScript 库,用于在网页上创建交互式地图。以下是一个基本的 Leaflet 教程,帮助你开始使用 Leaflet 创建一个简单的地图:
1. 引入 Leaflet 库
首先,在 HTML 文件中引入 Leaflet 库的 CSS 和 JavaScript 文件。从 Leaflet 的官方网站下载最新的库文件,或者使用 CDN 链接。Leaflet官网地址
<!DOCTYPE html>
<html>
<head>
<title>Leaflet 入门教程</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.9.3/dist/leaflet.css" />
<script src="https://unpkg.com/leaflet@1.9.3/dist/leaflet.js"></script>
</head>
<body>
<!-- 地图容器 -->
<div id="map" style="width: 100%; height: 400px;"></div>
<script>
// 在这里编写 JavaScript 代码来初始化地图
</script>
</body>
</html>
2. 初始化地图
在 JavaScript 代码中,使用 Leaflet 的 API 来初始化地图,并设置地图的中心位置、缩放级别和其他选项。
<script> // 初始化地图 var map = L.map('map').setView([51.505, -0.09], 13); // 设置地图的中心位置和缩放级别 // 添加瓦片图层 L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', { maxZoom: 19, }).addTo(map); </script>
L.map('map')
创建一个新的地图实例,并将其绑定到 ID 为 “map” 的 HTML 元素上。setView
方法设置地图的中心位置和缩放级别。使用 L.tileLayer
创建一个瓦片图层,并将其添加到地图上。此处使用了 OpenStreetMap 的瓦片图层作为示例。
3. 添加标记和弹窗
使用 Leaflet 添加标记(Marker)和弹窗(Popup)来显示地图上的特定位置的信息。
<script> // ...之前的代码... // 创建一个标记并添加到地图上 var marker = L.marker([51.5, -0.09]).addTo(map) .bindPopup("<b>Hello world!</b><br />I am a popup.").openPopup(); // 绑定一个弹窗并打开它 </script>
三、实际使用(以单页面使用为例)
准备工作(安装依赖):
npm install leaflet //地图主服务 npm install leaflet-geosearch //地图搜索服务 npm install leaflet.markercluster//点位聚合
1、页面引入:
import L from "leaflet"; import "leaflet/dist/leaflet.css"; import "leaflet.markercluster"; import "leaflet.markercluster/dist/MarkerCluster.css"; import "leaflet.markercluster/dist/MarkerCluster.Default.css";
2、页面定义元素(本身为空的块状元素,如果高度为0则无法查看)
<div id="map" style="height:500px"></div>
3、实例,直接上代码:
initMap() { // 地图的实例必须在页面加载完成后 在进行否则会报错 this.$nextTick(() => { this.map=null if(this.data){ // 判断是否有数据传入用于默认选择, setView视图设置 map实例化 if(this.data.GisLat&&this.data.GisLat!=''&&this.data.GisLon&&this.data.GisLon!=''){ this.map = L.map("map").setView([this.data.GisLat , this.data.GisLon], 13); }else{ this.map = L.map("map").setView([36.18, 120.41], 13); } }else{ this.map = L.map("map").setView([36.18, 120.41], 13); } //获取地图碎片,并添加到map实例上 L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { // 最大缩放 maxZoom: 19, }).addTo(this.map); //自定义标识图标 var myIcon = L.divIcon({ className: "my-div-icon" }); //聚合使用因为暂时没用所以只是 var markers = L.markerClusterGroup(); let num = 120.413; for (var i = 0; i < 10000; i++) { let a = [36.18, num]; var marker = L.marker(new L.LatLng(a[0], a[1]), { icon: myIcon, }); marker.bindPopup("标记的位置是: 提示内容"); markers.addLayer(marker); num += 0.001; } this.map.addLayer(markers); //地图标点 var currentMarker; if(this.data&&this.map){ if(this.data.GisLat&&this.data.GisLat!=''&&this.data.GisLon&&this.data.GisLon!=''){ Marker 标识 bindPopup 弹层 openPopup 打开弹层 currentMarker= L.marker([this.data.GisLat, this.data.GisLon],{ icon:myIcon }) .addTo(this.map).bindPopup(this.data.Address).openPopup(); this.result.lng = this.data.GisLon this.result.lat = this.data.GisLat this.result.address = this.data.Address } } var that = this; // 地图点击事件,时间内部this的指向会发生变化 所以不能用this this.map.on("click", function (e) { if (currentMarker) { //删除标识 that.map.removeLayer(currentMarker); } currentMarker=L.marker([e.latlng.lat, e.latlng.lng], { icon: myIcon }).addTo(that.map); // 获取点击的经纬度 var latlng = e.latlng; // 显示经纬度 // console.log("经度: " + latlng.lng + ", 纬度: " + latlng.lat); that.result.lng = e.latlng.lng that.result.lat = e.latlng.lat // 使用Nominatim逆地理编码服务获取地址名称 var url = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${latlng.lat}&lon=${latlng.lng}`; // 发送请求 fetch(url) .then((response) => response.json()) .then((data) => { // 输出完整的地址信息 that.result.address =data.display_name if (currentMarker) { · //删除标识 that.map.removeLayer(currentMarker); } currentMarker=L.marker([e.latlng.lat, e.latlng.lng], { icon: myIcon }) .addTo(that.map).bindPopup(data.display_name).openPopup(); }) .catch((err) => { console.error(err); // alert("无法获取地址信息"); }); }); }); },
4、搜索服务引入:
import { OpenStreetMapProvider } from "leaflet-geosearch";
5、页面定义搜索元素:
<div class="search-container"> <input type="text" id="search-input" placeholder="Search for a location" v-model="searchTerm" @input="search" /> <ul v-if="searchResults.length" class="search-results"> <li v-for="(result,index) in searchResults" :key="index" @click="flyTo(result)">{
{ result.label }}</li> </ul> </div>
6、将搜索内容返回英文
// 切换为英文显示搜索内容 provider: new OpenStreetMapProvider({ params: { "accept-language": "en", // 设置返回结果的语言为英文 }, }),
7、事件
// 页面搜索事件 async search() { if (this.searchTerm.length > 2) { const results = await this.provider.search({ query: this.searchTerm, }); this.searchResults = results; } else { this.searchResults = []; } }, // 点击赋值 flyTo(result) { this.map.flyTo([result.y, result.x], 8); this.searchResults = []; this.searchTerm = ""; },
8、封装成组件完整代码:
<template> <el-dialog :title="title"class="dialogYK" :close-on-click-modal="false" :visible.sync="visible" :before-close="cancelDialog" :append-to-body="addToBody"> <div class="map-container"> <div id="map"></div> <div class="search-container"> <input type="text" id="search-input" placeholder="Search for a location" v-model="searchTerm" @input="search" /> <ul v-if="searchResults.length" class="search-results"> <li v-for="(result,index) in searchResults" :key="index" @click="flyTo(result)">{
{ result.label }}</li> </ul> </div> </div> <div slot="footer" class="dialog-footer"> <el-button @click="cancelDialog">Cancel</el-button> <el-button type="primary" @click="confirmDialog">Confirm</el-button> </div> </el-dialog> </template> <script> import L from "leaflet"; import "leaflet/dist/leaflet.css"; import { OpenStreetMapProvider } from "leaflet-geosearch"; import "leaflet.markercluster"; import "leaflet.markercluster/dist/MarkerCluster.css"; import "leaflet.markercluster/dist/MarkerCluster.Default.css"; export default { props: { // 对话框标题 title: { type: String, default: "Dialog Box", }, // 初始化位置信息 data: { type: Object, default: () => { return {}; }, }, addToBody: { type: Boolean, default: true, }, visible: { type: Boolean, default: false, }, }, data() { return { result:{ lng:"", lat:"", address:"" }, map: null, searchTerm: "", searchResults: [], // 切换为英文显示搜索内容 provider: new OpenStreetMapProvider({ params: { "accept-language": "en", // 设置返回结果的语言为英文 }, }), }; }, mounted() { this.initMap(); }, methods: { initMap() { this.$nextTick(() => { this.map=null if(this.data){ // 判断是否有数据传入用于默认选择 if(this.data.GisLat&&this.data.GisLat!=''&&this.data.GisLon&&this.data.GisLon!=''){ this.map = L.map("map").setView([this.data.GisLat , this.data.GisLon], 13); }else{ this.map = L.map("map").setView([36.18, 120.41], 13); } }else{ this.map = L.map("map").setView([36.18, 120.41], 13); } //获取地图碎片 L.tileLayer("https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png", { maxZoom: 19, }).addTo(this.map); var myIcon = L.divIcon({ className: "my-div-icon" }); //聚合使用 var markers = L.markerClusterGroup(); let num = 120.413; for (var i = 0; i < 10000; i++) { let a = [36.18, num]; var marker = L.marker(new L.LatLng(a[0], a[1]), { icon: myIcon, }); marker.bindPopup("标记的位置是: 提示内容"); markers.addLayer(marker); num += 0.001; } this.map.addLayer(markers); //地图标点 var currentMarker; if(this.data&&this.map){ if(this.data.GisLat&&this.data.GisLat!=''&&this.data.GisLon&&this.data.GisLon!=''){ currentMarker= L.marker([this.data.GisLat, this.data.GisLon], { icon:myIcon }) .addTo(this.map).bindPopup(this.data.Address).openPopup(); this.result.lng = this.data.GisLon this.result.lat = this.data.GisLat this.result.address = this.data.Address } } var that = this; // 地图点击事件 this.map.on("click", function (e) { if (currentMarker) { that.map.removeLayer(currentMarker); } currentMarker=L.marker([e.latlng.lat, e.latlng.lng], { icon: myIcon }).addTo(that.map); // 获取点击的经纬度 var latlng = e.latlng; // 显示经纬度 // console.log("经度: " + latlng.lng + ", 纬度: " + latlng.lat); that.result.lng = e.latlng.lng that.result.lat = e.latlng.lat // 使用Nominatim逆地理编码服务获取地址名称 var url = `https://nominatim.openstreetmap.org/reverse?format=json&lat=${latlng.lat}&lon=${latlng.lng}`; // 发送请求 fetch(url) .then((response) => response.json()) .then((data) => { console.log(data,); // 输出完整的地址信息 // console.log("地址: " + data.display_name); that.result.address =data.display_name if (currentMarker) { that.map.removeLayer(currentMarker); } currentMarker=L.marker([e.latlng.lat, e.latlng.lng], { icon: myIcon }) .addTo(that.map).bindPopup(data.display_name).openPopup(); }) .catch((err) => { console.error(err); // alert("无法获取地址信息"); }); }); }); }, confirmDialog() { if (this.result.address === '') { this.$message.warning('Please Click the Map to Select Address') return } this.$emit('success', this.result) this.cancelDialog() }, // 关闭 cancelDialog() { this.$emit("close"); }, // 页面搜索事件 async search() { if (this.searchTerm.length > 2) { const results = await this.provider.search({ query: this.searchTerm, }); this.searchResults = results; } else { this.searchResults = []; } }, // 点击赋值 flyTo(result) { this.map.flyTo([result.y, result.x], 8); this.searchResults = []; this.searchTerm = ""; }, }, }; </script> <style lang="scss" scoped> .map-container { position: relative; } #map { height: 500px; } .search-container { position: absolute; top: 10px; left: 60px; right: 10px; z-index: 1000; } #search-input { width: 100%; padding: 10px; box-sizing: border-box; font-size: 16px; border: 2px solid #ddd; border-radius: 5px; } .search-results { list-style: none; margin: 0; padding: 0; background: white; border: 1px solid #ddd; border-top: none; border-radius: 0 0 5px 5px; box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2); max-height: 300px; overflow-y: auto; } .search-results li { padding: 10px; border-top: 1px solid #ddd; cursor: pointer; } .search-results li:hover { background-color: #f0f0f0; } ::v-deep .my-div-icon { background-image: url("./marker-icon.png"); width: 20px !important; height: 20px !important; } </style>
免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/156606.html