Leaflet教程,英文/国际版地图,vue项目集成Leaflet

        vue开发的项目要做海外版本,但是国内百度、高德等地图咔咔收费,只能找国外版本地图,对比参考了Mapbox、Leaflet、echarts等多种实现,最终选择了Leaflet开发,简单记录一下。效果如下图:

划重点:Leaflet必须翻墙(代理)使用外网才能正常渲染使用

一、Leaflet简介

        Leaflet是一个轻量级的Web地图库,它支持多种平台和浏览器,并具有广泛的功能、良好的性能以及易用性。Leaflet的主要特点包括:

  1. 轻量级:Leaflet的文件大小较小,加载速度快,特别适合在移动设备和低带宽环境下使用。
  2. 易用性:Leaflet提供了简单直观的API,使开发者能够快速上手并创建交互式地图。
  3. 可定制性:Leaflet支持自定义图层、标记样式和交互行为,开发者可以根据自己的需求进行定制。
  4. 跨平台兼容性: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,123321);
                            // 输出完整的地址信息
                            // 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>

  • 11
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值