leaflet绘制室内平面图

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>
相关推荐
硬件人某某某8 分钟前
Java基于SSM框架的社区团购系统小程序设计与实现(附源码,文档,部署)
java·开发语言·社区团购小程序·团购小程序·java社区团购小程序
kucupung20 分钟前
【C++基础】多线程并发场景下的同步方法
开发语言·c++
zzlyx9921 分钟前
.NET 9 微软官方推荐使用 Scalar 替代传统的 Swagger
javascript·microsoft·.net
Quantum&Coder26 分钟前
Objective-C语言的计算机基础
开发语言·后端·golang
五味香27 分钟前
Java学习,List 元素替换
android·java·开发语言·python·学习·golang·kotlin
Bunury28 分钟前
组件封装-List
javascript·数据结构·list
Joeysoda31 分钟前
Java数据结构 (从0构建链表(LinkedList))
java·linux·开发语言·数据结构·windows·链表·1024程序员节
迂幵myself31 分钟前
14-6-1C++的list
开发语言·c++·list
扫地僧00933 分钟前
(Java版本)基于JAVA的网络通讯系统设计与实现-毕业设计
java·开发语言
天乐敲代码34 分钟前
JAVASE入门九脚-集合框架ArrayList,LinkedList,HashSet,TreeSet,迭代
java·开发语言·算法