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

需求:

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

相关实现技术: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匹配的自定义IDif (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匹配的自定义IDif (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匹配的自定义IDif (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匹配的自定义IDif (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.mzph.cn/web/29213.shtml

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

相关文章

游戏运营与发行:从入门到实践

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…

在有限的分数有限下如何抉择?是选好专业还是选好学校

随着2024年高考的落幕&#xff0c;无数考生和家长站在了人生的重要十字路口。面对成绩单上的数字&#xff0c;一个难题摆在了面前&#xff1a;在分数限制下我们该如何平衡“心仪的专业”与“知名度更高的学校”之间的选择&#xff1f; 一、专业决定未来职业走向 选择一个好的专…

基于51单片机的脉搏测量仪—心率计

基于51单片机的脉搏测量仪 &#xff08;仿真&#xff0b;程序&#xff0b;原理图&#xff0b;设计报告&#xff09; 功能介绍 具体功能&#xff1a; 本系统由STC89C51/52单片机LCD1602显示模块5mm红外接收管LM358运放电路按键模块等构成 1.手指放到红外对管中&#xff0c;2…

Paragon NTFS For Mac 15软件下载_Paragon NTFS For Mac 15官网最新版下载

Paragon NTFS For Mac 15是一款强大的Mac读写工具。paragon ​​ntfs for mac​​​中文版为您轻松解决Mac不能识别 Windows NTFS文件难题&#xff0c;简单自如读写NTFS外置存储文件。在Mac OS X下全读/写访问NTFS的任何分区&#xff0c;paragon ntfs for mac中文版实现在Windo…

PostgreSQL源码分析——pg_control

pg_control为什么会存在&#xff1f; 为啥会有pg_control这么个文件呢&#xff1f; pg_control是PostgreSQL中一个很重要的文件&#xff0c;我们之前讲到过PostgreSQL的启动过程&#xff0c;启动过程中很重要的一项工作就是故障恢复&#xff0c;启动startup进程&#xff0c;回…

OpenSearch 与 Elasticsearch主要差异

1. 什么是 Elasticsearch&#xff1f; Elasticsearch 是一个基于 Apache Lucene 构建的开源、RESTful、分布式搜索和分析引擎。它旨在处理大量数据&#xff0c;使其成为日志和事件数据管理的流行选择。 Elasticsearch 还以其实时功能而闻名&#xff0c;允许用户在数据模式发生…

GO RACE 测试在低版本GCC上报错误 exit status 0xc0000139

windows机器环境&#xff0c;go程序使用race定位时一运行就报错&#xff0c;写了个example如&#xff1a; 能看到加了race之后就不行了&#xff0c; 搜了一下&#xff0c;git上有个issue&#xff1a; runtime: Race detector causes exit status 0xc0000139 on Windows 11 wi…

酷开会员 | 酷开系统将艺术、回忆与浪漫融入生活

随着审美观念的改变以及技术的提升&#xff0c;消费者对家用电视的需求已不局限于单纯的功能性&#xff0c;外观设计带来的美感与视觉效果也愈发成为消费者关注的焦点。在画质和功能逐步完善的当下&#xff0c;电视中的壁纸模式&#xff0c;则能让其更好地融入家居环境&#xf…

基于WPF技术的换热站智能监控系统17--项目总结

1、项目颜值&#xff0c;你打几分&#xff1f; 基于WPF技术的换热站智能监控系统01--项目创建-CSDN博客 基于WPF技术的换热站智能监控系统02--标题栏实现-CSDN博客 基于WPF技术的换热站智能监控系统03--实现左侧加载动画_wpf控制系统-CSDN博客 基于WPF技术的换热站智能监…

船舶能源新纪元:智能管理引领绿色航运潮流

在蓝色的大海上&#xff0c;无数船只乘风破浪&#xff0c;为全球的贸易和文化交流贡献着力量。然而&#xff0c;随着环保意识的提升和可持续发展的要求&#xff0c;船舶的能源消耗和排放问题逐渐成为了人们关注的焦点。在这个关键时刻&#xff0c;船舶能源管理系统应运而生&…

台球灯控计费系统安装教程,佳易王桌球房计费系统的安装方法教程

台球灯控计费系统安装教程&#xff0c;佳易王桌球房计费系统的安装方法教程 一、软件操作教程 以下软件操作教程以&#xff0c;佳易王台球计时计费管理软件为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 1、点击计时开灯&#xff0c;相应的灯…

【深度学习驱动流体力学】OpenFOAM框架剖析

目录 1. applications 目录solvers&#xff1a;存放各种求解器。mesh&#xff1a;网格生成相关工具。 2. src 目录3. tutorials 目录其他主要目录和文件参考 OpenFOAM 源码文件目录的框架如下,OpenFOAM 是一个开源的计算流体力学 (CFD) 软件包&#xff0c;其源码文件结构设计精…

深入理解并打败C语言难关之一————指针(3)

前言&#xff1a; 昨天把指针最为基础的内容讲完了&#xff0c;并且详细说明了传值调用和传址调用的区别&#xff08;这次我也是做到了每日一更&#xff0c;感觉有好多想写的但是没有写完&#xff09;&#xff0c;下面不多废话&#xff0c;下面进入本文想要说的内容 目录&#…

windows解决clion终端中文乱码

windows解决clion终端中文乱码 问题&#xff1a; 解决办法&#xff1a; 添加system("chcp 65001 > nul");

聚六亚甲基单胍(PHMB)为第三代胍类消毒剂 我国应用场景广泛

聚六亚甲基单胍&#xff08;PHMB&#xff09;为第三代胍类消毒剂 我国应用场景广泛 聚六亚甲基单胍全称为聚六亚甲基胍盐酸盐&#xff0c;简称PHMG&#xff0c;是一种高分子有机聚合物&#xff0c;易溶于水&#xff0c;水溶液无色无味&#xff0c;无毒&#xff0c;生物相容性良…

欢迎 Stable Diffusion 3 加入 Diffusers

作为 Stability AI 的 Stable Diffusion 家族最新的模型&#xff0c;Stable Diffusion 3(SD3) 现已登陆 Hugging Face Hub&#xff0c;并且可用在 &#x1f9e8; Diffusers 中使用了。 Stable Diffusion 3https://stability.ai/news/stable-diffusion-3-research-paper 当前放出…

MapStruct对象转换

MapStruct是一个Java注解处理器&#xff0c;用于简化对象的转换 遇到的问题&#xff1a; java: Internal error in the mapping processor: java.lang.NullPointerException 解决方案&#xff1a;修改编辑器配置 -Djps.track.ap.dependenciesfalse

从0开始C++(一)

目录 c的基本介绍 C语言和C 的区别 面向过程和面向对象的区别 引用 引用使用的注意事项 赋值 终端输入 cin getline string字符串类 遍历方式 字符串和数字转换 函数 内联函数 函数重载overload 小练习&#xff1a; 参考代码 c的基本介绍 C是一种通用的高级编…

Unity基础(一)unity的下载与安装

目录 一:下载与安装 1.官网下载地址 2.推荐直接下载UnityHub 3.选择编辑器版本(推荐长期支持版) 4.在UnityHub安装选择相应的模块 二:创建项目 简介: Unity 是一款广泛应用的跨平台游戏开发引擎。 它具有以下显著特点&#xff1a; 强大的跨平台能力&#xff1a;能将开发的游…

JavaScript之内置对象

内置对象 JavaScript中的对象分为3种&#xff1a;自定义对象、内置对象、浏览器对象前面两种对象是javascript基础内容&#xff0c;属于ECMAScript&#xff1b;第三个浏览器对象属于我们javascript独有的&#xff0c;我们javascript API讲解 内置对象就是指javascript语言自带…