leaflet室内地图\平面图点线面绘制

news2024/11/24 17:03:08

需求:

用户自定义上传一张平面图,然后可以在平面图内标点、绘制面、并且能够弹出相对应点、面的信息,信息可编辑,类似下图:

相关实现技术: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>

文件夹如下:

效果图如下:

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1835744.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

友思特应用 | 模型链接一应俱全:IC多类别视觉检测一站式解决方案

导读 高精度IC制造工艺需要对产品进行全方位检测以保证工艺质量过关。友思特 Neuro-T 通过调用平台的流程图功能&#xff0c;搭建多类深度学习模型&#xff0c;形成了一站式的视觉检测解决方案。本文将为您详述方案搭建过程与实际应用效果。 在当今集成电路&#xff08;IC&…

润滑不良:滚珠花键磨损的隐形杀手!

滚珠花键作为一种精密机械传动元件&#xff0c;被广泛应用于各种机器和设备中&#xff0c;起着传递动力和运动的重要作用。滚珠花键经过长时间的运行&#xff0c;难免会多少些磨损&#xff0c;严重的话还会导致设备不能正常运转。那么&#xff0c;如何保证它的正常运行呢&#…

frp安装与配置

个人从网上杂乱的信息中学习、试错&#xff0c;记录自己成功配置的方法&#xff0c;避免遗忘 一、frp的下载 因目前无法下载&#xff0c;仅保留下载方法&#xff0c;版本号根据实际修改&#xff0c;目前使用0.54版&#xff0c;不同系统下载不同文件。 wget https://github.c…

[渗透测试学习] BoardLight-HackTheBox

BoardLight-HackTheBox 信息搜集 nmap扫描一下 nmap -sV -v 10.10.11.11扫描结果如下 PORT STATE SERVICE VERSION 22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0) 80/tcp open http Apache httpd 2.4.41 ((Ubuntu))80端口有h…

分享一个自己写的PC版的Ai指令保存工具

今天给大家分享下我用非常古老的VB写的一个小工具。纯粹是每次电脑使用指令太麻烦了&#xff0c;所以写了一个小工具。这个工具支持5条指令&#xff0c;作为一般的应该够用了。使用场景&#xff1a;比如你要经常使用指令&#xff0c;但是觉得复制指令麻烦&#xff0c;那么你可以…

Qt | 简单的使用 QStyle 类(风格也称为样式)

01、前言 者在 pro 文件中已添加了正确的 QT+=widgets 语句 02、基础样式 1、QStyle 类继承自 QObject,该类是一个抽像类。 2、QStyle 类描述了 GUI 的界面外观,Qt 的内置部件使用该类执行几乎所有的绘制,以确保 使这些部件看起来与本地部件完全相同。 3、Qt 内置了一系…

水利部:关于推进水利工程建设数字孪生的指导意见

从《关于大力推进智慧水利建设的指导意见》到《十四五智慧水利建设规划》&#xff0c;自2022年以来&#xff0c;水利部先后出台一系列文件部署“数字孪生水利”政策框架。2024年4月1日&#xff0c;水利部再次颁发《关于推进水利工程建设数字孪生的指导意见》&#xff0c;强调&a…

自注意力与卷积高效融合!多SOTA、兼顾低成本与高性能

在自注意力机制中&#xff0c;模型计算输入序列中不同位置的相关性得分&#xff0c;以生成连接权重&#xff0c;从而关注序列中的重要部分。而卷积通过滑动窗口的方式&#xff0c;在输入上应用相同权重矩阵来提取局部特征。 如果将以上两者结合&#xff0c;就可以同时利用自注…

