/** * 高德地图点标记绘制相关 */ import { map, AMap } from './index' import { useToolboxStore } from '@/stores/toolbox' import util from './util' import * as echarts from 'echarts' import exceedIcon from '@/assets/exceed.png' import exceptionIcon from '@/assets/exception.png' import offlineIcon from '@/assets/offline.png' const toolboxStore = useToolboxStore() var _massMarks = undefined // 状态图标配置 const statusIcons = { exceed: exceedIcon, // 油烟浓度超标 exception: exceptionIcon, // 供电异常 offline: offlineIcon, // 设备或网络异常 online: createCustomMarkerOnline(), // 在线状态 offlineStatus: createCustomMarkerOffline(), // 离线状态 } /** * 绘制海量点标记 * @param {Array} shops 店铺对象数组 * @param {Object} shops[].shop 店铺基本信息 * @param {string} shops[].shop.name 店铺名称 * @param {string} shops[].shop.address 店铺地址 * @param {number} shops[].shop.latitude 纬度 * @param {number} shops[].shop.longitude 经度 * @param {string} shops[].shop.ringCodeLevel 最新环信码等级 * @param {string} shops[].shop.ringCodePublishTime 最新环信码发布时间 * @param {boolean} shops[].shop.isOnline 在线状态 * @param {number} shops[].shop.exceptionStatus 异常状态(0: 油烟浓度超标, 1: 供电异常, 2: 设备或网络异常, 3: 无异常) * @param {Array} shops[].recentData 近1小时的监测数据 * @param {string} shops[].recentData[].sampleTime 数据采样时间 * @param {number} shops[].recentData[].oilSmokeConcentration 油烟浓度 * @param {number} shops[].recentData[].purifierCurrent 净化器电流 * @param {number} shops[].recentData[].fanCurrent 风机电流 */ function drawMassMarks(shops) { // 配置样式 const massMarksStyle = [ { url: statusIcons.exceed, size: new AMap.Size(20, 20), anchor: new AMap.Pixel(10, 10), }, { url: statusIcons.exception, size: new AMap.Size(20, 20), anchor: new AMap.Pixel(10, 10), }, { url: statusIcons.offline, size: new AMap.Size(20, 20), anchor: new AMap.Pixel(10, 10), }, { url: statusIcons.online, size: new AMap.Size(32, 32), anchor: new AMap.Pixel(10, 10), }, { url: statusIcons.offlineStatus, size: new AMap.Size(32, 32), anchor: new AMap.Pixel(10, 10), }, ] // 准备海量点数据 const massMarksData = shops.map((shop, index) => { // 根据异常状态和在线状态获取样式索引 let styleIndex = 3 // 默认在线状态 if (shop.shop.exceptionStatus !== undefined) { switch (shop.shop.exceptionStatus) { case 0: // 油烟浓度超标 styleIndex = 0 break case 1: // 供电异常 styleIndex = 1 break case 2: // 设备或网络异常 styleIndex = 2 break case 3: // 无异常,根据在线状态决定 styleIndex = shop.shop.isOnline ? 3 : 4 break default: styleIndex = shop.shop.isOnline ? 3 : 4 } } else { // 没有异常状态时,根据在线状态决定 styleIndex = shop.shop.isOnline ? 3 : 4 } return { id: index, name: shop.shop.name, lnglat: [shop.shop.longitude, shop.shop.latitude], style: styleIndex, // 样式索引,对应 massMarksStyle extData: shop, // 存储完整的店铺信息 } }) // 清除现有的海量点标记 if (window.massMarks) { window.massMarks.clear() } // 创建新的海量点标记 window.massMarks = new AMap.MassMarks(massMarksData, { zIndex: 111, cursor: 'pointer', style: massMarksStyle, }) // 添加点击事件 window.massMarks.on('click', function (e) { const shop = e.data.extData showShopInfoWindow(shop) }) var marker = new AMap.Marker({ content: ' ', map: map, offset: new AMap.Pixel(13, 12), }) var timeout window.massMarks.on('mouseover', (e) => { if (timeout) { clearTimeout(timeout) } marker.setPosition(e.data.lnglat) marker.setLabel({ content: e.data.name }) map.add(marker) timeout = setTimeout(() => { map.remove(marker) }, 2000) }) // 添加到地图 window.massMarks.setMap(map) util.setBound(massMarksData.map((item) => item.lnglat)) } /** * 根据环信码等级获取颜色 * @param {string} level 环信码等级 * @returns {string} 颜色值 */ function getColorByRingCodeLevel(level) { switch (level + '') { case '0': return '#52c41a' // 绿色 case '1': return '#faad14' // 黄色 case '2': return '#f5222d' // 红色 default: return '#8c8c8c' // 灰色 } } /** * 根据环信码等级获取中文 * @param {string} level 环信码等级 * @returns {string} 中文表示 */ function getRingCodeLevelText(level) { switch (level + '') { case '0': return '绿色' case '1': return '黄色' case '2': return '红色' default: return '未知' } } /** * 根据异常状态获取中文 * @param {number} status 异常状态 * @returns {string} 中文表示 */ function getExceptionStatusText(status) { switch (status + '') { case '0': return '油烟浓度超标' case '1': return '供电异常' case '2': return '设备或网络异常' case '3': return '无异常' default: return '未知状态' } } /** * 根据异常状态获取颜色 * @param {number} status 异常状态 * @returns {string} 颜色值 */ function getColorByExceptionStatus(status) { switch (status + '') { case '0': return '#f5222d' // 油烟浓度超标 - 红色 case '1': return '#faad14' // 供电异常 - 黄色 case '2': return '#8c8c8c' // 设备或网络异常 - 灰色 case '3': return '#52c41a' // 无异常 - 绿色 default: return '#8c8c8c' // 灰色 } } /** * 创建自定义标记 * @param {string} color 标记颜色 * @returns {string} 标记的SVG URL */ function createCustomMarker(color) { const svg = ` ` return 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg))) } /** * 创建在线状态的SVG标记(油烟监测设备形象) * @returns {string} 标记的SVG URL */ function createCustomMarkerOnline() { const svg = ` ` return 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg))) } /** * 创建离线状态的SVG标记(油烟监测设备形象) * @returns {string} 标记的SVG URL */ function createCustomMarkerOffline() { const svg = ` ` return 'data:image/svg+xml;base64,' + btoa(unescape(encodeURIComponent(svg))) } /** * 显示店铺信息窗口 * @param {Object} shop 店铺对象 */ function showShopInfoWindow(shop) { // 准备信息窗口内容 // 获取在线状态文本 const onlineStatusText = shop.shop.isOnline ? '在线' : '离线' // 获取异常状态文本 const exceptionStatusText = getExceptionStatusText(shop.shop.exceptionStatus) // 获取异常状态颜色 const exceptionStatusColor = getColorByExceptionStatus(shop.shop.exceptionStatus) // 根据状态获取对应的图标 let statusIcon = statusIcons.online // 默认在线图标 if (shop.shop.exceptionStatus !== undefined) { switch (shop.shop.exceptionStatus) { case 0: statusIcon = statusIcons.exceed break case 1: statusIcon = statusIcons.exception break case 2: statusIcon = statusIcons.offline break case 3: statusIcon = shop.shop.isOnline ? statusIcons.online : statusIcons.offlineStatus break default: statusIcon = shop.shop.isOnline ? statusIcons.online : statusIcons.offlineStatus } } else { statusIcon = shop.shop.isOnline ? statusIcons.online : statusIcons.offlineStatus } // 根据在线状态获取图标 const onlineIcon = shop.shop.isOnline ? statusIcons.online : statusIcons.offlineStatus // 根据异常状态获取图标 let exceptionIcon = statusIcons.online // 默认在线图标 if (shop.shop.exceptionStatus !== undefined) { switch (shop.shop.exceptionStatus) { case 0: exceptionIcon = statusIcons.exceed break case 1: exceptionIcon = statusIcons.exception break case 2: exceptionIcon = statusIcons.offline break case 3: exceptionIcon = statusIcons.online break default: exceptionIcon = statusIcons.online } } const content = `

