一、官网
https://leafletjs.com/reference.html
leaflet是一个轻量级的地图组件,但是由于地图使用的是瓦片地图图片,而leaflet使用的图片是 https://tile.openstreetmap.org/{z}/{x}/{y}.png,而这个地址国内是无法访问的,所以默认地图上并不能展示。
可以替换为国内可以访问的瓦片地图,或者自己下载地图图片搭建服务器。
打开官网,能看到界面已经分类好,到Download中下载leaflet的js和css即可。
下载完毕后,解压缩如下,plugins是我新建的目录,用来使用插件的,
二、Quick Start
官网有快速了解的demo,里面有一些基础内容,分别是maker,画圆,多边形绘制等。
新建一个html文件,引入leaflet.js和leaflet.css,
<!DOCTYPE html>
<html lang="en">
<head>
<base target="_top">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Quick Start - Leaflet</title>
<link rel="stylesheet" href="leaflet/leaflet.css"/>
<script src="leaflet/leaflet.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
}
</style>
</head>
<body>
</body>
</html>
按照官网上所说,定义一个div,感觉和使用echarts一样,
<div id="map"></div>
<script>
var map = L.map('map').setView([51.505, -0.09], 13);
L.tileLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', {
attribution: '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors'
}).addTo(map);
</script>
从官网上可以知道,第一个参数表示中心点位置,第二个是缩放等级。
此处本应该展示出地图,但是并没有地图出现,因为是对应的一个个地图图片,从下面请求地址可以看到,想获取一个PNG图片,但是访问不到这个地址。
当然,也可以搭建一个瓦片地图网站,将https请求换为自己的服务地址。
标记
var marker = L.marker([51.5, -0.09]).addTo(map);
var circle = L.circle([51.508, -0.11], {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5,
radius: 500
}).addTo(map);
var polygon = L.polygon([
[51.509, -0.08],
[51.503, -0.06],
[51.51, -0.047]
]).addTo(map);
在地图上标记一个点位,增加区域范围,增加三角,形成如下形状,
弹框
在上述上个图形中点击弹出一个框,
marker.bindPopup("<b>Hello world!</b><br>I am a popup.").openPopup();
circle.bindPopup("I am a circle.");
polygon.bindPopup("I am a polygon.");
初始时显示一个弹框
var popup = L.popup()
.setLatLng([51.513, -0.09])
.setContent("I am a standalone popup.")
.openOn(map);
点击事件
function onMapClick(e) {
alert("You clicked the map at " + e.latlng);
}
map.on('click', onMapClick);
点击地图,就会出现一个弹框,
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("You clicked the map at " + e.latlng.toString())
.openOn(map);
}
map.on('click', onMapClick);
更多的交互事件可以在api中找到,
quick start的完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<base target="_top">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Quick Start - Leaflet</title>
<link rel="stylesheet" href="leaflet/leaflet.css"/>
<script src="leaflet/leaflet.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
}
#map { height: 480px; width:680px }
</style>
</head>
<body>
<div id="map"></div>
</body>
<script>
var map = L.map('map').setView([51.505, -0.09], 13);
var marker = L.marker([51.5, -0.09]).addTo(map);
var circle = L.circle([51.508, -0.11], {
color: 'red',
fillColor: '#f03',
fillOpacity: 0.5,
radius: 500
}).addTo(map);
var polygon = L.polygon([
[51.509, -0.08],
[51.503, -0.06],
[51.51, -0.047]
]).addTo(map);
marker.bindPopup("<b>Hello world!</b><br>I am a popup.").openPopup();
circle.bindPopup("I am a circle.");
polygon.bindPopup("I am a polygon.");
var popup = L.popup()
.setLatLng([51.513, -0.09])
.setContent("I am a standalone popup.")
.openOn(map);
var popup = L.popup();
function onMapClick(e) {
popup
.setLatLng(e.latlng)
.setContent("You clicked the map at " + e.latlng.toString())
.openOn(map);
}
map.on('click', onMapClick);
</script>
</html>
自定义标记图片
这是官网给出的国内第二个可以使用的,自定义标记图片。
我去阿里素材网站上下了三个图片,用于自定义,放在leaflet的图片目录下。
方式一
var map = L.map('map').setView([51.505, -0.09], 13);
var greenIcon = L.icon({
iconUrl: 'leaflet/images/blue_marker.png',
//shadowUrl: 'leaflet/images/marker-shadow.png',
iconSize: [38, 65], // size of the icon
shadowSize: [50, 64], // size of the shadow
iconAnchor: [22, 94], // point of the icon which will correspond to marker's location
shadowAnchor: [4, 62], // the same for the shadow
popupAnchor: [-3, -76] // point from which the popup should open relative to the iconAnchor
});
L.marker([51.5, -0.09], {icon: greenIcon}).addTo(map);
方式二
var map = L.map('map').setView([51.505, -0.09], 13);
var LeafIcon = L.Icon.extend({
options: {
//shadowUrl: 'leaf-shadow.png',
iconSize: [38, 65],
shadowSize: [50, 64],
iconAnchor: [22, 94],
shadowAnchor: [4, 62],
popupAnchor: [-3, -76]
}
});
var greenIcon = new LeafIcon({iconUrl: 'leaflet/images/blue_marker.png'}),
redIcon = new LeafIcon({iconUrl: 'leaflet/images/red_marker.png'}),
orangeIcon = new LeafIcon({iconUrl: 'leaflet/images/grey_marker.png'});
L.icon = function (options) {
return new L.Icon(options);
};
L.marker([51.5, -0.09], {icon: greenIcon}).addTo(map).bindPopup("I am a green leaf.");
L.marker([51.495, -0.083], {icon: redIcon}).addTo(map).bindPopup("I am a red leaf.");
L.marker([51.49, -0.1], {icon: orangeIcon}).addTo(map).bindPopup("I am an orange leaf.");
从代码可以知道,自定义图标使用icon,作为marker的参数,
缩放等级
var map = L.map('map', {
minZoom: 0,
maxZoom: 1
});
var cartodbAttribution = '© <a href="https://www.openstreetmap.org/copyright">OpenStreetMap</a> contributors, © <a href="https://carto.com/attribution">CARTO</a>';
var positron = L.tileLayer('https://{s}.basemaps.cartocdn.com/light_all/{z}/{x}/{y}.png', {
attribution: cartodbAttribution
}).addTo(map);
map.setView([0, 0], 0);
//minZoom和maxZoom控制,也可以用下面setZoom方法控制。
setInterval(function(){
map.setZoom(0);
setTimeout(function(){
map.setZoom(1);
}, 2000);
}, 4000);
可以用来设置最小缩放,和最大缩放。
图片展示
var map = L.map('map', {
crs: L.CRS.Simple,
minZoom: -5 //最小缩放
});
var bounds = [[0,0], [1000,1000]];
var image = L.imageOverlay('uqm_map_full.png', bounds).addTo(map);
map.fitBounds(bounds);
图片和html同级,此处的uqm_map_full.png也可以换成一个http图片地址,也能展示。
从上述代码可知,展示图片使用ImageOverlay,第一个参数是图片路径,第二个是图片所表示的地理边界,
图片加标记
var map = L.map('map', {
crs: L.CRS.Simple,
});
var bounds = [[0,0], [1000,1000]];
var image = L.imageOverlay('uqm_map_full.png', bounds).addTo(map);
map.fitBounds(bounds);
var sol = L.latLng([ 145, 175.2 ]);
L.marker(sol).addTo(map);
map.setView( [70, 120], 1);
图片中标记点连线
var map = L.map('map', {
crs: L.CRS.Simple,
});
var bounds = [[0,0], [1000,1000]];
var image = L.imageOverlay('uqm_map_full.png', bounds).addTo(map);
map.fitBounds(bounds);
var sol = L.latLng([ 175.2, 145.0 ]);
var deneb = L.latLng([ 218.7, 8.3 ]);
L.marker( sol).addTo(map).bindPopup( 'Sol');
L.marker( deneb).addTo(map).bindPopup( 'Deneb');
var travel = L.polyline([sol, deneb]).addTo(map);
其他比如绘制矩形,多边形,圆形等都在API中的Vector Layer(矢量图层),如下图所示,
视频播放
在ImageOverlay下面是VideoOverlay,可以加载视频文件,
var map = L.map('map').setView([37.8, -96], 4);
var videoUrls = [
'23.mp4'
];
var errorOverlayUrl = 'uqm_map_full.png';
var latLngBounds = L.latLngBounds([[32, -130], [43, -60]]);
var videoOverlay = L.videoOverlay(videoUrls, latLngBounds, {
opacity: 0.8,
errorOverlayUrl: errorOverlayUrl,
interactive: true,
autoplay: true,
muted: true,
playsInline: true
}).addTo(map);
上述代码就可以在界面播放一个MP4文件。
加控制按钮
videoOverlay.on('load', function () {
var MyPauseControl = L.Control.extend({
onAdd: function() {
var button = L.DomUtil.create('button');
button.title = 'Pause';
button.innerHTML = '<span aria-hidden="true">⏸</span>';
L.DomEvent.on(button, 'click', function () {
videoOverlay.getElement().pause();
});
return button;
}
});
var MyPlayControl = L.Control.extend({
onAdd: function() {
var button = L.DomUtil.create('button');
button.title = 'Play';
button.innerHTML = '<span aria-hidden="true">▶️</span>';
L.DomEvent.on(button, 'click', function () {
videoOverlay.getElement().play();
});
return button;
}
});
var pauseControl = (new MyPauseControl()).addTo(map);
var playControl = (new MyPlayControl()).addTo(map);
这样在右上角就有播放和暂停两个控制按钮,看控制代码应该内部封装了video元素。
文字标注
为了在图片上展示自己的文字或者html效果,通过leaflet的api可以知道divIcon继承自Icon,同时可以自定义html代码。
var icon = L.divIcon({
html:"<div style='width:120px;'>茶水间</div>",
className:'my-divIcon',
iconSize:30
});
L.marker([388,582],{icon:icon}).addTo(map);
leaflet.pm插件
一个用于在图片上绘制和标记的插件,挺好用的。左边是一堆操作按钮,右边是绘制的图形和标记点。
加载一张图片
加载图片的方式在之前已有,此处只是换了一张图片而已。
添加插件
https://github.com/geoman-io/leaflet-geoman
我是直接引入的js和css,下载下面这个就行,
<link rel="stylesheet" href="leaflet/plugins/leaflet-geoman.css"/>
<script src="leaflet/plugins/leaflet-geoman.min.js"></script>
插件使用
// 添加左侧按钮
map.pm.addControls({
position: "topleft",
drawPolygon: true, // 添加绘制多边形
drawMarker: true, //添加按钮以绘制标记
drawRectangle: true, //添加按钮绘制矩形
drawCircleMarker: true, //添加按钮以绘制圆形标记
drawPolyline: true, //添加按钮绘制线条
drawCircle: true, // 添加按钮绘制圆圈
editMode: true, // 添加按钮编辑多边形
dragMode: true, // 添加按钮拖动多边形
cutPolygon: true, // 添加一个按钮以删除图层里面的部分内容
removalMode: true, // 清除图层
});
这些对应的是左侧的操作按钮,理论上到这里就可以在图片上绘制图形。但是绘制好的图形数据如何得到?以便下次进来时加载。下图代码打印的就是绘制的数据,
/**
获取绘制的数据
*/
function getlatLngs() {
var drawData = {};
map.on("pm:remove", e => {
let leaflet_id = e.layer._leaflet_id
});
// //pm:drawstart 开始第一个点的时候调用
// //pm:drawend 禁止绘制时调用
// //pm:create 创建完成时调用
map.on("pm:drawstart", e => {
});
map.on("pm:create", e => {
if (e.shape == 'Marker') {
let index = [e.layer._latlng.lat, e.layer._latlng.lng]
drawData = {areaIndex:index,leafletId: e.layer._leaflet_id,type:e.shape}
}else if(e.shape=='Line'){
let listIndex = [];
e.layer._latlngs.forEach(item=>{
let zindex = [item.lat, item.lng]
listIndex.push(zindex)
})
drawData = {areaIndex:listIndex,leafletId: e.layer._leaflet_id,type:e.shape}
}else if(e.shape=='Text'){
let index = [e.layer._latlng.lat, e.layer._latlng.lng];
drawData = {areaIndex:index,content:e.layer.options.text,leafletId: e.layer._leaflet_id,type:e.shape}
}else if(e.shape=='Circle'||e.shape=='CircleMarker'){
let index = [e.layer._latlng.lat, e.layer._latlng.lng];
drawData = {areaIndex:index,radius:e.layer._radius,leafletId: e.layer._leaflet_id,type:e.shape}
}
else if(e.shape=='Rectangle'||e.shape=='Polygon'){
let indexs = e.layer._latlngs;
let listIndex = []
indexs[0].forEach(item => {
let zindex = [item.lat, item.lng]
listIndex.push(zindex)
})
drawData = {areaIndex:listIndex,leafletId: e.layer._leaflet_id,type:e.shape}
}
//console.log(drawData)
});
}
此处的shape为Text时有问题,打印出文字,e.layer.options.text 应该能获取到文字,但是实际获取到为空。
绘制图形
使用如下代码,传递需要绘制的数据,一般是点位置,即getlatLngs中的areaIndex数据。
/**
绘制图形
*/
function drawImg(drawData) {
var item = drawData;
if(item.type == 'Marker'){
let drawRadius = L.marker( item.areaIndex, {color: "red"}).addTo(map);
let leaflet_id = drawRadius._leaflet_id;
item.leafletId = leaflet_id;
drawRadius.on({pm:'edit'}, obj => {
obj.target.setStyle({ color: "orange" });
});
}else if(item.type=='Line'){
let style1 ={
weight: 2,
color: "#3089FD",
fillOpacity: 0.2
}
let drawObj = L.polygon(item.areaIndex, style1);
let upFlag = true
drawObj.addTo(map);
let leaflet_id = drawObj._leaflet_id;
item.leafletId = leaflet_id;
drawObj.on({pm:'edit'
}, obj => {
obj.target.setStyle({color: "orange"});
});
}else if(item.type=='Circle'||item.type=='CircleMarker'){
var circle = L.circle(item.areaIndex, {
color: '3089FD',
fillColor: '#3089FD',
fillOpacity: 0.7,
radius: item.radius
}).addTo(map);
}else if(item.type=='Text'){
var icon = L.divIcon({
html:"<span>"+item.content+"</span>",
className:'my-divIcon',
iconSize:30
});
L.marker(item.areaIndex,{icon:icon}).addTo(map);
}
else{
//矩形
// color:线段颜色
// weight:线段宽度
// opacity:线段透明度
// dashArray:虚线间隔
// fill:是否填充内部(true/false)
// fillColor:内部填充颜色,如不设置,默认为color颜色
// fillOpacity:内部填充透明度
let style1 ={
weight: 2,
color: "#3089FD",
fillOpacity: 0.2
}
let drawObj = L.polygon(item.areaIndex, style1);
drawObj.addTo(map);
let leaflet_id = drawObj._leaflet_id;
item.leafletId = leaflet_id;
drawObj.on({pm:'edit'
}, obj => {
obj.target.setStyle({color: "orange"});
});
}
}
将getlatLngs中打印的数据在html中绘制后,能有如下图形,
var drawData = {
"areaIndex": [
566,
312
],
"leafletId": 150,
"type": "Marker"
}
drawImg(drawData);
drawData = {
"areaIndex": [
[
556,
426
],
[
570,
646
]
],
"leafletId": 170,
"type": "Line"
}
drawImg(drawData);
drawData = {
"areaIndex": [
[
768,
546
],
[
776,
848
],
[
724,
752
]
],
"leafletId": 121,
"type": "Polygon"
}
drawImg(drawData);
drawData = {
"areaIndex": [
[
696,
188
],
[
784,
188
],
[
784,
470
],
[
696,
470
]
],
"leafletId": 98,
"type": "Rectangle"
}
drawImg(drawData);
drawData = {
"areaIndex": [
406,
296
],
"radius": 73,
"leafletId": 162,
"type": "Circle"
}
drawImg(drawData);
drawData = {
"areaIndex": [
388,
582
],
"content": "的的",
"leafletId": 156,
"type": "Text"
}
drawImg(drawData);
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<base target="_top">
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Quick Start - Leaflet</title>
<script src="jquery-3.6.3.min.js"></script>
<link rel="stylesheet" href="leaflet/leaflet.css"/>
<script src="leaflet/leaflet.js"></script>
<link rel="stylesheet" href="leaflet/plugins/leaflet-geoman.css"/>
<script src="leaflet/plugins/leaflet-geoman.min.js"></script>
<style>
html, body {
height: 100%;
margin: 0;
}
#map { height: 780px; width:100% }
</style>
</head>
<body>
<div id="map"></div>
</body>
<script>
var map = L.map('map', {
crs: L.CRS.Simple,
minZoom: -5 //缩放比例
});
var bounds = [[0,0], [1000,1000]];
var image = L.imageOverlay('floor.png', bounds).addTo(map);
map.fitBounds(bounds);
/*
var icon = L.divIcon({
html:"<div style='width:120px;'>茶水间</div>",
className:'my-divIcon',
iconSize:30
});
L.marker([388,582],{icon:icon}).addTo(map);*/
// 添加左侧按钮
map.pm.addControls({
position: "topleft",
drawPolygon: true, // 添加绘制多边形
drawMarker: true, //添加按钮以绘制标记
drawRectangle: true, //添加按钮绘制矩形
drawCircleMarker: true, //添加按钮以绘制圆形标记
drawPolyline: true, //添加按钮绘制线条
drawCircle: true, // 添加按钮绘制圆圈
editMode: true, // 添加按钮编辑多边形
dragMode: true, // 添加按钮拖动多边形
cutPolygon: true, // 添加一个按钮以删除图层里面的部分内容
removalMode: true, // 清除图层
});
map.pm.setPathOptions({
color: "#3089FD",
fillColor: "#3089FD",
fillOpacity: 0.2
});
map.pm.setLang('zh');
getlatLngs();
var drawData = {
"areaIndex": [
566,
312
],
"leafletId": 150,
"type": "Marker"
}
drawImg(drawData);
drawData = {
"areaIndex": [
[
556,
426
],
[
570,
646
]
],
"leafletId": 170,
"type": "Line"
}
drawImg(drawData);
drawData = {
"areaIndex": [
[
768,
546
],
[
776,
848
],
[
724,
752
]
],
"leafletId": 121,
"type": "Polygon"
}
drawImg(drawData);
drawData = {
"areaIndex": [
[
696,
188
],
[
784,
188
],
[
784,
470
],
[
696,
470
]
],
"leafletId": 98,
"type": "Rectangle"
}
drawImg(drawData);
drawData = {
"areaIndex": [
406,
296
],
"radius": 73,
"leafletId": 162,
"type": "Circle"
}
drawImg(drawData);
drawData = {
"areaIndex": [
388,
582
],
"content": "的的",
"leafletId": 156,
"type": "Text"
}
drawImg(drawData);
//console.log(map)
/**
获取绘制的数据
*/
function getlatLngs() {
var drawData = {};
map.on("pm:remove", e => {
let leaflet_id = e.layer._leaflet_id
});
// //pm:drawstart 开始第一个点的时候调用
// //pm:drawend 禁止绘制时调用
// //pm:create 创建完成时调用
map.on("pm:drawstart", e => {
});
map.on("pm:create", e => {
if (e.shape == 'Marker') {
let index = [e.layer._latlng.lat, e.layer._latlng.lng]
drawData = {areaIndex:index,leafletId: e.layer._leaflet_id,type:e.shape}
}else if(e.shape=='Line'){
let listIndex = [];
e.layer._latlngs.forEach(item=>{
let zindex = [item.lat, item.lng]
listIndex.push(zindex)
})
drawData = {areaIndex:listIndex,leafletId: e.layer._leaflet_id,type:e.shape}
}else if(e.shape=='Text'){
let index = [e.layer._latlng.lat, e.layer._latlng.lng];
drawData = {areaIndex:index,content:e.layer.options.text,leafletId: e.layer._leaflet_id,type:e.shape}
}else if(e.shape=='Circle'||e.shape=='CircleMarker'){
let index = [e.layer._latlng.lat, e.layer._latlng.lng];
drawData = {areaIndex:index,radius:e.layer._radius,leafletId: e.layer._leaflet_id,type:e.shape}
}
else if(e.shape=='Rectangle'||e.shape=='Polygon'){
let indexs = e.layer._latlngs;
let listIndex = []
indexs[0].forEach(item => {
let zindex = [item.lat, item.lng]
listIndex.push(zindex)
})
drawData = {areaIndex:listIndex,leafletId: e.layer._leaflet_id,type:e.shape}
}
//console.log(drawData)
});
}
/**
绘制图形
*/
function drawImg(drawData) {
var item = drawData;
if(item.type == 'Marker'){
let drawRadius = L.marker( item.areaIndex, {color: "red"}).addTo(map);
let leaflet_id = drawRadius._leaflet_id;
item.leafletId = leaflet_id;
drawRadius.on({pm:'edit'}, obj => {
obj.target.setStyle({ color: "orange" });
});
}else if(item.type=='Line'){
let style1 ={
weight: 2,
color: "#3089FD",
fillOpacity: 0.2
}
let drawObj = L.polygon(item.areaIndex, style1);
let upFlag = true
drawObj.addTo(map);
let leaflet_id = drawObj._leaflet_id;
item.leafletId = leaflet_id;
drawObj.on({pm:'edit'
}, obj => {
obj.target.setStyle({color: "orange"});
});
}else if(item.type=='Circle'||item.type=='CircleMarker'){
var circle = L.circle(item.areaIndex, {
color: '3089FD',
fillColor: '#3089FD',
fillOpacity: 0.7,
radius: item.radius
}).addTo(map);
}else if(item.type=='Text'){
var icon = L.divIcon({
html:"<span>"+item.content+"</span>",
className:'my-divIcon',
iconSize:30
});
L.marker(item.areaIndex,{icon:icon}).addTo(map);
}
else{
//矩形
// color:线段颜色
// weight:线段宽度
// opacity:线段透明度
// dashArray:虚线间隔
// fill:是否填充内部(true/false)
// fillColor:内部填充颜色,如不设置,默认为color颜色
// fillOpacity:内部填充透明度
let style1 ={
weight: 2,
color: "#3089FD",
fillOpacity: 0.2
}
let drawObj = L.polygon(item.areaIndex, style1);
drawObj.addTo(map);
let leaflet_id = drawObj._leaflet_id;
item.leafletId = leaflet_id;
drawObj.on({pm:'edit'
}, obj => {
obj.target.setStyle({color: "orange"});
});
}
}
</script>
</html>
更多插件
https://www.cnblogs.com/defineconst/p/13560438.html