[C#]winform基于opencvsharp实现黑白图像上色

【算法简介】 技术有时会提高艺术&#xff0c;但有时也会破坏艺术。着色黑白电影是一个可以追溯到1902年的古老想法。几十年来&#xff0c;许多电影创作者反对将黑白电影着色的想法&#xff0c;并将其视为对艺术的破坏。但今天它被接受为艺术形式的增强。该技术本身已经从艰苦…

PO、VO、BO、DAO、DTO、POJO

PO、VO、BO、DAO、DTO关系图 DAO&#xff08;Data Access Object&#xff09;数据访问对象&#xff0c;数据访问&#xff1a;顾名思义就是与数据库打交道&#xff0c;它封装了与底层数据源&#xff08;如数据库&#xff09;的交互逻辑。为业务逻辑层&#xff08;Service&#…

绿色免费离线版JS加密混淆工具 - 支持全景VR加密, 小程序js加密, H5网站加密

自从我们推出在线版的免费JS加密混淆工具以来&#xff0c;受到了广大用户的热烈欢迎。特别是全景开发人员&#xff0c;他们使用该工具加密VR插件的JS代码, 添加域名锁等&#xff0c;都非常有效地保护了插件的代码资源。 最近&#xff0c;我们收到了许多用户的反馈&#xff0c;…

【数据库编程-SQLite3(四)】基本常用操作

学习分享 1、sql数据类型1.1、约束 2、数据定义语言DDL(Data Definition Language)2.1、创建表2.2、修改表2.3、删除表 3、 数据操纵语言DML(Data Manipulation Language)3.1、INSERT语句3.2、DELETE语句3.3、UPDATE语句3.4、SELECT语句 4、高级查询4.1、SQL通配符4.2、模糊查询…

JAVA的优势是什么?

在开始前刚好我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「java的资料从专业入门到高级教程」&#xff0c; 点个关注在评论区回复“666”之后私信回复“666”&#xff0c;全部无偿共享给大家&#xff01;&#xff01;&#xff01; java编程语言自1995年问世…

基于 Vitis HLS 的单个乘法 DSP 映射研究

文章目录 1 自媒体账号2 引言3 整数乘法4 定点乘法5 浮点乘法6 总结 1 自媒体账号 目前运营的自媒体账号如下&#xff1a; 哔哩哔哩 【雪天鱼】: 雪天鱼个人主页-bilibili.com 如果觉得有所收获的话&#xff0c;可以点击我的主页 -> 充电 -> 自定义充电 支持一下&#…

人工智能在癌症新辅助治疗领域的研究|顶刊速递·24-06-18

小罗碎碎念 本期推文主题——人工智能在肿瘤新辅助治疗领域中的研究进展。 这一期推文谋划已久&#xff0c;最早可以追溯到五一假期。五一的时候&#xff0c;我第一站去的就是北大。当时和北医的一个师兄&#xff08;博士&#xff09;聊天&#xff0c;主要聊的就是新辅助治疗AI…

摄像头图像矫正的表格生成方法

1.设置单元格高宽 点击表格左上角 的 小三角 列宽: HOME -> Rows and Columns -> Column Width 5 CM 行高: HOME -> Rows and Columns -> Row Height 5 CM 2.设置 条件格式 HOME -> Conditional Formatting-> Manager Rules 点击 左上方 New Rule…

老杨说运维 | 如何结合现状进行运维路径建设(文末附演讲视频)

青城山脚下的滔滔江水奔涌而过&#xff0c;承载着擎创一往无前的势头&#xff0c;共同去向未来。2024年6月&#xff0c;双态IT成都用户大会擎创科技“数智化可观测赋能双态运维”专场迎来了完满的收尾。 本期回顾来自擎创科技CTO葛晓波的现场演讲&#xff1a;数智化转型的核心目…

PCI-E 5.0固态硬盘温度价格「双降」,速度近15GB/s

都 2024 年了&#xff0c;相信各位同学对固态硬盘都不陌生了吧。 随着技术的不断更新迭代&#xff0c;固态硬盘接口速率如今最高已经来到了 PCI-e 5.0 了。 其实这不算什么新技术了&#xff0c;早在2023年5月美光就上市了全球首款 PCI-e 5.0 固态硬盘&#xff0c; 英睿达 T700…

推动电子凭证服务革新,加速政务数字化转型

随着“互联网政务”的深入实施&#xff0c;电子凭证已成为政务服务数字化升级的关键要素。电子凭证不仅极大地方便了企业和群众&#xff0c;而且作为国家信息资源的重要组成部分&#xff0c;对于优化政务服务、加强社会治理和行业监管具有深远的影响。然而&#xff0c;由于政务…

Android-apk自动签名

一、创建apk签名 1、有得话忽略 Build->Generate Signed Bundle or APK&#xff0c;选择APK&#xff0c;然后Next&#xff0c;然后选择Create new 2、 2.在app/build.gradle中&#xff0c;在android{…}中添加以下内容 signingConfigs { release { storeFile file(androi…