import { reactive } from 'vue'; import calculate from '@/utils/map/calculate'; import gridMapUtil from '@/utils/map/grid'; import { map, onMapMounted } from '@/utils/map/index_old'; import { getGridDataDetailFactorValue } from '@/model/GridDataDetail'; import { useGridStore } from '@/stores/grid-info'; const gridStore = useGridStore(); /** * 卫星遥测网格管理 */ export class SatelliteGrid { constructor(name) { this.name = name; } gridGroupId; // 默认地图网格相关对象 mapViews; gridState; gridDataDetail; // 地图网格对象Map结构,存储对应key下的网格对象、网格坐标信息 mapViewsMap = new Map(); // 地图网格对象的参数状态 gridStateMap = new Map(); // 网格数据Map结构,存储对应key下的网格监测数据信息 gridDataDetailMap = new Map(); districtPolygon; firstEvent; events = new Map(); selectedFactorType; setShowFactorType(e) { this.selectedFactorType = e; this.changeGridColor({ factorName: e.name }); this.changeText({ factorName: e.name }); } // 绘制区县边界 drawDistrict(districtName, isNew) { onMapMounted(() => { if (this.districtPolygon && !isNew) { map.remove(this.districtPolygon); map.add(this.districtPolygon); } else { // eslint-disable-next-line no-undef var district = new AMap.DistrictSearch({ extensions: 'all', //返回行政区边界坐标等具体信息 level: 'district' //设置查询行政区级别为区 }); district.search(districtName, (status, result) => { var bounds = result.districtList[0].boundaries; //获取朝阳区的边界信息 if (bounds) { for (var i = 0; i < bounds.length; i++) { //生成行政区划 polygon // eslint-disable-next-line no-undef this.districtPolygon = new AMap.Polygon({ map: map, //显示该覆盖物的地图对象 strokeWeight: 2, //轮廓线宽度 path: bounds[i], //多边形轮廓线的节点坐标数组 fillOpacity: 0, //多边形填充透明度 fillColor: '#CCF3FF', //多边形填充颜色 // strokeColor: '#ffffff' //线条颜色 strokeColor: '#0552f7', //线条颜色 zIndex: 9 }); } map.setFitView(); //将覆盖物调整到合适视野 } }); } }); } clearAll(mapViews) { if (mapViews) { if (typeof mapViews.gridViews === 'object') { map.remove(mapViews.gridViews); } } this.clearText(mapViews); } clearText(mapViews) { if (mapViews) { if (typeof mapViews.dataTxt === 'object') { map.remove(mapViews.dataTxt); } if (typeof mapViews.dataLayer === 'object') { map.remove(mapViews.dataLayer); } if (typeof mapViews.rankTxt === 'object') { map.remove(mapViews.rankTxt); } if (typeof mapViews.rankLayer === 'object') { map.remove(mapViews.rankLayer); } } } // 绘制网格线 drawPolyline(gridInfo, event) { this.firstEvent = event; // 绘制网格 const points = gridInfo.map((v) => { return calculate.wgs84_To_Gcj02(v.longitude, v.latitude); // return [v.longitude, v.latitude]; }); // const gridPoints = gridMapUtil.parseGridPoint(points); // console.log('gridPoints:', gridPoints); const gridPoints = gridInfo.map((v, i) => { return { path: [ calculate.wgs84_To_Gcj02(v.point1Lon, v.point1Lat), calculate.wgs84_To_Gcj02(v.point2Lon, v.point2Lat), calculate.wgs84_To_Gcj02(v.point3Lon, v.point3Lat), calculate.wgs84_To_Gcj02(v.point4Lon, v.point4Lat) // [v.point1Lon, v.point1Lat], // [v.point2Lon, v.point2Lat], // [v.point3Lon, v.point3Lat], // [v.point4Lon, v.point4Lat] ] // eslint-disable-next-line no-undef .map((d) => new AMap.LngLat(d[0], d[1])), extData: { centerPoint: points[i], // gridPoints, gridCell: v } }; }); const gridViews = gridMapUtil.drawPolylines({ points: gridPoints, event }); return { gridViews, gridPoints, points }; } // 绘制监测数据值 drawDataText(points, gridDataDetail, textViews, isCustomColor, useColor) { const data = gridDataDetail.map((v, i) => { return { lnglat_GD: points[i], data: getGridDataDetailFactorValue(v, this.selectedFactorType.name) }; }); // return gridMapUtil.drawGridTextLabel(data, textViews, labelsLayer, 'bottom'); return gridMapUtil.drawGridText({ points: data, textViews, anchor: 'top-center', type: 'data', isCustomColor, useColor, factorName: this.selectedFactorType.name }); } changeText({ tags = [], factorName, isCustomColor, useColor, type }) { let { _mapViewsList, _gridDataDetailList } = this._getMapViews(...tags); if (_mapViewsList.length == _gridDataDetailList.length) { _mapViewsList.forEach((v, i) => { if (v.dataTxt) { let dataList = []; if (type == 'data' || type == undefined) { dataList.push({ type: 'data', data: _gridDataDetailList[i].map((v) => { return { data: getGridDataDetailFactorValue( v, factorName ? factorName : this.selectedFactorType.name ) }; }) }); } if (type == 'rank' || type == undefined) { dataList.push({ type: 'rank', data: _gridDataDetailList[i].map((v) => { return { data: v.rank ? v.rank : '' }; }) }); } dataList.forEach((d) => { gridMapUtil.changeGridText({ points: d.data, textViews: d.type == 'data' ? v.dataTxt : v.rankTxt, type: d.type, isCustomColor, useColor, factorName: factorName ? factorName : this.selectedFactorType.name }); }); } }); } } // 绘制监测数据排名文本 drawRankText(points, gridDataDetail, textViews, labelsLayer) { const data = gridDataDetail.map((v, i) => { return { lnglat_GD: points[i], data: v.rank ? v.rank : '' }; }); // return gridMapUtil.drawGridTextLabel(data, textViews, labelsLayer, 'top'); return gridMapUtil.drawGridText({ points: data, textViews, anchor: 'bottom-center', type: 'rank', factorName: this.selectedFactorType.name }); } // 绘制监测数据值对应网格颜色 drawColor({ gridViews, points, gridDataDetail, lastGridViews, customColor, style }) { // 根据数据筛选有数据的网格 const res = []; // 以及对应的中心点坐标 const pointsRes = []; // 网格按照其编号升序排列,然后计算编号和下表的偏差值 const offset = gridViews[0].getExtData().gridCell.cellIndex - 0; gridDataDetail.forEach((d) => { // 根据数据关联的网格编号,找到对应网格 const cellId = d.cellId; if (cellId > gridViews.length) { throw Error( '遥测数据的网格索引编号超出网格组范围,数据和网格组可能不对应' ); } res.push(gridViews[cellId - offset]); pointsRes.push(points[cellId - offset]); }); // 根据绘制颜色方式绘制网格 let resGridViews; if (customColor) { resGridViews = gridMapUtil.drawGridColorCustom( res, gridDataDetail, this.selectedFactorType.name ); } else { resGridViews = gridMapUtil.drawGridColor( res, gridDataDetail, this.selectedFactorType.name, style ); } if (lastGridViews) { map.remove(lastGridViews); } map.add(resGridViews); map.setFitView(resGridViews); return { resGridViews, pointsRes }; } // 卫星网格配置准备 gridPrepare(gridInfo, event) { // clearText(mapViews); this.clearAll(this.mapViews); this.mapViews = this.drawPolyline(gridInfo, event); } // 绘制网格遥感数据值和网格颜色 drawGrid({ mapViews, gridState, data, grid = { useCustomColor: false, style: { opacity: 1, zIndex: 11, isMixGridHighlight: false } }, dataTxt = { isShow: false, useCustomColor: false, useColor: false }, rankTxt = { isShow: false } }) { // 确定绘制的网格组及其参数对象 const _mapViews = mapViews ? mapViews : this.mapViews; const _gridState = gridState ? gridState : this.gridState; // 根据网格数据绘制对应的网格 const { resGridViews, pointsRes } = this.drawColor({ gridViews: _mapViews.gridViews, points: _mapViews.points, gridDataDetail: data, lastGridViews: _mapViews.lastGridViews, customColor: grid.useCustomColor, style: grid.style }); // 保存地图网格图形对象和经纬度对象 _mapViews.lastGridViews = resGridViews; _mapViews.lastPoints = pointsRes; // 更新网格状态参数 _gridState.showGrid = true; _gridState.showGridCustomColor = false; _gridState.gridOpacityValue = grid.style.opacity; _gridState.gridZIndex = grid.style.zIndex; _gridState.highlightFusionGrid = grid.style.isMixGridHighlight; // 绘制数据文本 const { textViews: dtv } = this.drawDataText( _mapViews.lastPoints, data, _mapViews.dataTxt, dataTxt.useCustomColor, dataTxt.useColor ); _mapViews.dataTxt = dtv; // 更新数据文本状态参数 _gridState.showData = dataTxt.isShow; _gridState.showDataColor = dataTxt.useColor; _gridState.showDataCustomColor = dataTxt.useCustomColor; // 绘制排名文本 const { textViews: rtv } = this.drawRankText( _mapViews.lastPoints, data, _mapViews.rankTxt, _mapViews.rankLayer ); _mapViews.rankTxt = rtv; // 更新排名文本状态参数 _gridState.showRank = rankTxt.isShow; if (dataTxt.isShow) { map.add(_mapViews.dataTxt); } if (rankTxt.isShow) { map.add(_mapViews.rankTxt); } } /** * 根据tag标签,展示已有的网格或绘制一组新的网格 * @param {Object} param0 { * tag, 标签 * gridDataDetail, 网格数据数组 * useCustomColor, 是否使用对比色 * opacity, 透明度,取值[0, 1] * zIndex, 地图显示层级 * showDataTxt, 是否显示数据文本 * showRankTxt, 是否显示排名文本 * extData, 自定义额外信息对象 * } */ drawTagGrid({ tag, data, grid, dataTxt, rankTxt, extData }) { if (!this.mapViewsMap.has(tag)) { const newMapViews = this._createNewMapViews({ extData }); const newGridState = this._createNewGridState({ extData }); this.mapViewsMap.set(tag, newMapViews); map.on('zoomend', newMapViews.mapZoomEvent); this.gridStateMap.set(tag, newGridState); this.gridDataDetailMap.set(tag, data); } const _mapViews = this.mapViewsMap.get(tag); const _gridState = this.gridStateMap.get(tag); this.drawGrid({ mapViews: _mapViews, gridState: _gridState, data, grid, dataTxt, rankTxt }); } /** * 根据标签删除对应的网格,同时移除地图缩放事件等 * @param {*} tags 网格标签数组 */ deleteTagGrid(tags) { this.changeVisibility({ tags, showGridViews: false, showDataTxt: false, showRankTxt: false }); tags.forEach((t) => { const { mapZoomEvent } = this.mapViewsMap.get(t); map.off('zoomend', mapZoomEvent); this.mapViewsMap.delete(t); this.gridDataDetailMap.delete(t); this.gridStateMap.delete(t); }); } // 调整各类地图覆盖物的可见性 changeVisibility({ tags = [], showGridViews, showDataTxt, showRankTxt }) { let { _mapViewsList, _gridStateList } = this._getMapViews(...tags); if (showGridViews != undefined) { if (showGridViews) { _mapViewsList.forEach((v, i) => { if (v.lastGridViews) { map.add(v.lastGridViews); _gridStateList[i].showGrid = true; } }); } else { _mapViewsList.forEach((v, i) => { if (v.lastGridViews) { map.remove(v.lastGridViews); _gridStateList[i].showGrid = false; _gridStateList[i].showHeatMap = false; } }); } } if (showDataTxt != undefined) { if (showDataTxt) { _mapViewsList.forEach((v, i) => { if (v.dataTxt) { map.add(v.dataTxt); _gridStateList[i].showData = true; } }); } else { _mapViewsList.forEach((v, i) => { if (v.dataTxt) { map.remove(v.dataTxt); _gridStateList[i].showData = false; } }); } } if (showRankTxt != undefined) { if (showRankTxt) { _mapViewsList.forEach((v, i) => { if (v.rankTxt) { map.add(v.rankTxt); _gridStateList[i].showRank = true; } }); } else { _mapViewsList.forEach((v, i) => { if (v.rankTxt) { map.remove(v.rankTxt); _gridStateList[i].showRank = false; } }); } } } changeGridOpacity({ tag, isOpacity, opacityValue }) { let { _mapViewsList, _gridStateList } = tag ? this._getMapViews(tag) : this._getMapViews(); _mapViewsList.forEach((v, i) => { if (v.lastGridViews) { v.lastGridViews.forEach((e) => { e.setOptions({ fillOpacity: typeof opacityValue === 'number' ? opacityValue : isOpacity ? 0.1 : 0.7 }); }); _gridStateList[i].gridOpacityValue = opacityValue; } }); } changeGridColor({ tags = [], factorName, useCustomColor, opacity, zIndex, isMixGridHighlight }) { let { _mapViewsList, _gridDataDetailList, _gridStateList } = this._getMapViews(...tags); if (_mapViewsList.length == _gridDataDetailList.length) { _mapViewsList.forEach((v, i) => { if (v.lastGridViews) { const lastGridDataDetail = _gridDataDetailList[i]; const _gridState = _gridStateList[i]; if (useCustomColor != undefined) _gridState.showGridCustomColor = useCustomColor; if (useCustomColor) { gridMapUtil.drawGridColorCustom( v.lastGridViews, lastGridDataDetail, factorName ? factorName : this.selectedFactorType.name ); } else { if (opacity != undefined) _gridState.gridOpacityValue = opacity; if (zIndex != undefined) _gridState.gridZIndex = zIndex; if (isMixGridHighlight != undefined) _gridState.highlightFusionGrid = isMixGridHighlight; gridMapUtil.drawGridColor( v.lastGridViews, lastGridDataDetail, factorName ? factorName : this.selectedFactorType.name, { opacity, zIndex, isMixGridHighlight } ); } } }); } } setGridEvent(tags, name, event) { const { _mapViewsList, _gridDataDetailList } = this._getMapViews(...tags); if (!this.events.has(name)) { this.events.set(name, []); } const list = this.events.get(name); if (list.length > 0) { const lastEvent = list[list.length - 1]; _mapViewsList.forEach((v) => { v.gridViews.forEach((polygon) => { polygon.off(name, lastEvent); }); }); } this.events.get(name).push(event); _mapViewsList.forEach((v, i) => { const gridDataDetailList = _gridDataDetailList[i]; v.gridViews.forEach((polygon) => { const { gridCell } = polygon.getExtData(); const cellIndex = gridCell.cellIndex; const gridDataDetail = gridDataDetailList.find( (v) => v.cellId == cellIndex ); polygon.on(name, (e) => { event({ gridCell, gridDataDetail, polygon, extData: v.extData }); }); }); }); } setDefaultGridClickEvent(tags) { this.setGridEvent( tags, 'click', ({ gridCell, gridDataDetail, extData }) => { gridStore.selectedGridCellAndDataDetail = { gridCell, gridDataDetail, extData }; } ); //鼠标移入事件 this.setGridEvent(tags, 'mouseover', ({ polygon }) => { const ext = polygon.getExtData(); const originOption = polygon.getOptions(); ext.originOption = originOption; polygon.setOptions({ strokeWeight: 2, strokeColor: 'red' }); polygon.setExtData(ext); }); //鼠标移出事件 this.setGridEvent(tags, 'mouseout', ({ polygon }) => { const ext = polygon.getExtData(); polygon.setOptions(ext.originOption); }); } goBackGridEvent(name) { if (this.events.has(name)) { const eventList = this.events.get(name); //先移除原有的事件 const lastEvent = eventList.pop(); this.mapViews.gridViews.forEach((polygon) => { polygon.off(name, lastEvent); }); //获取上一个事件 const event = eventList.pop(); this.mapViews.gridViews.forEach((polygon) => { polygon.on(name, event); }); } } /** * 将多组网格进行融合 * 重叠的网格进行监测数据均值计算并重新计算对应颜色,形成新的一组融合网格 * @param {Array} tags 需要融合的网格标签,当为空时,默认融合所有网格 */ mixGrid2({ tags, isMixGridHighlight = true, gridDataDetailList }) { tags.sort((a, b) => { return a < b ? -1 : 1; }); const mixTag = tags.join('-'); if (this.mapViewsMap.has(mixTag)) { this.changeVisibility({ tags: [mixTag], showGridViews: true }); this.changeGridColor({ tags: [mixTag], isMixGridHighlight }); } else { gridDataDetailList.forEach((gdd) => { // 网格数据是融合的,展示高亮的样式 if (gdd.mixData) { gdd.gridStyle = { strokeWeight: 2, strokeColor: '#23dad1' }; } }); this.drawTagGrid({ tag: mixTag, data: gridDataDetailList, grid: { style: { isMixGridHighlight } }, extData: { name: `走航融合 - ${mixTag}`, type: 1 } }); } return mixTag; } drawHeatGrid2(tag, headGridDataDetailList) { const heatTag = `heat-${tag}`; if (this.gridStateMap.has(tag)) { this.gridStateMap.get(tag).showHeatMap = true; } if (this.mapViewsMap.has(heatTag)) { this.changeVisibility({ tags: [heatTag], showGridViews: true }); } else { this.drawTagGrid({ tag: heatTag, data: headGridDataDetailList, extData: { name: `走航热力图 - ${heatTag}`, type: 2 } }); } return heatTag; } search(gdd, width, height, eachwidth, eachheight, searchLength) { function getCellWidthRange(cellId, width, height) { const total = width * height; const x = Math.ceil(cellId / total) - 1; let first = 1 + x * total, last = width + x * total; let scale = 0; while (scale < height) { const min = first + scale * width; const max = last + scale * width; if (cellId >= min && cellId <= max) { return [min, max]; } scale++; } } const cellId = gdd.cellId; // const minData = gdd.pm25 / 2 const dataOffset = (gdd.pm25 - gdd.pm25 / 2) / 3; const hOffset = eachwidth; const wOffset = 1; const cellIdMin = 1; const cellIdMax = width * height; let searchWidth = 0 - searchLength, searchHeight = 0 - searchLength; const result = []; const eachRange = getCellWidthRange(cellId, eachwidth, eachheight); const groupRange = getCellWidthRange( Math.ceil(cellId / (eachwidth * eachheight)), width / eachwidth, height / eachheight ); for (let w = searchWidth; w <= searchLength; w++) { // 先进行横向的坐标变换 let _cellId = cellId + w * wOffset; if (_cellId < eachRange[0] || _cellId > eachRange[1]) { const cellOffset = _cellId < eachRange[0] ? _cellId - eachRange[0] : _cellId - eachRange[1]; const groupOffset = Math[cellOffset / eachwidth > 0 ? 'ceil' : 'floor']( cellOffset / eachwidth ); const newEachRange = eachRange.map( (r) => r + groupOffset * eachwidth * eachheight ); _cellId = groupOffset > 0 ? newEachRange[0] + cellOffset - wOffset : newEachRange[1] + cellOffset + wOffset; const _groupId = Math.ceil(_cellId / (eachwidth * eachheight)); if (_groupId < groupRange[0] || _groupId > groupRange[1]) { continue; } } for (let h = searchHeight; h <= searchLength; h++) { if (w == 0 && h == 0) continue; const _eachRange = getCellWidthRange(_cellId, eachwidth, eachheight); // if (_eachRange == undefined) { // console.log(); // } const wOffset = _cellId - _eachRange[0]; let _resCellId = _cellId + h * hOffset; if (_resCellId < cellIdMin || _resCellId > cellIdMax) continue; const total = eachwidth * eachheight; const x = Math.ceil(_cellId / total) - 1; const eachCellIdMin = 1 + x * total; const eachCellIdMax = total + x * total; const topCell = eachCellIdMin + wOffset; const bottomCell = eachCellIdMax - eachwidth + 1 + wOffset; if (_resCellId < eachCellIdMin || _resCellId > eachCellIdMax) { const cellOffset = _resCellId < eachCellIdMin ? _resCellId - topCell : _resCellId - bottomCell; const newTopCell = cellOffset > 0 ? topCell + width * eachheight : topCell - width * eachheight; const newBottomCell = cellOffset > 0 ? bottomCell + width * eachheight : bottomCell - width * eachheight; _resCellId = cellOffset > 0 ? newTopCell + cellOffset - hOffset : newBottomCell + cellOffset + hOffset; } result.push({ groupId: gdd.groupId, cellId: _resCellId, pm25: gdd.pm25 - Math.max(Math.abs(w), Math.abs(h)) * dataOffset }); } } return result; } _getMapViews(...tags) { let _mapViewsList = [], _gridDataDetailList = [], _gridStateList = []; if (tags.length > 0) { tags.forEach((tag) => { if (this.mapViewsMap.has(tag) && this.gridDataDetailMap.has(tag)) { _mapViewsList.push(this.mapViewsMap.get(tag)); _gridDataDetailList.push(this.gridDataDetailMap.get(tag)); _gridStateList.push(this.gridStateMap.get(tag)); } }); } else { this.mapViewsMap.forEach((v) => { _mapViewsList.push(v); }); this.gridDataDetailMap.forEach((v) => { _gridDataDetailList.push(v); }); this.gridStateMap.forEach((v) => { _gridStateList.push(v); }); if (this.mapViews && this.gridDataDetail && this.gridState) { _mapViewsList.push(this.mapViews); _gridDataDetailList.push(this.gridDataDetail); _gridStateList.push(this.gridState); } } return { _mapViewsList, _gridDataDetailList, _gridStateList }; } _createNewMapViews({ tag, extData }) { return { gridViews: gridMapUtil.drawPolylines({ points: this.mapViews.gridPoints, event: this.firstEvent }), gridPoints: JSON.parse(JSON.stringify(this.mapViews.gridPoints)), points: JSON.parse(JSON.stringify(this.mapViews.points)), extData, show: true, mapZoomEvent: () => { const zoomLevel = map.getZoom(); if (zoomLevel >= 16.5) { this.changeGridOpacity({ tag, opacityValue: 0.1 }); } else { this.changeGridOpacity({ tag, opacityValue: 1 }); } // console.log('mapZoomEvent', map.getZoom()); } }; } _createNewGridState({ extData }) { return { // 网格名称 name: extData.name, // 网格类型,0:走航网格;1:走航融合网格;2:走航热力图 type: extData.type, // 网格相关参数 showGrid: true, // 是否显示网格 showGridCustomColor: false, // 是否显示对比色 gridOpacityValue: 1, // 网格透明度 gridZIndex: 11, // 网格z轴高度 // 排名文本相关参数 showRank: false, // 排名文本是否显示 // 数据文本相关参数 showData: false, // 数据文本是否显示 showDataColor: false, // 数据文本是否使用对应监测因子的标准颜色 showDataCustomColor: false, // 数据文本是否使用对应监测因子的对比颜色 // 是否显示对应热力图 showHeatMap: false, // 如果是融合网格,是否高亮显示重叠网格 highlightFusionGrid: false, // 如果是普通的走航网格,是否显示对应的走航轨迹 showUnderway: false, // 可能的自定义额外数据 extData }; } }