需求:
用户自定义上传一张平面图,然后可以在平面图内标点、绘制面、并且能够弹出相对应点、面的信息,信息可编辑,类似下图:
相关实现技术:leaflet
中文网:Leaflet - 一个交互式地图 JavaScript 库 (leafletjs.cn)
官方网:Leaflet - a JavaScript library for interactive maps (leafletjs.com)
官网相关示例:Overlays - Leaflet - 一个交互式地图 JavaScript 库 (leafletjs.cn)
相关js跟css资源可在npm中下载:leaflet - npm (npmjs.com)
开始编码demo
实现功能如下:加载平面图并默认生成一个点跟面、新增绘制点,面功能、编辑点,面弹出消息功能、删除点面功能、以及设置绘制面的颜色。全部代码如下:
<html>
<head>
<link rel="stylesheet" href="./leaflet.css" />
<script type="text/javascript" src="./jquery.min.js"></script>
<script src="./leaflet.js"> </script>
<script src="./leaflet-src.js"> </script>
<script src="./leaflet-src.esm.js"> </script>
<style>
#image-map {
min-width: 60vw;
height: 80vh;
border: 1px solid #ccc;
margin-bottom: 10px;
}
#buttons {
margin: 10px;
}
.activeBtn {
color: white;
background-color: #7070ff;
}
.btn {
margin-right: 10px;
padding: 10px;
border: none;
cursor: pointer;
}
.btnCol {
margin-right: 10px;
padding: 10px;
border: none;
cursor: pointer;
}
.delete-button {
position: absolute;
right: 0;
}
#markers-list,
#polygons-list {
flex: 1;
display: flex;
flex-direction: column;
align-items: center;
}
#markers-list>div,
#polygons-list>div {
position: relative;
}
#markers-list .childDiv,
#polygons-list .childDiv {
cursor: pointer;
margin: 10px;
width: 150px;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
border: 1px solid #eaeaea;
border-radius: 50px;
padding: 10px;
}
.allBox {
display: flex;
justify-content: space-between;
}
/* .leaflet-important {
transform: translate3d(0px, 0px, 0px) !important;
} */
.leaflet-container {
background-color: white;
}
.leaflet-bottom {
display: none;
}
</style>
</head>
<body>
<div id="buttons">
<button id="add-marker-button" class="btn">新增标记点</button>
<button id="draw-area-button" class="btn">绘制区域</button>
<button id="red" class="btnCol">红色</button>
<button id="green" class="btnCol activeBtn">绿色</button>
<button id="orange" class="btnCol">橙色</button>
</div>
<div class="allBox">
<div id="markers-list"></div>
<div id="image-map"></div>
<div id="polygons-list"></div>
</div>
<script>
$(document).ready(function () {
// 切换颜色按钮
var DBXcolor = 'green';
$("#red").click(function () {
DBXcolor = 'red'
$(".btnCol.activeBtn").removeClass("activeBtn");
$(this).addClass('activeBtn')
})
$("#green").click(function () {
DBXcolor = 'green'
$(".btnCol.activeBtn").removeClass("activeBtn");
$(this).addClass('activeBtn')
})
$("#orange").click(function () {
DBXcolor = 'orange'
$(".btnCol.activeBtn").removeClass("activeBtn");
$(this).addClass('activeBtn')
})
// 创建一个Leaflet地图实例
var map = L.map('image-map', {
minZoom: 1, // 设置地图的最小缩放级别
maxZoom: 4, // 设置地图的最大缩放级别
crs: L.CRS.Simple // 使用简单的坐标参考系统,适用于平面图片的映射
});
// 定义图片的宽度和高度,以及图片的路径
let w = 3200,
h = 2600,
url = './base.png';
// 将像素坐标转换为经纬度坐标,并定义图片的边界(西南和东北角)
var southWest = map.unproject([0, h], map.getMaxZoom() - 1); // 西南角像素坐标映射为经纬度坐标
var northEast = map.unproject([w, 0], map.getMaxZoom() - 1); // 东北角像素坐标映射为经纬度坐标
var bounds = new L.LatLngBounds(southWest, northEast); // 创建边界范围
// 在地图上添加图片覆盖层,并指定图片的边界范围以及位置
L.imageOverlay(url, bounds).addTo(map);
map.fitBounds(bounds);
// 在地图上添加标记,并绑定弹出窗口,默认情况下弹出窗口是打开的
let newMarkerId1 = generateUniqueId('marker');
let customIcon = L.icon({
iconUrl: 'one.gif',
iconSize: [64, 52], // 设置图标大小
iconAnchor: [32, 32], // 设置图标的中心点
popupAnchor: [0, -32] // 设置弹出窗口的位置
});
let divStr = `
<div>
<div style="display: flex;">
<div>相机名称:</div>
<div>xxx</div>
</div>
<div style="display: flex;">
<div>报警地址:</div>
<div>xxx</div>
</div>
<div style="display: flex;">
<div>报警类型:</div>
<div>xxx</div>
</div>
<div style="display: flex;">
<div>报警次数:</div>
<div>xxx</div>
</div>
</div>`
let marker = L.marker([-180, 200], { icon: customIcon, id: newMarkerId1 }).addTo(map).bindPopup(divStr).openPopup();
// 添加mouseover事件
marker.on('mouseover', function (e) {
this.openPopup();
});
// 添加mouseout事件
marker.on('mouseout', function (e) {
this.closePopup();
});
// 在地图上添加一个多边形,并绑定弹出窗口
let newPolygonId = generateUniqueId('polygon');
L.polygon([
[-200, 80],
[-250, 80],
[-120, 150],
[-140, 100]
], { id: newPolygonId, color: 'green' }).addTo(map).bindPopup("多边形");
function generateUniqueId(prefix) {
return prefix + '_' + Date.now() + '_' + Math.random().toString(36).substr(2, 9);
}
// 获取当前地图上所有的 marker 和 polygon 信息
function getMapLayersInfo() {
let markers = [];
let polygons = [];
map.eachLayer(function (layer) {
if (layer instanceof L.Marker) {
let markerInfo = {
id: layer.options.id || null,
latlng: layer.getLatLng(),
popup: layer.getPopup() ? layer.getPopup().getContent() : null
};
markers.push(markerInfo);
} else if (layer instanceof L.Polygon) {
let polygonInfo = {
id: layer.options.id || null,
latlngs: layer.getLatLngs(),
popup: layer.getPopup() ? layer.getPopup().getContent() : null
};
polygons.push(polygonInfo);
}
});
// 遍历并添加markers到HTML中
let markersList = $('#markers-list');
markersList.empty();
$.each(markers, function (index, marker) {
let markerElementBox = $('<div></div>');
let markerElementChild = $('<div></div>').text(`Marker ID: ${marker.id}`).addClass('childDiv');
// 创建删除按钮
let deleteButton = $('<button>X</button>').addClass('delete-button');
deleteButton.on('click', function (event) {
event.stopPropagation();
handleMarkerDoubleClick(marker.id);
});
// 将删除按钮添加到 markerElementChild 中
markerElementBox.append(deleteButton);
markerElementBox.append(markerElementChild);
markerElementChild.on('click', function () {
handleMarkerClick(marker.id);
});
markersList.append(markerElementBox);
});
// 遍历并添加polygons到HTML中
let polygonsList = $('#polygons-list');
polygonsList.empty();
$.each(polygons, function (index, polygon) {
let polygonElementBox = $('<div></div>');
let polygonElementChild = $('<div></div>').text(`Polygon ID: ${polygon.id}`).addClass('childDiv');
// 创建删除按钮
let deleteButton = $('<button>X</button>').addClass('delete-button');
deleteButton.on('click', function (event) {
event.stopPropagation();
handlePolygonDoubleClick(polygon.id);
});
// 将删除按钮添加到 polygonElementChild 中
polygonElementBox.append(deleteButton);
polygonElementBox.append(polygonElementChild);
polygonElementChild.on('click', function () {
handlePolygonClick(polygon.id);
});
polygonsList.append(polygonElementBox);
});
console.log("Markers:", markers);
console.log("Polygons:", polygons);
}
// 处理标注点双击事件
function handleMarkerDoubleClick(markerId) {
console.log("Double clicked Marker ID:", markerId);
// 进行其他操作
map.eachLayer(function (layer) {
// 如果图层是标记且具有与目标ID匹配的自定义ID
if (layer instanceof L.Marker && layer.options.id === markerId) {
map.removeLayer(layer);
getMapLayersInfo()
}
});
}
// 处理多边形双击事件
function handlePolygonDoubleClick(polygonId) {
console.log("Double clicked Polygon ID:", polygonId);
// 进行其他操作,如放大多边形或显示相关信息
map.eachLayer(function (layer) {
// 如果图层是标记且具有与目标ID匹配的自定义ID
if (layer instanceof L.Polygon && layer.options.id === polygonId) {
map.removeLayer(layer);
getMapLayersInfo()
}
});
}
// 处理点击标记的事件
function handleMarkerClick(markerId) {
console.log("Clicked Marker ID:", markerId);
// 进行其他操作,如高亮标记或显示相关信息
// 通过 ID 获取对应的标记对象
// 遍历地图上的每个图层
map.eachLayer(function (layer) {
// 如果图层是标记且具有与目标ID匹配的自定义ID
if (layer instanceof L.Marker && layer.options.id === markerId) {
let currentPopupContent = layer.getPopup() ? layer.getPopup().getContent() : '';
// 修改标记的 bindPopup 值
console.log(currentPopupContent);
let newPopupContent = prompt(currentPopupContent);
if (newPopupContent !== null) {
layer.bindPopup(newPopupContent).openPopup();
}
}
});
}
// 处理点击多边形的事件
function handlePolygonClick(polygonId) {
console.log("Clicked Polygon ID:", polygonId);
// 进行其他操作,如高亮多边形或显示相关信息
map.eachLayer(function (layer) {
// 如果图层是标记且具有与目标ID匹配的自定义ID
if (layer instanceof L.Polygon && layer.options.id === polygonId) {
let currentPopupContent = layer.getPopup() ? layer.getPopup().getContent() : '';
// 修改标记的 bindPopup 值
let newPopupContent = prompt(currentPopupContent);
if (newPopupContent !== null) {
layer.bindPopup(newPopupContent).openPopup();
}
}
});
}
// 示例调用获取地图上所有的 marker 和 polygon 信息
getMapLayersInfo();
// 新增标记点功能
let addMarkerMode = false; // 标记是否处于新增标记模式
let drawAreaMode = false; // 标记是否处于绘制区域模式
let polygon; // 保存当前绘制的多边形
let latlngs = []; // 保存多边形顶点的数组
$('#add-marker-button').on('click', function () {
addMarkerMode = !addMarkerMode; // 切换模式
if (addMarkerMode) {
$(this).text("取消新增标记点").addClass('activeBtn');
// 确保绘制区域模式关闭
drawAreaMode = false;
$('#draw-area-button').text("绘制区域");
map.on('click', onMapClick);
} else {
$(this).text("新增标记点").removeClass('activeBtn');
map.off('click', onMapClick);
getMapLayersInfo();
}
});
$('#draw-area-button').on('click', function () {
drawAreaMode = !drawAreaMode; // 切换模式
if (drawAreaMode) {
$(this).text("完成绘制区域").addClass('activeBtn');
// 确保新增标记模式关闭
addMarkerMode = false;
$('#add-marker-button').text("新增标记点");
map.on('click', onDrawAreaClick);
} else {
$(this).text("绘制区域").removeClass('activeBtn');
map.off('click', onDrawAreaClick);
if (latlngs.length > 2) {
let popupContent = prompt("请输入区域的弹出内容:");
if (popupContent) {
map.removeLayer(polygon);
let newPolygonId = generateUniqueId('polygon');
L.polygon(latlngs, { id: newPolygonId, color: DBXcolor }).addTo(map).bindPopup(popupContent).openPopup();
getMapLayersInfo();
} else {
map.removeLayer(polygon); // 移除多边形
}
latlngs = []; // 重置顶点数组
} else {
alert('不足三个点');
}
}
});
function onMapClick(e) {
let popupContent = prompt("请输入标记点的弹出内容:");
if (popupContent) {
let newMarkerId = generateUniqueId('marker');
L.marker(e.latlng, { icon: customIcon, id: newMarkerId }).addTo(map).bindPopup(popupContent).openPopup();
}
}
function onDrawAreaClick(e) {
latlngs.push(e.latlng); // 添加顶点到数组
if (polygon) {
map.removeLayer(polygon); // 移除之前的临时多边形
}
polygon = L.polygon(latlngs, { color: DBXcolor }).addTo(map); // 创建临时多边形
}
});
</script>
</body>
</html>
文件夹如下:
效果图如下: