当前位置: 首页 > news >正文

vue3+leaflet案例:告警系统GIS一张图(附源码下载)

基于vue3+leaflet的告警系统GIS一张图案例,覆盖功能点包括底图切换、告警事件模拟统计、模拟实时告警点移动信息、模拟告警管理以及数据诊断等,适合学习leaflet与前端框架结合开发webgis开发可视化项目。

demo源码运行环境以及配置

运行环境:依赖Node安装环境,demo本地Node版本:推荐v18+。

运行工具:vscode或者其他工具。

前端项目配置方式:下载demo源码,vscode打开,然后顺序执行以下命令:
(1)下载demo环境依赖包命令:npm install
(2)启动demo命令:npm run dev
(3)打包demo命令: npm run build

技术栈

Vue 3.2.39

Vite 2.5.10

leaflet 1.9.4

示例效果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
核心源码

import { onMounted, onUnmounted, nextTick, ref, reactive } from "vue";
import L from "leaflet";
import * as echarts from "echarts";
import config from "../config";
import { getPointAlongLine, getDistanceBy2Points } from "../utils/geoUtils";
// import { useRouter } from "vue-router";
// import { ElMessage } from 'element-plus';
// const router = useRouter();
const isShowAlarmSystem = ref(false);
const isShowDataSystem = ref(false);
const isShowLeftMenu = ref(true);
// 告警事件统计图表对象
let myChartEvent = null;
// 诊断数据统计图表对象
let dataChartEvent = null;
// 按照日期切换统计图表
const radio = ref(1);
const alarmEventTal = ref({emergency: 100,important: 85,general: 200,prompt: 96
})
// 统计图表数据源-月
let echartsDataMonth = {"xAxis": ["雷击","外力破坏","覆冰","温度异常"],"yAxis": ["154","125","188","178",]
};
// 统计图表数据源-周
let echartsDataWeek = {"xAxis": ["雷击","外力破坏","覆冰","温度异常"],"yAxis": ["64","75","58","88",]
};
// 统计图表数据源-天
let echartsDataDay = {"xAxis": ["雷击","外力破坏","覆冰","温度异常"],"yAxis": ["34","45","48","58",]
};
// 数据诊断图表数据源
let timeXData = ['0.00', '4.09', '8.17', '12.26', '16.35', '20.43', '24.52', '28.60', '32.69', '36.78', '40.86', '44.95', '49.04', '53.12', '57.21', '61.30', '65.38', '69.47', '73.55', '77.64', '81.73', '85.81','89.90', '93.99', '98.07', '102.16', '106.25', '110.33', '114.42', '118.50', '122.59', '126.68', '130.76', '134.85', '138.94', '143.02', '147.11', '151.20', '155.28', '159.37', '163.45', '167.54', '171.63', '175.71', '179.80', '183.89','187.97', '192.06', '196.15', '200.23', '204.32', '208.40', '212.49', '216.58', '220.66', '224.75', '228.84', '232.92', '237.01', '241.10', '245.18', '249.27', '253.35', '257.44', '261.53', '265.61', '269.70', '273.79', '277.87', '281.96'
];
let timeYData = [13.996026, 25.939534, 24.403596, 24.156721, 22.940715, 20.067240, 12.103183, 11.610609, 10.647344, 11.052902, 12.320346, 12.305370, 19.471151, 18.599405, 28.122542, 28.465133, 28.932984, 29.017473, 38.485951, 38.036698, 38.076452, 47.790099, 47.922228, 37.787734,37.816976, 37.440732, 27.890553, 27.412264, 27.708192, 27.559679, 27.617277, 17.692056, 17.692056, 17.692056, 17.368156, 27.278625, 27.414416, 37.849099, 17.414209, 16.960530, 16.960530, 17.411365, 17.665097, 27.495684, 27.103351, 27.506305, 27.238517, 27.154806, 27.296673, 17.110990, 17.104874, 17.445841,17.249234, 17.576882, 18.122866, 17.470368, 17.534492, 17.575769, 7.834490, 7.624720, 7.938114, 7.337769, 7.783019, 7.611189, 7.107963, 7.622926, 8.084923, 7.655780, 7.464569, 7.790213
]
const formData = ref({id: '',data_type: '',channel_number: '',
})
// 数据诊断面板
let dataDialogVisible = ref(false);
// 告警管理面板
let alarmDialogVisible = ref(false);
const formAlarmData = reactive({id: '',alarmType: '',siteName: '',timeDate: '',shortcuts: [{text: '最近一周',value: () => {const end = new Date()const start = new Date()start.setTime(start.getTime() - 3600 * 1000 * 24 * 7)return [start, end]},},{text: '最近一个月',value: () => {const end = new Date()const start = new Date()start.setTime(start.getTime() - 3600 * 1000 * 24 * 30)return [start, end]},},{text: '最近3个月',value: () => {const end = new Date()const start = new Date()start.setTime(start.getTime() - 3600 * 1000 * 24 * 90)return [start, end]},},]
})
// 告警管理的实时告警信息表
const tableData = [{eventCode: '1',siteName: 'xx桩',mileageLocation: 'xx桩+0.34km',lon: 114.320941,lat: 34.799099,opticalCableDistance: '3.34km',peak: '50℃',alarmType: '高温异常',alarmLevel: '红色预警',alarmTime: '2025-03-31 14:00:00',startPoint: 912.3,sampleStep: 2.1,pointCount: 490,pointInfo: [{ postion: 2, Info: 40 },{ postion: 4, Info: 44 }]}
]
// 轨迹点模拟数据
const pointsSource = [[25.29132248, 99.34718605],[25.28893705, 99.34728116]
]
// 实时报警点数据
// const alarmPoint = [25.29054437177747, 99.34721946716309];
let alarmMarker = null;
let map = null; // 地图对象
let geojsonLayer = null; // 框选绘制图层对象
let alarmTimer = null; // 报警点更新定时器
let currentDistance = 0; // 当前距离累加器
onMounted(() => {nextTick(() => {// 初始化地图对象initMap();// 初始化轨迹点及线initPoints2Line(pointsSource);// 首页统计图表初始化initCharts(echartsDataMonth);});
});
// 组件卸载时清除定时器
onUnmounted(() => {if (alarmTimer) {clearInterval(alarmTimer);alarmTimer = null;}
});
// 初始化地图
const initMap = () => {// 创建地图对象map = L.map("map", {attributionControl: false,zoomControl: false,// maxZoom: 20,}).setView(config.mapInitParams.center, config.mapInitParams.zoom);map.on('click', function (e) {const latlng = e.latlng; // 获取点击位置的经纬度对象console.log("经度: " + latlng.lng + ", 纬度: " + latlng.lat);});//创建底图切换数据源const baseLayer1 = L.tileLayer(config.baseMaps[0].Url); //OSM街道图const baseLayer2 = L.tileLayer(config.baseMaps[1].Url); //ArcGIS影像图const baseLayer4 = L.layerGroup([L.tileLayer(config.baseMaps[3].Url, {subdomains: ["0", "1", "2", "3", "4", "5", "6", "7"],}), L.tileLayer(config.baseMaps[4].Url, {subdomains: ["0", "1", "2", "3", "4", "5", "6", "7"],})]); //天地图街道图const baseLayer5 = L.layerGroup([L.tileLayer(config.baseMaps[5].Url, {subdomains: ["0", "1", "2", "3", "4", "5", "6", "7"],}), L.tileLayer(config.baseMaps[6].Url, {subdomains: ["0", "1", "2", "3", "4", "5", "6", "7"],})]); //天地图影像图const baseLayer8 = L.tileLayer(config.baseMaps[9].Url, {subdomains: ["1", "2", "3", "4"],}); //高德街道图const baseLayer9 = L.tileLayer(config.baseMaps[10].Url, {subdomains: ["1", "2", "3", "4"],}); //高德影像图// 构建图层标题及图例const getTitle = (text, borderColor, fillColor, isBorderDashed) => {return `<i style='display:inline-block;border:${isBorderDashed ? "dashed" : "solid"} 2px ${borderColor};background:${fillColor};width:20px;height:20px;position:relative;top:4px;'></i><span style='padding-left:1px;margin-top: 2px;position: relative;top:1px;'>${text}</span>`;};// 构建图片形式的标题及图例const getImageTitle = (text, imgUrl, size) => {return `<div style='display:inline-block;width:${size}px;height:${size}px;position:relative;top:4px;'><img src='${imgUrl}' style='height:${size}px;'/></div><span style='padding-left:1px;margin-top: 2px;position: relative;top:1px;'>${text}</span>`;};map.addLayer(baseLayer2); //地图默认加载的底图// map.addLayer(labelPointLayer); //地图默认加载的叠加图层const baseLayers = {[getImageTitle(`OSM街道图`, `./img/OSMVector.png`, 35)]: baseLayer1,[getImageTitle(`ArcGIS影像图`, `./img/arcgisImage.png`, 35)]: baseLayer2,[getImageTitle(`天地图街道图`, `./img/tdtVector.png`, 35)]: baseLayer4,[getImageTitle(`天地图影像图`, `./img/tdtImage.png`, 35)]: baseLayer5,[getImageTitle(`高德街道图`, `./img/gaodeVector.png`, 35)]: baseLayer8,[getImageTitle(`高德街道图`, `./img/gaodeImage.png`, 35)]: baseLayer9,};//底图切换控件// const OHTER_SPOT_COLOR = "#006fff";// const SPOT_FILL_COLOR = "rgba(0,0,255,0.15)";// 专题图层const overlayMaps = {// [getImageTitle(`充电站`, `./img/labelPointMarker.png`, 20)]://   labelPointLayer,// [getImageTitle("热力图", `./img/heatmap.png`, 25)]: heatmapLayer,};//底图切换控件L.control.layers(baseLayers, overlayMaps, { position: 'bottomright' }).addTo(map);
};
// 自定义图标
// const customIcon = L.icon({
//   iconUrl: './public/img/labelPointMarker.png', // 图标图片路径
//   iconSize: [30, 30], // 图标大小(宽, 高)
// });
const getCustomIcon = (iconUrl, iconSize) => {return L.icon({iconUrl: iconUrl,iconSize: iconSize,iconAnchor: [iconSize[0] / 2, iconSize[1]]});
};
// 创建轨迹点及点连线
const initPoints2Line = (points) => {if (points.length > 0) {// 创建起点和终点for (let i = 0; i < points.length; i++) {let marker = L.marker(new L.LatLng(points[i][0],points[i][1]),{properties: {},icon: getCustomIcon(`./public/img/${i === 0 ? 'starPoint.png' : 'endPoint.png'}`, [42, 42]) // marker点图标}).addTo(map);}// 起点终点连线const polyline = L.polyline(points,{// color: '#1f9653',weight: 6}).addTo(map);// 模拟实时报警点更新const totalDistance = getDistanceBy2Points(pointsSource[0], pointsSource[1]);currentDistance = 0; // 重置当前距离// 清除可能存在的旧定时器if (alarmTimer) {clearInterval(alarmTimer);}refreshAlarmPoint(currentDistance + 10);// 设置定时器,每5秒执行一次,距离累加5米alarmTimer = setInterval(() => {currentDistance += 10; // 每次距离累加5米// 如果超过总距离,重置为起点if (currentDistance > totalDistance) {currentDistance = 0;}refreshAlarmPoint(currentDistance);}, 3000); // 3秒执行一次}
};
// 刷新绘制实时报警点
const refreshAlarmPoint = (distance) => {const alarmPoint = getPointAlongLine(pointsSource[0], pointsSource[1], distance);if (alarmPoint) {if (alarmMarker) {alarmMarker.remove();}alarmMarker = L.marker(alarmPoint, {icon: L.divIcon({className: 'animated-gif-icon',iconSize: [40, 40] // 设置图标大小以匹配 GIF 的大小})}).addTo(map);// alarmMarker.on('click', function () {//   const popup = L.popup()//     .setLatLng(alarmMarker.getLatLng())//     .setContent('我是一个弹窗')//     .openOn(map);// });const properties = tableData[0];map.closePopup();openMapPopup(properties);}
}
// 构造地图弹窗内容
const getPopupContent = (properties) => {return `预警点所属标桩名称:${properties.siteName}</br></br>预警类型:${properties.alarmType}</br></br>预警级别:${properties.alarmLevel}</br></br>预警时间:${properties.alarmTime}</br></br>坐标:${properties.lon},${properties.lat}`;
};
// 搜索结果列表点击定位地图弹窗
const openMapPopup = (properties) => {const elements = getPopupContent(properties);// const latlng = L.latLng(properties.lat, properties.lon);const latlng = alarmMarker.getLatLng();map.openPopup(elements, latlng);// map.setView(latlng, 15);
}
// 复位
const resetFun = () => {map.setView(config.mapInitParams.center, config.mapInitParams.zoom);
}
// 首页 月 周 天切换
const handleChange = (value) => {if (value === 1) {alarmEventTal.value = {emergency: 100,important: 85,general: 200,prompt: 96}initCharts(echartsDataMonth);} else if (value === 2) {alarmEventTal.value = {emergency: 70,important: 65,general: 150,prompt: 56}initCharts(echartsDataWeek);} else if (value === 3) {alarmEventTal.value = {emergency: 40,important: 45,general: 100,prompt: 36}initCharts(echartsDataDay);}
}
// 首页统计图表初始化
const initCharts = (echartsData) => {// 基于准备好的dom,初始化echarts实例const myChartDom = document.getElementById('echartsId');if (myChartDom) {myChartEvent?.dispose();}myChartEvent = echarts.init(myChartDom);const option = {title: {text: '告警事件统计分析',left: 'center',// left: '1%',top: '3%',textStyle: {color: '#fff',fontSize: '16',}},tooltip: {trigger: 'axis',axisPointer: {type: 'shadow'},backgroundColor: 'rgba(0, 0, 0, 0.35)', // 提示框背景颜色textStyle: {color: '#fff', // 提示框文本颜色fontSize: 14, // 文本字体大小// fontFamily: 'Arial' // 文本字体},// borderColor: '#000', // 设置Tooltip边框颜色为黑色borderWidth: 0, // 设置Tooltip边框宽度为2像素},grid: {left: '3%',top: '12%',right: '3%',bottom: '3%',containLabel: true},xAxis: [{type: 'category',data: echartsData.xAxis,axisLine: {show: true,lineStyle: {color: "rgba(255,255,255,.1)",width: 1,type: "solid"},},axisTick: {show: false,},axisLabel: {interval: 0,// rotate:50,show: true,splitNumber: 15,textStyle: {color: "rgba(255,255,255,.6)",fontSize: '12',},},}],yAxis: [{type: 'value',axisLabel: {//formatter: '{value} %'show: true,textStyle: {color: "rgba(255,255,255,.6)",fontSize: '12',},},axisTick: {show: false,},axisLine: {show: true,lineStyle: {color: "rgba(255,255,255,.1 )",width: 1,type: "solid"},},splitLine: {lineStyle: {color: "rgba(255,255,255,.1)",}}}],series: [{type: 'bar',data: echartsData.yAxis,barWidth: '35%', //柱子宽度itemStyle: {normal: {color: '#2f89cf',opacity: 1,barBorderRadius: 5,}},tooltip: {valueFormatter: function (value) {return value + ' 个';}}}]};// 使用刚指定的配置项和数据显示图表。myChartEvent.setOption(option);window.addEventListener("resize", function () {myChartEvent?.resize();});
}
// 数据诊断统计图表初始化
const initDataCharts = () => {// prettier-ignore// timeData = timeData.map(function (str) {//   return str.replace('2009/', '');// });const option = {// title: {//   text: '温度',//   left: 'center'// },tooltip: {trigger: 'axis',axisPointer: {animation: false},formatter: function (params) {// console.log('params:', params);let result = params[0].name + ' 米' + '<br/>';for (let i = 0; i < params.length; i++) {result += params[i].marker + params[i].seriesName + ': ' + params[i].value + ' ℃<br/>';}return result;},},// legend: {//   data: ['温度'],//   left: 10// },// toolbox: {//   feature: {//     dataZoom: {//       yAxisIndex: 'none'//     },//     restore: {},//     saveAsImage: {}//   }// },axisPointer: {link: [{xAxisIndex: 'all'}]},dataZoom: [{show: true,realtime: true,textStyle: {color: '#fff' // 设置字体颜色为红色},start: 30,end: 70,xAxisIndex: [0, 1]},{type: 'inside',textStyle: {color: '#fff' // 设置字体颜色为红色},realtime: true,start: 30,end: 70,xAxisIndex: [0, 1]}],grid: [{left: 60,right: 50,height: '35%'},{left: 60,right: 50,top: '55%',height: '35%'}],xAxis: [{type: 'category',name: '米',nameTextStyle: {//y轴上方单位的颜色color: '#fff'},boundaryGap: false,axisLine: { onZero: true },data: timeXData,axisLabel: {// interval: 0,// rotate:50,show: true,// splitNumber: 15,textStyle: {color: "rgba(255,255,255,1)",fontSize: '12',},},}],yAxis: [{name: '温度(℃)',type: 'value',max: 60,nameTextStyle: {//y轴上方单位的颜色color: '#fff'},axisLabel: {//formatter: '{value} %'show: true,textStyle: {color: "rgba(255,255,255,1)",fontSize: '12',},}}],series: [{name: '温度',type: 'line',symbolSize: 8,// prettier-ignoredata: timeYData}]};nextTick(() => {// 基于准备好的dom,初始化echarts实例const myChartDom = document.getElementById('echartsIdData');if (myChartDom) {dataChartEvent?.dispose();}dataChartEvent = echarts.init(myChartDom);// 使用刚指定的配置项和数据显示图表。dataChartEvent.setOption(option);window.addEventListener("resize", function () {dataChartEvent?.resize();});})
}
// 告警系统面板
const openAlarmDialog = () => {// reSetStatus();isShowDataSystem.value = false;dataDialogVisible.value = false;isShowAlarmSystem.value = !isShowAlarmSystem.value;alarmDialogVisible.value = !alarmDialogVisible.value;isShowAlarmSystem.value ? isShowLeftMenu.value = false : isShowLeftMenu.value = true;// isShowLeftMenu.value = false;
}
// 数据诊断面板
const openDataDialog = () => {// reSetStatus();isShowAlarmSystem.value = false;alarmDialogVisible.value = false;isShowDataSystem.value = !isShowDataSystem.value;dataDialogVisible.value = !dataDialogVisible.value;isShowDataSystem.value ? isShowLeftMenu.value = false : isShowLeftMenu.value = true;// isShowLeftMenu.value = false;if (dataDialogVisible.value) {nextTick(() => {// 数据诊断统计图表初始化initDataCharts();})}
}
http://www.lryc.cn/news/621351.html

相关文章:

  • 新增和编辑共用弹窗模板
  • 深度解析 Vue 高阶技巧:提升工程化能力的实用方案
  • 机器人伴侣的智能升级:Deepoc具身智能模型如何重塑成人伴侣体验
  • AI驱动的智能爬虫架构与应用
  • C++中的链式操作原理与应用(三):专注于异步操作延的C++开源库 continuable
  • 开发避坑指南(26):Vue3 input输入框前置后 置元素解决方案
  • uniapp开发动态添加密码验证
  • 【力扣322】零钱兑换
  • C++ 排序指南
  • Kafka下载和安装
  • Ubuntu 22.04 远程桌面设置固定密码的方法
  • HQA-Attack: Toward High Quality Black-Box Hard-Label Adversarial Attack on Text
  • CoreShop商城框架开启多租户(3)
  • PyTorch 2025全解析:从基础到前沿,深度学习框架的技术演进与实战指南
  • ESP32入门开发·通用硬件定时器 (GPTimer)
  • C# 高并发处理方式
  • 算法题Day1
  • torchvision中数据集的使用与DataLoader 小土堆pytorch记录
  • # Vue 列表渲染详解
  • VLMs开发——基于Qwen2.5-VL 实现视觉语言模型在目标检测中的层级结构与实现方法
  • RxJava Android 创建操作符实战:从数据源到Observable
  • 中久数创——笔试题
  • PiscTrace基于YOLO追踪算法的物体速度检测系统详解
  • 2025.8.24复习总结
  • React.memo、useMemo 和 React.PureComponent的区别
  • 基于场景的无人驾驶叉车分类研究:应用场景与技术选型分析
  • springboot myabtis返回list对象集合,对象的一个属性为List对象
  • 飞算 JavaAI 真是 yyds
  • 一周学会Matplotlib3 Python 数据可视化-绘制面积图(Area)
  • [C++] Git 使用教程(从入门到常用操作)