Article directory
foreword
As an important tool for acquiring, storing, analyzing and managing geospatial data, GIS is more flexible to draw maps with GIS technology than traditional manual or automatic mapping tools. Today I would like to share with you a JavaScript class library package for GIS client development — leafletJs .
1. What is leafletJs?
To quote the official words, Leaflet is an open source and mobile-friendly interactive map JavaScript library. And it has all the map features that most developers need. Moreover, Leaflet can efficiently run on desktop and mobile platforms, and has a large number of extensions, excellent documentation, easy-to-use API , complete cases, and better readable source code. Chinese document address: https://leafletjs.cn/reference.html
2. Quick start
1. Installation
leafletJs provides two ways to introduce, you can choose one according to your personal needs.
Method 1: Direct import and use
<link rel = "stylesheet" href = "http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.css" />
<script src = "http://cdn.leafletjs.com/leaflet-0.7.3/leaflet.js"></script>
Method 2: npm installation
npm install leaflet --save
cnpm install leaflet --save
main.js import and register
import Leaflet from 'leaflet'
import "leaflet/dist/leaflet.css"
Vue.use(Leaflet);
2. Quick start
2.1. Prepare a div container, which must have width and height.
<div class="oilMap" id="oilMap"></div>
2.2. Define a map instance in data
data() {
return {
map: null,
};
},
2.3. Initialize map instance
mounted() {
this.initMap();
},
// 地图初始化
initMap() {
// 使用 id 为 map 的 div 容器初始化地图,同时指定地图的中心点和缩放级别
this.map = L.map("oilMap", {
center: [39.91, 116.91], //中心经纬度
zoom: 8, //初始缩放大小
});
// 引入图层
L.tileLayer("http://192.168.0.138/OSM_BLUE/{z}/{x}/{y}.png").addTo(
this.map
);
},
2.4. Don't forget to set the width and height for the map
<style lang="scss" scoped>
.oilMap {
width: 100%;
height: 100vh;
z-index: 9;
background: #00192e;
}
</style>
From this, you can get an initialized map as follows
3. Advanced Learning
1. Map control
Map
Controls are mainly tools used to control the map display.
1.1 Scale bar control
L.control.scale({
metric: true, // 显示米
imperial: false, // 不显示英尺
}).addTo(this.map);
1.2 Disable double-click to zoom in
this.map = L.map("oilMap", {
center: [39.91, 116.91], //中心经纬度
zoom: 8, //初始缩放大小
doubleClickZoom: false, //禁用双击放大
});
1.3 Whether the zoom control is displayed
this.map = L.map("oilMap", {
center: [39.91, 116.91], //中心经纬度
zoom: 8, //初始缩放大小
zoomControl: true, //缩放控件是否显示
});
1.4 Whether the copyright control is displayed
this.map = L.map("oilMap", {
center: [39.91, 116.91], //中心经纬度
zoom: 8, //初始缩放大小
attributionControl: false, //版权控件是否显示
});
1.5 Map maximum/minimum zoom level
this.map = L.map("oilMap", {
center: [39.91, 116.91], //中心经纬度
zoom: 8, //初始缩放大小
minZoom: 6,//地图最小缩放层级
maxZoom: 10,//地图最大缩放层级
});
2. Marker mark
L.Marker
Used to display clickable/dragable icons on the map.
2.1 Add icon mark points
// 定义图标属性
let markerIcon = L.icon({
iconUrl:"https://unpkg.com/[email protected]/dist/images/marker-icon-2x.png",
iconSize: [20, 34],//图标图像的尺寸,单位是像素。
iconAnchor: [7, 9],//图标 "tip" 的坐标(相对于其左上角)。图标将被对齐,使该点位于标记的地理位置。如果指定了尺寸,默认为居中,也可以在CSS中设置负的边距。
popupAnchor: [-3, -3],//弹出窗口(popup)的坐标,相对于图标锚点而言,将从该点打开。
});
// 生成标记点
L.marker([39.91, 116.91], {
icon: markerIcon,}).addTo(this.map);
Icon mark points to achieve the effect
2.2 Add a circle marker
L.circle([40.91, 116.91], {
color: "red", //描边颜色
fillColor: "yellow", //填充颜色
fillOpacity: 0.5, //填充的不透明度
radius: 5000, //圆的半径,以米为单位
}).addTo(this.map);
Round markers to achieve the effect
2.3 Adding polygon markers
let latlngs = [
[39.91, 116.91],
[39.87, 116.11],
[39.51, 116.51],
[39.51, 116.81],
[39.91, 116.91],
];
L.polygon(latlngs,{
color:'rgb(51,136,255)',fillColor:'red', fillOpacity:'0.4'}).addTo(this.map);
Polygon markers to achieve the effect
3. Popup pop-up window
bindPopup("弹出内容").openPopup()
Used to open popups at certain locations on the map.
chestnut
// 定义图标属性
let markerIcon = L.icon({
iconUrl:"https://unpkg.com/[email protected]/dist/images/marker-icon-2x.png",
iconSize: [20, 34],//图标图像的尺寸,单位是像素。
iconAnchor: [7, 9],//图标 "tip" 的坐标(相对于其左上角)。图标将被对齐,使该点位于标记的地理位置。如果指定了尺寸,默认为居中,也可以在CSS中设置负的边距。
popupAnchor: [-3, -3],//弹出窗口(popup)的坐标,相对于图标锚点而言,将从该点打开。
});
// 生成标记点
L.marker([39.91, 116.91], {
icon: markerIcon,}).addTo(this.map).bindPopup("弹出内容").openPopup();
achieve effect
4. Layers
Leaflet
In , anything that can be added to a map is a layer. Therefore, the markers, circles, and polygons above all belong to the layer, and can also be understood as the elements of the map.
4.1 Clear layers
//示例图层
let lineSet = L.polygon(latlngs,{
color:'rgb(51,136,255)',fillColor:'red', fillOpacity:'0.4'}).addTo(this.map);
//清除指定layer
this.map.removeLayer(lineSet)
//清除地图上所有layer
this.map.clearLayers()
4.2 Map level judgment
this.map.on("zoom", function (e) {
if (e.target._zoom > 8) {
// 执行操作
}
});
4.3 Monitor map zoom
this.map.on("zoom", function () {
// 执行操作
});
4.4 Binding click event
let marker = L.marker([39.91, 116.91]).addTo(this.map);
marker.on("click", function (e) {
// 执行操作
});
4.5 Add GeoJson layer
//jsondata必须为指定格式为GeoJson格式
let GeoJsonlayer = L.geoJSON(jsondata, {
style: {
color: "#4e98f444",
fillOpacity: 1,
weight: 1,
},
});
GeoJsonlayer.setZIndex(1);// 改变网格图层的 zIndex
this.map.addLayer(GeoJsonlayer);// 将给定的图层添加到组中
4. Project actual combat
The above are the main points of using Leaflet
the map . Next, we will combine the above mentioned points with the needs of the project to realize various interesting operations on the map.
package file
// 地图公共方法封装 /utils/index
import * as L from 'leaflet'
import {
Message, Notification } from 'element-ui'
export default class LMap {
// 初始化地图
/***
* 【options】参数合集
* [tileUrl]瓦片地图地址
* [minZoom]最小级别
* [maxZoom]最大级别
* [zoom]初始化级别
* [crs]坐标系
* [center]初始化视图中心点
* [fullscreenControl]是否显示全屏控件
* [zoomControl]是否显示缩放控件
* [inertia]是否开启惯性,在拖动和在某一时间段内持续朝同一方向移动建有动力的地图时,会有惯性的效果
* [attributionControl]属性控制是否默认加载在地图上
* [renderer]// 渲染方式 (默认使用canvas提高性能)
* [bounds]// 地图限制拖动区域 [下,上,右,左]
*
*
* ***/
async initMap(divId, options) {
// let url = process.env.VUE_APP_MAP_URL + '/OSM_BLUE/{z}/{x}/{y}.png'
const {
tileUrl,
minZoom,
maxZoom,
zoom,
crs,
center,
fullscreenControl,
zoomControl,
inertia,
attributionControl,
renderer,
bounds
} = options
this.map = L.map(divId, {
minZoom: minZoom ? minZoom : 8, // 地图最小缩放层级
maxZoom: maxZoom ? maxZoom : 15, // 地图最大缩放层级,
// crs: crs ? crs : L.CRS.EPSG3857, // 坐标系
center: center ? center : [39.7506, 118.3423], // 地图中心
zoom: zoom ? zoom : 8, // 地图默认缩放层级
fullscreenControl: fullscreenControl ? fullscreenControl : false, // 是否显示全屏控件
zoomControl: zoomControl ? zoomControl : false, // 是否显示缩放控件
inertia: inertia ? inertia : true, // 是否开启惯性,在拖动和在某一时间段内持续朝同一方向移动建有动力的地图时,会有惯性的效果
attributionControl: attributionControl ? attributionControl : false, // 属性控制是否默认加载在地图上
renderer: renderer ? renderer : L.canvas() //使用canvas 渲染 提高性能
})
if (tileUrl) {
// 将mapbox自定义样式添加到地图中
// this.map.addLayer(L.tileLayer(tileUrl, {
// tileSize: 256,
// zoomOffset: 1,
// attribution: 'stamen',
// }))
this.map.addLayer(L.tileLayer(tileUrl))
} else {
console.error('---请配置瓦片图层地址---')
}
// 给地图区域做限制的上下左右四部分
if (bounds) {
const mapBounds = bounds
// L.latLngBounds([
// [40.7844, 118.2935], // 下
// [38.4575, 118.4979], // 上
// [39.782, 115.778], // 左
// [39.8041, 121.583] // 右
// ])
this.map.setMaxBounds(mapBounds)
}
// 渲染完成返回true
return this.map
}
getMap() {
return this.map || {
}
}
mapArea() {
}
mapDistance() {
}
}
4.1 Basic bitmap
<template>
<div>
<div class="zMap" id="map"></div>
<!-- 点位详情 -->
<div ref="detailsObj" v-if="showPup">
<div class="dataBox">
<div class="item">
<div>点位名称:</div>
<div>{
{
markerData.name ? markerData.name : "--" }}</div>
</div>
<div class="item">
<div>车牌号:</div>
<div> {
{
markerData.cph ? markerData.cph : "--" }}</div>
</div>
<div class="item">
<div>排放阶段:</div>
<div>{
{
markerData.pfjd ? markerData.pfjd : "--" }}</div>
</div>
</div>
</div>
</div>
</template>
<script>
import areaGeo from "@/utils/area.json"; //引入离线本地json
import LMap from "@/utils/index";//地图公共方法封装
import * as L from "leaflet";
import onlineMarker from "../assets/zaix.png";//不同状态的图片
import offlineMarker from "../assets/lix.png";//不同状态的图片
export default {
name: "lMap",
data() {
return {
map: null, //地图对象
lmap: new LMap(), //地图公共方法封装
labelGroup: null, //文本图层
markerLayer: null, //marker 图层
bondarylayer: null, //geojson 图层
layerPup: null, //弹窗
markerData: {
}, //详情数据
showPup: false, //详情弹框是否弹出
};
},
mounted() {
this.initMap(); //初始化地图方法
},
methods: {
//初始化地图
async initMap() {
let _this = this;
let {
lmap } = this;
//配置地图项
const mapConfig = {
tileUrl: "http://192.168.0.138/OSM_BLUE/{z}/{x}/{y}.png", //瓦片图层地址
minZoom: 8, //允许缩放最小级别
maxZoom: 10, //允许缩放最大级别
zoom: 8, //初始化级别
center: [30.536095454317287, 104.15024279778913], //中心经纬度
bounds: L.latLngBounds([
[30.536095454317287, 104.15024279778913], // 下
[30.536095454317287, 104.15024279778913], // 上
[30.536095454317287, 104.15024279778913], // 左
[30.536095454317287, 104.15024279778913], // 右
]), //限制拖动的区域
};
this.map = await lmap.initMap("map", mapConfig);
if (this.map) {
this.labelGroup = new L.layerGroup(); //文本图层
this.labelGroup.addTo(this.map);
this.markerLayer = new L.layerGroup(); //标记
this.markerLayer.addTo(this.map);
this.getGeojsonByName(areaGeo, true);
this.maptopPoint();
} else {
console.error("--地图加载失败!!!--");
}
},
//加载geojson数据
getGeojsonByName(data, flag) {
let _this = this;
this.bondarylayer = L.geoJSON(data, {
style: {
color: "#4e98f444",
fillOpacity: 1,
weight: 2,
},
pane: "overlayPane",
onEachFeature: (feature, layer) => {
if (!flag) {
_this.addText(feature, layer);
}
},
});
this.bondarylayer.setZIndex(1);
this.map.addLayer(this.bondarylayer);
},
addText(feature, layer) {
let name = feature.properties.NAME;
let location = layer._bounds.getCenter();
//显示文字
var content = `${
name}`;
// 区县名称文字
var text = L.divIcon({
html: "<div>" + content + "</div>",
iconSize: [60, 20],
iconAnchor: [0, 10],
className: "labelStyle",
});
let marker = new L.marker(location, {
icon: text });
//中心点位
this.labelGroup.addLayer(marker);
},
//模拟地图点位接口
maptopPoint() {
let dataList = [
{
lng: "104.882",
lat: "30.436",
name: "点位1",
cph: "川A31255",
pfjd: "国五",
status: "0",
},
{
lng: "103.652",
lat: "30.565",
name: "点位2",
cph: "川C32585",
pfjd: "国四",
status: "1",
},
{
lng: "104.352",
lat: "31.136",
name: "点位3",
cph: "川X36985",
pfjd: "国三",
status: "0",
},
{
lng: "104.452",
lat: "30.536",
name: "点位4",
cph: "川D31255",
pfjd: "国二",
status: "1",
},
{
lng: "104.202",
lat: "30.136",
name: "点位5",
cph: "川E31125",
pfjd: "国一",
status: "1",
},
];
this.checkMarkerList(dataList);
},
//监测数据对marker进行渲染
checkMarkerList(list) {
let _this = this;
if (!list.length) {
return;
}
list.map((item) => {
_this.addMarker(item);
});
},
addMarker(item) {
let _this = this;
let Icon = _this.getMarkerIcon(item);
if (Icon) {
var marker = new L.marker(L.latLng(item.lat, item.lng), {
icon: Icon,
});
//区县编码
marker["markerInfo"] = item;
marker.bindTooltip(item.name); //鼠标触摸显示提示信息
marker.on("click", function (e) {
let MarkerTarget = e.target; //获取 点击marker
_this.showMarkerPup(MarkerTarget);
});
this.markerLayer.addLayer(marker);
}
},
//显示marker 弹窗信息
showMarkerPup(marker) {
let _this = this;
_this.markerData = marker["markerInfo"];
let location = marker._latlng;
_this.showPup = true;
_this.$nextTick(() => {
_this.layerPup = L.popup({
className: "district-pup",
closeButton: false,
})
.setLatLng(location)
.setContent(_this.$refs.detailsObj)
.openOn(_this.map);
});
},
//获取marker图标
getMarkerIcon(data) {
let markerIcon, imgUrl;
let {
status } = data; //1在线 2离线
if (status) {
status = Number(status);
}
if (status == 1) {
imgUrl = onlineMarker;
} else {
imgUrl = offlineMarker;
}
markerIcon = L.icon({
iconUrl: imgUrl,
iconSize: [20, 20],
iconAnchor: [12, 12],
popupAnchor: [-3, -3],
});
return markerIcon;
},
},
};
</script>
<style lang="scss" scoped>
.zMap {
width: 100%;
height: 100vh;
z-index: 9;
}
.dataBox {
width: 100%;
.item {
display: flex;
padding: 4px 0px;
}
}
::v-deep {
.leaflet-popup-content-wrapper,
.leaflet-popup-tip {
border: none;
background: rgba($color: #020a31, $alpha: 0.7) !important;
border-radius: 4px;
box-shadow: 0px 0 6px 0px #33aeff inset;
color: rgba($color: #fff, $alpha: 0.8);
}
.leaflet-container a.leaflet-popup-close-button {
color: #fff;
}
}
</style>
achieve effect
4.2 Driving trajectory map
<template>
<div>
<div class="leftRightBox">
<!-- 左 -->
<div class="oilMap" id="map"></div>
<!-- 右 -->
<div class="rightTxtBox">
<el-card class="box-card">
<!-- 播放轨迹 -->
<div class="trackBox">
<div>
<i title="播放" v-if="playIsShow" @click="playPauseOn" class="el-icon-video-play"></i>
<i title="暂停" v-else @click="playPauseOn" class="el-icon-video-pause"></i>
</div>
<div>
<el-slider :min="0" :max="points.length - 1" @change="sliderOn" v-model="progress"></el-slider>
</div>
</div>
<div class="multipleBox">
<span>倍速:</span>
<el-select @change="multipleOn" style="width:70px" size="mini" v-model="multipleNumber" placeholder="请选择倍速">
<el-option v-for="item in multipleOptions" :key="item.multipleNumber" :label="item.label" :value="item.value">
</el-option>
</el-select>
</div>
</el-card>
</div>
</div>
</div>
</template>
<script>
import LMap from "@/mapUtils";
import areaGeo from "@/mapUtils/area.json";
import abnormal from "../../../../assets/overview/normal.png"; //地图图标
export default {
data() {
return {
multipleOptions: [
{
value: 800,
label: "1",
},
{
value: 550,
label: "1.5",
},
{
value: 400,
label: "2",
},
],
map: null,
lmap: new LMap(), //地图公共方法封装
labelGroup: null, //文本图层
tipsLayerGroup: null, //tips
bondarylayer: null, //geojson 图层
points: [
//模拟点位数据
[39.898457, 116.391844],
[39.898595, 116.377947],
[39.898341, 116.368001],
[39.898063, 116.357144],
[39.899095, 116.351934],
[39.905871, 116.35067],
[39.922329, 116.3498],
[39.931017, 116.349671],
[39.939104, 116.349225],
[39.942233, 116.34991],
[39.947263, 116.366892],
[39.947568, 116.387537],
[39.947764, 116.401988],
[39.947929, 116.410824],
[39.947558, 116.42674],
[39.9397, 116.427338],
[39.932404, 116.427919],
[39.923109, 116.428377],
[39.907094, 116.429583],
[39.906858, 116.41404],
[39.906622, 116.405321],
[39.906324, 116.394954],
[39.906308, 116.391264],
],
playIsShow: true, //播放/暂停
multipleNumber: 1, //倍速
progress: 0, //进度
marker: null,
polyline: null,
duration: 0,
timer: null, //定时器
time: 800, //定时器速度
};
},
mounted() {
this.duration = this.points.length - 1;
this.initMap();
},
watch: {
progress() {
this.updatePosition();
},
},
destroyed() {
clearTimeout(this.timer);
},
methods: {
updatePosition() {
const position = this.points[this.progress];
this.marker.setLatLng(position);
this.map.setView(position, 12); //放大比例
},
// 滑动进度
sliderOn(e) {
console.log(e, "滑动进度");
},
// 选择倍速
multipleOn(e) {
clearTimeout(this.timer);
this.time = e;
this.recursion();
},
// 点击播放暂停按钮
playPauseOn() {
if (this.progress == this.points.length - 1) {
this.progress = 0;
}
this.playIsShow = !this.playIsShow;
this.recursion();
},
// 递归
recursion() {
if (!this.playIsShow) {
// 模拟数据变化
this.timer = setTimeout(() => {
this.progress++;
if (this.progress >= this.points.length - 1) {
this.playIsShow = true;
this.polyline = L.polyline(this.points, {
color: "red" }).addTo(
this.map
);
clearTimeout(this.timer);
}
this.recursion();
}, this.time);
}
},
//初始化地图
async initMap() {
let {
lmap } = this;
//配置地图项
const mapConfig = {
tileUrl: window.api.teilUrl + "/OSM_BLUE/{z}/{x}/{y}.png", //瓦片图层地址
crs: L.CRS.EPSG4326,
minZoom: 8, //允许缩放最小级别
maxZoom: 15, //允许缩放最大级别
zoom: 10, //初始化级别
center: [39.91, 116.91], //初始化中心点
};
this.map = await lmap.initMap("map", mapConfig);
if (this.map) {
this.labelGroup = new L.layerGroup(); //文本图层
this.labelGroup.addTo(this.map);
this.tipsLayerGroup = new L.layerGroup();
this.tipsLayerGroup.addTo(this.map);
this.getGeojsonByName(areaGeo, true);
} else {
console.error("--地图加载失败!!!--");
}
var icon = L.icon({
iconUrl: abnormal,
iconSize: [16, 13],
iconAnchor: [8, 7],
});
this.marker = L.marker(this.points[0], {
icon: icon }).addTo(this.map);
this.polyline = L.polyline(this.points, {
color: "red" }).addTo(this.map);
setInterval(() => {
this.marker.setLatLng(this.points[this.progress]);
this.polyline.setLatLngs(this.points.slice(0, this.progress + 1));
}, 100);
},
//加载geojson数据 falg 是否显示名称
getGeojsonByName(data, flag) {
let _this = this;
this.bondarylayer = L.geoJSON(data, {
style: {
color: "#4e98f444",
fillOpacity: 1,
weight: 1,
},
pane: "overlayPane",
onEachFeature: (feature, layer) => {
// console.log(layer)
if (flag) {
_this.addText(feature, layer);
}
},
});
this.bondarylayer.setZIndex(1);
this.map.addLayer(this.bondarylayer);
},
//添加市所有区县
addText(feature, layer) {
let name = feature.properties.name;
// let center = feature.properties.center;
let location = layer._bounds.getCenter(); //[center[1], center[0]]; //
//显示文字
var content = `${
name}`;
// 区县名称文字
var text = L.divIcon({
html: "<div class='labelStyle'>" + content + "</div>",
iconSize: [60, 20],
iconAnchor: [0, 10],
className: "labelStyle",
});
let marker = new L.marker(location, {
icon: text });
//中心点位
this.labelGroup.addLayer(marker);
},
},
};
</script>
<style scoped>
.oilMap {
height: 500px;
}
</style>
achieve effect
持续更新...