Leaflet绘制平面图:
背景:区域楼层定位平面展示,根据wifi信号强弱进行定位
这是效果图
点击查看 =>在线演示地址
源代码 200多行代码
优化,增加了很多插件
html
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>测试demo</title>
<link rel="stylesheet" href="https://unpkg.com/leaflet@1.6.0/dist/leaflet.css"/>
<link rel="stylesheet" href="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.css"/>
<link rel="stylesheet" href="https://unpkg.com/leaflet-fullscreen/dist/leaflet.fullscreen.css"/>
<link rel="stylesheet" href="https://unpkg.com/leaflet-measure/dist/leaflet-measure.css"/>
<link rel="stylesheet" href="https://unpkg.com/leaflet.pm/dist/leaflet.pm.css"/>
<script src="https://unpkg.com/leaflet@1.6.0/dist/leaflet.js"></script>
<script src="https://unpkg.com/leaflet-control-geocoder/dist/Control.Geocoder.js"></script>
<script src="https://unpkg.com/leaflet-fullscreen/dist/Leaflet.fullscreen.min.js"></script>
<script src="https://unpkg.com/leaflet-measure/dist/leaflet-measure.js"></script>
<script src="https://unpkg.com/leaflet.pm/dist/leaflet.pm.min.js"></script>
<style>
html, body {
padding: 0;
margin: 0;
height: 100%;
}
#map {
width: 100%;
height: 100%;
}
#controls {
position: absolute;
top: 10px;
right: 10px;
z-index: 1000;
background: rgba(255, 255, 255, 0.8);
padding: 10px;
border-radius: 5px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.label-icon {
background: rgba(255, 255, 255, 0.8);
padding: 5px 10px;
border-radius: 5px;
color: black;
font-size: 14px;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
</style>
</head>
<body>
<div id="map"></div>
<div id="controls">
<input type="text" id="appMac" placeholder="请输入AppMac 地址">
<input type="text" id="appName" placeholder="请输入名称">
<button onclick="">标注文字</button>
</div>
<script>
const map = L.map('map');
const imageUrl = './imag.jpg';
const imageBounds = [[-10.712216, -74.22655], [-10.773941, -74.12544]];
const imageOverlay = L.imageOverlay(imageUrl, imageBounds).addTo(map);
map.fitBounds(imageBounds);
// 存储标记的数组
const markers = [];
// 生成随机坐标并添加标记
function addRandomMarkers() {
// const bounds = imageOverlay.getBounds();
// const northWest = bounds.getNorthWest();
// const southEast = bounds.getSouthEast();
//
// for (let i = 0; i < 3; i++) {
// const randomLat = Math.random() * (southEast.lat - northWest.lat) + northWest.lat;
// const randomLng = Math.random() * (southEast.lng - northWest.lng) + northWest.lng;
// const latlng = L.latLng(randomLat, randomLng);
//
// // 计算相对坐标
// const relativeX = (latlng.lng - northWest.lng) / (southEast.lng - northWest.lng);
// const relativeY = (southEast.lat - latlng.lat) / (southEast.lat - northWest.lat);
//
// // 创建标记并添加到地图
// const marker = L.marker(latlng).addTo(map);
//
// // 绑定弹窗并打开
// marker.bindPopup(`x轴坐标:${relativeX.toFixed(6)}<br>y轴坐标:${relativeY.toFixed(6)}`).openPopup();
//
// // 将标记添加到数组中
// markers.push(marker);
//
// // 发送相对坐标到后端
// // sendCoordinatesToBackend(relativeX, relativeY);
// }
}
// 获取点击位置的相对坐标
// map.on('click', function(e) {
// const latlng = e.latlng;
// const bounds = imageOverlay.getBounds();
// const northWest = bounds.getNorthWest();
// const southEast = bounds.getSouthEast();
//
// // 计算相对坐标
// const relativeX = (latlng.lng - northWest.lng) / (southEast.lng - northWest.lng);
// const relativeY = (southEast.lat - latlng.lat) / (southEast.lat - northWest.lat);
//
// // 创建标记并添加到地图
// const marker = L.marker(latlng).addTo(map);
//
// // 绑定弹窗并打开
// marker.bindPopup(`x轴坐标:${relativeX.toFixed(6)}<br>y轴坐标:${relativeY.toFixed(6)}`).openPopup();
//
// // 将标记添加到数组中
// markers.push(marker);
//
// // 发送相对坐标到后端
// sendCoordinatesToBackend(relativeX, relativeY);
// });
function sendCoordinatesToBackend(x, y) {
fetch('/api/save-coordinates', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({x, y})
})
.then(response => response.json())
// .then(data => console.log('Success:', data)) // 注释掉或移除这一行
.catch((error) => console.error('Error:', error));
}
// 页面加载时添加随机标记
window.onload = function () {
addRandomMarkers();
};
// 移动标记的函数
let currentMarkerIndex = 0;
function moveMarkersInOrder() {
if (markers.length === 0) return;
// 获取最后一个标记
const lastMarker = markers[markers.length - 1];
// 计算新的随机坐标
const bounds = imageOverlay.getBounds();
const northWest = bounds.getNorthWest();
const southEast = bounds.getSouthEast();
const randomLat = Math.random() * (southEast.lat - northWest.lat) + northWest.lat;
const randomLng = Math.random() * (southEast.lng - northWest.lng) + northWest.lng;
const nextLatLng = L.latLng(randomLat, randomLng);
// 使用 requestAnimationFrame 来实现平滑移动
requestAnimationFrame(() => {
lastMarker.setLatLng(nextLatLng);
moveMarkersInOrder();
});
}
// 假设的获取WiFi定位数据的函数,模拟返回不同的随机坐标
function getWifiLocation() {
const bounds = imageOverlay.getBounds();
const northWest = bounds.getNorthWest();
const southEast = bounds.getSouthEast();
const randomLat = Math.random() * (southEast.lat - northWest.lat) + northWest.lat;
const randomLng = Math.random() * (southEast.lng - northWest.lng) + northWest.lng;
return L.latLng(randomLat, randomLng);
}
// 创建用户标记
let userMarker = L.marker(getWifiLocation()).addTo(map);
// 计算初始相对坐标
const initialLatLng = userMarker.getLatLng();
const bounds = imageOverlay.getBounds();
const northWest = bounds.getNorthWest();
const southEast = bounds.getSouthEast();
const initialRelativeX = (initialLatLng.lng - northWest.lng) / (southEast.lng - northWest.lng);
const initialRelativeY = (southEast.lat - initialLatLng.lat) / (southEast.lat - northWest.lat);
// 绑定弹窗并打开
userMarker.bindPopup(`x轴坐标:${initialRelativeX.toFixed(6)}<br>y轴坐标:${initialRelativeY.toFixed(6)}`).openPopup();
// 更新用户位置的函数
function updateUserLocation() {
// 获取新的目标坐标
const newLatLng = getWifiLocation();
// 获取用户标记当前的坐标
const currentLatLng = userMarker.getLatLng();
// 使用 requestAnimationFrame 实现平滑移动
const duration = 2000; // 移动持续时间(毫秒)
const startTime = performance.now(); // 记录动画开始的时间
function animate(time) {
// 计算已经过去的时间
const elapsedTime = time - startTime;
// 计算动画进度(0 到 1 之间)
const progress = Math.min(elapsedTime / duration, 1);
// 计算新的纬度
const lat = currentLatLng.lat + (newLatLng.lat - currentLatLng.lat) * progress;
// 计算新的经度
const lng = currentLatLng.lng + (newLatLng.lng - currentLatLng.lng) * progress;
// 更新用户标记的位置
userMarker.setLatLng(L.latLng(lat, lng));
// 计算相对坐标
const bounds = imageOverlay.getBounds();
const northWest = bounds.getNorthWest();
const southEast = bounds.getSouthEast();
const relativeX = (lng - northWest.lng) / (southEast.lng - northWest.lng);
const relativeY = (southEast.lat - lat) / (southEast.lat - northWest.lat);
// 更新弹窗内容
userMarker.getPopup().setContent(`x轴坐标:${relativeX.toFixed(6)}<br>y轴坐标:${relativeY.toFixed(6)}`);
// 如果动画进度小于 1,继续动画
if (progress < 1) {
requestAnimationFrame(animate);
}
}
// 开始动画
requestAnimationFrame(animate);
}
// 模拟定时更新用户位置
setInterval(updateUserLocation, 4000); // 每4秒更新一次位置
// 添加图层切换控件
const baseMaps = {
"图像叠加": imageOverlay
};
L.control.layers(baseMaps, {}, {position: 'topright'}).addTo(map);
// 添加全屏控件
map.addControl(new L.Control.Fullscreen({
title: {
'false': '全屏显示',
'true': '退出全屏'
}
}));
// 添加测量控件
const measureControl = new L.Control.Measure({
primaryLengthUnit: 'meters',
secondaryLengthUnit: 'kilometers',
primaryAreaUnit: 'sqmeters',
secondaryAreaUnit: 'hectares',
activeColor: '#ABE67E',
completedColor: '#38ADD1',
position: 'topright',
units: {
feet: '英尺',
nautical_mile: '海里',
metric: '米',
imperial: '英里'
}
});
measureControl.addTo(map);
// 添加地理编码控件
L.Control.geocoder({
position: 'topright',
placeholder: '搜索地址',
errorMessage: '未找到地址'
}).addTo(map);
// 初始化 Leaflet.PM
map.pm.addControls({
position: 'topleft',
drawCircle: false, // 禁用绘制圆形
drawPolyline: true, // 启用绘制折线
drawRectangle: true, // 启用绘制矩形
drawPolygon: true, // 启用绘制多边形
drawMarker: true, // 启用绘制标记
editMode: true, // 启用编辑模式
removalMode: true // 启用删除模式
});
// 设置 Leaflet.PM 的中文提示信息
map.pm.setLang('zh');
// 监听绘制事件
map.on('pm:create', (e) => {
console.log('Created layer:', e.layer);
});
map.on('pm:edit', (e) => {
console.log('Edited layer:', e.layer);
});
map.on('pm:remove', (e) => {
console.log('Removed layer:', e.layer);
});
</script>
</body>
</html>