${shop.shop.name}

地址:${shop.shop.address}

在线状态:${onlineStatusText} 异常状态:${exceptionStatusText}
环信码等级:${getRingCodeLevelText(shop.shop.ringCodeLevel)} 发布时间:${shop.shop.ringCodePublishTime}

近1小时监测数据

` // 清除现有的信息窗口 if (window.infoWindow) { window.infoWindow.close() } // 创建新的信息窗口 window.infoWindow = new AMap.InfoWindow({ content: content, size: new AMap.Size(400, 400), offset: new AMap.Pixel(-24, -80), }) // 打开信息窗口 window.infoWindow.open(map, [shop.shop.longitude, shop.shop.latitude]) // 等待信息窗口加载完成后初始化图表 setTimeout(() => { const chartdom = document.getElementById('infowindowChartContainer') if (chartdom) { initChart(chartdom, shop.recentData) } }, 100) } /** * 初始化监测数据图表 * @param {Array} data 监测数据 */ function initChart(chartdom, data) { // 准备图表数据 const times = data.map((item) => item.sampleTime) const oilSmokeData = data.map((item) => item.oilSmokeConcentration) const purifierData = data.map((item) => item.purifierCurrent) const fanData = data.map((item) => item.fanCurrent) // 初始化图表 const chart = echarts.init(chartdom) // 图表配置 const option = { tooltip: { trigger: 'axis', axisPointer: { type: 'cross', label: { backgroundColor: '#6a7985', }, }, }, legend: { data: ['油烟浓度', '净化器电流', '风机电流'], top: 0, }, grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true, }, xAxis: [ { type: 'category', boundaryGap: false, data: times.map((time) => time.split(' ')[1]), axisLabel: { rotate: 0, fontSize: 10, }, }, ], yAxis: [ { type: 'value', name: '油烟浓度 (mg/m³)', position: 'left', left: '30%', }, { type: 'value', name: '电流 (A)', position: 'right', }, ], series: [ { name: '油烟浓度', type: 'line', data: oilSmokeData, yAxisIndex: 0, smooth: true, }, { name: '净化器电流', type: 'line', data: purifierData, yAxisIndex: 1, smooth: true, }, { name: '风机电流', type: 'line', data: fanData, yAxisIndex: 1, smooth: true, }, ], } // 应用配置 chart.setOption(option) // 响应式处理 window.addEventListener('resize', function () { chart.resize() }) } /** * 清除海量点标记 */ function clearMassMarks() { if (window.massMarks) { window.massMarks.clear() window.massMarks.setMap(null) window.massMarks = null } if (window.infoWindow) { window.infoWindow.close() window.infoWindow = null } } export default { drawMassMarks, clearMassMarks, // /** // * 绘制海量点标记 // * @param fDatas 完整监测数据 // * @param _factor 当前展示的监测因子对象 // */ // drawMassMarks(fDatas, _factor, onClick) { // if (!toolboxStore.dataMarkerStatus) { // return; // } // this.clearMassMarks(); // const lnglats = fDatas.lnglats_GD; // var data = []; // for (let i = 0; i < lnglats.length; i++) { // data.push({ // lnglat: lnglats[i], //点标记位置 // name: `${fDatas.times[i]}
${_factor.factorName}: ${_factor.datas[i].factorData} μg/m³`, // id: i // }); // } // // 创建样式对象 // var styleObject = { // url: 'https://a.amap.com/jsapi_demos/static/images/mass1.png', // // url: './asset/mipmap/ic_up_white.png', // 图标地址 // size: new AMap.Size(11, 11), // 图标大小 // anchor: new AMap.Pixel(5, 5) // 图标显示位置偏移量,基准点为图标左上角 // }; // var massMarks = new AMap.MassMarks(data, { // zIndex: 5, // 海量点图层叠加的顺序 // zooms: [15, 18], // 在指定地图缩放级别范围内展示海量点图层 // style: styleObject // 设置样式对象 // }); // massMarks.on('click', (event) => { // const i = event.data.id; // // 3. 自定义点击事件 // onClick(i); // }); // var marker = new AMap.Marker({ // content: ' ', // map: map, // offset: new AMap.Pixel(13, 12) // }); // var timeout; // massMarks.on('mouseover', (e) => { // if (timeout) { // clearTimeout(timeout); // } // marker.setPosition(e.data.lnglat); // marker.setLabel({ content: e.data.name }); // map.add(marker); // timeout = setTimeout(() => { // map.remove(marker); // }, 2000); // }); // _massMarks = massMarks; // map.add(massMarks); // }, // clearMassMarks() { // if (_massMarks) { // map.remove(_massMarks) // _massMarks = undefined // } // }, /** * 创建标记点 * @param {string | Array} img 图标或图标数组 * @param {Array} dataList 监测数据 * @param {boolean} collision 标注避让 * @returns */ createLabelMarks(img, dataList, collision = true, showTxt = true) { const layer = new AMap.LabelsLayer({ zooms: [3, 20], zIndex: 1000, // 开启标注避让,默认为开启,v1.4.15 新增属性 collision: collision, // 开启标注淡入动画,默认为开启,v1.4.15 新增属性 animation: true, }) map.add(layer) // var markers = []; for (var i = 0; i < dataList.length; i++) { const data = dataList[i] var curData = { name: data.name, position: [data.longitude, data.latitude], zooms: [10, 20], opacity: 1, zIndex: 10, icon: { type: 'image', image: typeof img === 'string' ? img : img[i], // clipOrigin: [14, 92], // clipSize: [50, 68], size: [30, 30], anchor: 'bottom-center', angel: 0, retina: true, }, text: { content: showTxt ? data.name : '', direction: 'top', offset: [0, -5], style: { fontSize: 16, fontWeight: 'normal', fillColor: '#fff', strokeColor: '#333', strokeWidth: 0, backgroundColor: '#122b54a9', }, }, } curData.extData = { index: i, } var labelMarker = new AMap.LabelMarker(curData) // markers.push(labelMarker); layer.add(labelMarker) } return layer }, createMarker({ position, img, title, content, label = '', extData }) { //创建 AMap.Icon 实例: const icon = new AMap.Icon({ size: new AMap.Size(30, 30), //图标尺寸 image: img, //Icon 的图像 // imageOffset: new AMap.Pixel(-9, -3), //图像相对展示区域的偏移量,适于雪碧图等 imageSize: new AMap.Size(30, 30), //根据所设置的大小拉伸或压缩图片 }) const marker = new AMap.Marker({ position: position, // offset: new AMap.Pixel(-13, -30), icon: icon, //添加 icon 图标 URL content: content, title: title, label: { content: label, direction: 'bottom', }, extData, }) // map.add(marker); return marker }, }