From 7f6661cca40e3530111d628222fa25462022ec78 Mon Sep 17 00:00:00 2001 From: riku <risaku@163.com> Date: 星期四, 04 九月 2025 18:25:36 +0800 Subject: [PATCH] 2025.9.4 --- src/api/dataAnalysisApi.js | 8 public/underway_season_report.docx | 0 src/utils/map/calculate.js | 37 +++ src/utils/chart/chart-map.js | 200 ++++++++++++++++--- src/views/historymode/component/MissionReport.vue | 311 +++++++++++++++++++++++++----- 5 files changed, 464 insertions(+), 92 deletions(-) diff --git a/public/underway_season_report.docx b/public/underway_season_report.docx index 95ff1f6..a29e88d 100644 --- a/public/underway_season_report.docx +++ b/public/underway_season_report.docx Binary files differ diff --git a/src/api/dataAnalysisApi.js b/src/api/dataAnalysisApi.js index 0035d5c..25a2c97 100644 --- a/src/api/dataAnalysisApi.js +++ b/src/api/dataAnalysisApi.js @@ -76,5 +76,13 @@ params: { startTime, endTime } }) .then((res) => res.data); + }, + + fetchGridFusion({ startTime, endTime, area, factorTypes }) { + return $http + .post(`air/analysis/report/gridFusion`, area, { + params: { startTime, endTime, factorTypes: factorTypes.join(',') } + }) + .then((res) => res.data); } }; diff --git a/src/utils/chart/chart-map.js b/src/utils/chart/chart-map.js index cb40c75..fb72feb 100644 --- a/src/utils/chart/chart-map.js +++ b/src/utils/chart/chart-map.js @@ -2,6 +2,12 @@ // import * as shanghaiMap from 'echarts-china-cities-js/echarts-china-cities-js/shanghai.js'; // 淇鍦板浘鏁版嵁鏂囦欢璺緞 // import 'echarts-china-cities-js/echarts-china-cities-js/shanghai.js'; // 淇鍦板浘鏁版嵁鏂囦欢璺緞 import { shanghai, jingan } from '@/utils/chart/shanghai.js'; // 纭繚璺緞姝g‘ +import calculate from '@/utils/map/calculate.js'; + +// 鍋忕Щ绾害锛屽搴旂害100绫� +const OFFSET_LAT = 0.00082; +// 鍋忕Щ绾害锛屽搴旂害100绫� +const OFFSET_LNG = 0.0009; /** * 璁$畻鍦板浘涓績鐐瑰拰缂╂斁姣斾緥 @@ -9,40 +15,47 @@ * @returns */ function calCenterPointAndZoom(gridData) { - let centerLng = 0; - let centerLat = 0; + const coordinates = []; let maxLng = -180; let minLng = 180; let maxLat = -90; let minLat = 90; - let zoom = 100; + let zoom = 1; gridData.forEach((g) => { - centerLng += g.centerLng; - centerLat += g.centerLat; - maxLng = Math.max(maxLng, g.centerLng); - minLng = Math.min(minLng, g.centerLng); - maxLat = Math.max(maxLat, g.centerLat); - minLat = Math.min(minLat, g.centerLat); + g.coordinates.forEach((coordArr) => { + coordinates.push({ + lng: coordArr[0], + lat: coordArr[1] + }); + maxLng = Math.max(maxLng, coordArr[0]); + minLng = Math.min(minLng, coordArr[0]); + maxLat = Math.max(maxLat, coordArr[1]); + minLat = Math.min(minLat, coordArr[1]); + }); }); - if (gridData.length > 0) { - centerLng /= gridData.length; - centerLat /= gridData.length; - } - console.log((maxLng - centerLng) / zoom); + console.log('zoom', zoom); return { - centerLng, - centerLat, - zoom + centerLng: (maxLng + minLng) / 2, + centerLat: (maxLat + minLat) / 2, + zoom, + bounds: [ + minLng - OFFSET_LNG, + minLat - OFFSET_LAT, + maxLng + OFFSET_LNG, + maxLat + OFFSET_LAT + ] }; } function generateGridMap(gridData) { + const width = 800; + const height = 400; // 1. 鍒涘缓涓存椂DOM鍏冪礌 const div = document.createElement('div'); - div.style.width = '800px'; - div.style.height = '400px'; + div.style.width = `${width}px`; + div.style.height = `${height}px`; document.body.appendChild(div); // 娉ㄥ唽涓婃捣甯傚湴鍥炬暟鎹� // console.log(shanghaiMap); @@ -57,7 +70,8 @@ value: [grid.centerLng, grid.centerLat, grid.value], // 涓績鐐圭粡绾害鍜屾暟鍊� coords: grid.coordinates // 缃戞牸鍥涜缁忕含搴﹀潗鏍囨暟缁� [[lng1,lat1], [lng2,lat2], ...] })); - const { centerLng, centerLat, zoom } = calCenterPointAndZoom(gridData); + const { centerLng, centerLat, zoom, bounds } = + calCenterPointAndZoom(gridData); // 2. 閰嶇疆椤� const option = { @@ -72,18 +86,6 @@ label: { show: true }, // 鏄剧ず鍦板悕鏍囩 itemStyle: { areaColor: '#ddddddff', borderColor: '#999' } }, - // series: [ - // { - // type: 'scatter', - // coordinateSystem: 'geo', - // data: [ - // { - // name: '榛勬郸鍖�', - // value: [121.490317, 31.222771, 100] // 涓績鐐瑰潗鏍�+鏁板�� - // } - // ] - // } - // ] series: [ { type: 'custom', @@ -112,12 +114,136 @@ }; chart.setOption(option); - // 3. 瀵煎嚭涓哄浘鐗囷紙杩斿洖base64锛� - return chart.getDataURL({ - type: 'png', - pixelRatio: 1, - backgroundColor: '#ddddddff' + + // 灏嗗儚绱犲潗鏍囪浆鎹负缁忕含搴� + // const convert = (x, y) => { + // return chart.convertFromPixel( + // { + // geoIndex: 0 + // }, + // [x, y] + // ); + // }; + + // 灏嗙粡绾害杞崲涓哄儚绱犲潗鏍� + const convert = (lng, lat) => { + return chart.convertToPixel( + { + geoIndex: 0 + }, + [lng, lat] + ); + }; + + // 璁$畻鐢诲竷鐨勫乏涓婅鍜屽彸涓嬭瀵瑰簲鐨勭粡绾害 + // 璁$畻缃戞牸鍖哄煙鐨勫乏涓婅鍜屽彸涓嬭缁忕含搴﹀搴旂殑鍍忕礌鍧愭爣 + const topLeft = convert(bounds[0], bounds[3]); + const bottomRight = convert(bounds[2], bounds[1]); + // 璁$畻鍚堥�傜殑缂╂斁鍊嶆暟 + const scale = Math.min( + Math.abs(width / (bottomRight[0] - topLeft[0])), + Math.abs(height / (bottomRight[1] - topLeft[1])) + ); + console.log('scale', scale); + + // 鍦板浘缂╂斁姣斾緥 + chart.setOption({ + geo: { + zoom: scale + } }); + + // 3. 瀵煎嚭涓哄浘鐗囷紙杩斿洖base64锛� + return new Promise((resolve, reject) => { + // 寤惰繜鎵ц纭繚缁樺埗瀹屾垚 + setTimeout(() => { + const url = chart.getDataURL({ + type: 'png', + pixelRatio: 2, + backgroundColor: '#fff' + }); + resolve(url); + }, 1000); + }); + // return captureMapByBounds({ + // chart: chart, + // bounds: bounds + // }).catch((err) => { + // console.error('鎴浘澶辫触:', err); + // }); } +/** + * 鏍规嵁缁忕含搴﹁寖鍥存埅鍙栧湴鍥惧尯鍩� + * @param {Object} params - 鎴彇鍙傛暟 + * @param {Array} params.bounds - 缁忕含搴﹁寖鍥� [minLng, minLat, maxLng, maxLat] + * @param {Object} params.chart - ECharts瀹炰緥 + * @returns {Promise<string>} 鎴彇鍖哄煙鐨刡ase64鍥剧墖 + */ +// function captureMapByBounds(params) { +// const { bounds, chart } = params; +// const [minLng, minLat, maxLng, maxLat] = bounds; + +// // 鑾峰彇鍦板浘鍧愭爣绯� +// // const geo = chart.getModel().getComponent('geo'); +// // if (!geo) return Promise.reject('鏈壘鍒板湴鍥剧粍浠�'); + +// // 灏嗙粡绾害杞崲涓哄儚绱犲潗鏍� +// const convert = (lng, lat) => { +// return chart.convertToPixel( +// { +// geoIndex: 0 +// }, +// [lng, lat] +// ); +// }; + +// // 璁$畻鍥涗釜瑙掔殑鍍忕礌鍧愭爣 +// const topLeft = convert(minLng, maxLat); +// const bottomRight = convert(maxLng, minLat); + +// // 鍒涘缓涓存椂Canvas +// const canvas = document.createElement('canvas'); +// const ctx = canvas.getContext('2d'); + +// // 鑾峰彇鍘熷鍥捐〃Canvas +// const originalCanvas = chart.getDom().querySelector('canvas'); + +// // 璁剧疆Canvas灏哄涓烘埅鍙栧尯鍩熷ぇ灏� +// topLeft[0] -= 10; +// topLeft[1] -= 10; +// bottomRight[0] += 10; +// bottomRight[1] += 10; +// topLeft[0] = Math.max(topLeft[0], 0); +// topLeft[1] = Math.max(topLeft[1], 0); +// bottomRight[0] = Math.min(bottomRight[0], originalCanvas.width); +// bottomRight[1] = Math.min(bottomRight[1], originalCanvas.height); +// const width = bottomRight[0] - topLeft[0]; +// const height = bottomRight[1] - topLeft[1]; +// canvas.width = width; +// canvas.height = height; + +// // 瑁佸壀鎸囧畾鍖哄煙 +// ctx.drawImage( +// originalCanvas, +// topLeft[0], +// topLeft[1], // 婧愬浘鍍忚鍓捣鐐� +// width, +// height, // 婧愬浘鍍忚鍓昂瀵� +// 0, +// 0, // 鐩爣鍥惧儚缁樺埗璧风偣 +// width, +// height // 鐩爣鍥惧儚缁樺埗灏哄 +// ); + +// // 杞崲涓篵ase64鍥剧墖 +// return new Promise((resolve) => { +// // 寤惰繜鎵ц纭繚缁樺埗瀹屾垚 +// setTimeout(() => { +// const base64 = canvas.toDataURL('image/png', 1.0); +// resolve(base64); +// }, 100); +// }); +// } + export default { generateGridMap }; diff --git a/src/utils/map/calculate.js b/src/utils/map/calculate.js index 01ffd8d..152837c 100644 --- a/src/utils/map/calculate.js +++ b/src/utils/map/calculate.js @@ -286,5 +286,40 @@ gcj02towgs84, //浠嶨PS杞珮寰� - wgs84_To_Gcj02 + wgs84_To_Gcj02, + + /** + * 璁$畻涓�缁勭粡绾害鍧愭爣鐨勪腑蹇冪偣 + * @param {Array} coordinates - 缁忕含搴︽暟缁勶紝鏍煎紡: [{lng: number, lat: number}, ...] + * @returns {Object} 涓績鐐瑰潗鏍� {lng: number, lat: number} + */ + calculateCenterCoordinates(coordinates) { + if (coordinates.length === 0) return { lng: 0, lat: 0 }; + if (coordinates.length === 1) return coordinates[0]; + + let x = 0, + y = 0, + z = 0; + coordinates.forEach((p) => { + const lng = (p.lng * Math.PI) / 180; // 缁忓害杞姬搴� + const lat = (p.lat * Math.PI) / 180; // 绾害杞姬搴� + + // 杞崲涓轰笁缁村潗鏍� + x += Math.cos(lat) * Math.cos(lng); + y += Math.cos(lat) * Math.sin(lng); + z += Math.sin(lat); + }); + + // 鍙栧钩鍧囧�� + const avgX = x / coordinates.length; + const avgY = y / coordinates.length; + const avgZ = z / coordinates.length; + + // 杞崲鍥炵粡绾害 + const lng = (Math.atan2(avgY, avgX) * 180) / Math.PI; + const hypotenuse = Math.sqrt(avgX ** 2 + avgY ** 2); + const lat = (Math.atan2(avgZ, hypotenuse) * 180) / Math.PI; + + return { lng, lat }; + } }; diff --git a/src/views/historymode/component/MissionReport.vue b/src/views/historymode/component/MissionReport.vue index 2f55c67..8168286 100644 --- a/src/views/historymode/component/MissionReport.vue +++ b/src/views/historymode/component/MissionReport.vue @@ -72,6 +72,7 @@ import chartMapAmap from '@/utils/chart/chart-map-amap'; import { Legend } from '@/model/Legend'; import { getHexColor, getColorBetweenTwoColors } from '@/utils/color'; +import { getGridDataDetailFactorValue } from '@/model/GridDataDetail'; // 鍊熺敤鍗槦閬ユ祴妯″潡涓殑100绫崇綉鏍� const props = defineProps({ @@ -106,7 +107,8 @@ cityName: formObj.value.location.cName, districtCode: formObj.value.location.dCode, districtName: formObj.value.location.dName - } + }, + factorTypes: radioOptions(TYPE0).map((e) => e.name) }; }); @@ -174,6 +176,31 @@ } ] } + ], + gridFusionByAQIList: [ + { + pollutionDegree: '浼�', + _areaDes: '璧拌埅鍖哄煙澶у皬', + _gridDes: '100绫虫鏂瑰舰缃戞牸', + _missionDes: '20250729銆�20250730涓ゆ', + highRiskGridList: [ + { + index: 1, + factor: 'PM2.5', + // 鏍囧噯鑹茬綉鏍煎浘 + gridImgUrl1: '', + // 瀵规瘮鑹茬綉鏍煎浘 + gridImgUrl2: '', + factorValue: 20, + // 鍥涜嚦鑼冨洿锛岄『搴忎负鏈�灏忕粡搴︼紝鏈�澶х粡搴︼紝鏈�灏忕含搴︼紝鏈�澶х含搴� + bounds: [121.4945, 121.4955, 31.2304, 31.2314], + _boundsDes: '鍥涜嚦鑼冨洿', + // 娑夊強琛楅晣 + town: '', + _scenesDes: '娑夊強鐨勬薄鏌撳満鏅�' + } + ] + } ] }; @@ -183,7 +210,9 @@ generateMissionList(params.value).then(() => { generateMissionDetail(params.value).then(() => { generateClueByRiskArea(params.value).then(() => { - generateDocx(); + generateGridFusion(params.value).then(() => { + generateDocx(); + }); }); }); }); @@ -348,63 +377,237 @@ return images; } -function handleMixClick() { - const tags = [1, 2]; - const fetchGridData = () => { - gridApi.mixUnderwayGridData(props.groupId, tags).then((res) => { - const gridData = res.data.map((v) => { - const data = v.pm25; - const grid = gridCellList.value.find((g) => { - return g.cellIndex == v.cellId; +function generateGridFusion(param) { + return dataAnalysisApi.fetchGridFusion(param).then((res) => { + const promiseList = []; + templateParam.gridFusionByAQIList = []; + + res.data.forEach((item) => { + const scenes = []; + item.missionList.forEach((m) => { + m.keyScene.map((s) => { + if (scenes.indexOf(s.name) == -1) { + scenes.push(s.name); + } }); - const { color, nextColor, range, nextRange } = - Legend.getStandardColorAndNext('PM25', data); - const ratio = (data - range) / (nextRange - range); - const _color = getColorBetweenTwoColors( - color.map((v) => v * 255), - nextColor.map((v) => v * 255), - ratio - ); - - // // 鏍规嵁閬ユ祴鏁版嵁璁$畻缃戞牸棰滆壊 - // const { color, nextColor, range, nextRange } = - // Legend.getCustomColorAndNext(data, min, max); - // const ratio = (data - range) / (nextRange - range); - - // const _color = getColorBetweenTwoColors( - // color.map((v) => v * 255), - // nextColor.map((v) => v * 255), - // ratio - // ); - return { - centerLng: grid.longitude, - centerLat: grid.latitude, - value: _color, - coordinates: [ - [grid.point1Lon, grid.point1Lat], - [grid.point2Lon, grid.point2Lat], - [grid.point3Lon, grid.point3Lat], - [grid.point4Lon, grid.point4Lat] - ] - }; }); - // chartMapAmap.generateGridMap(gridData).then((url) => { - // gridBase64Url.value = url; - // }); - gridBase64Url.value = chartMap.generateGridMap(gridData); + const gfbAQI = { + pollutionDegree: item.pollutionDegree, + _areaDes: `璧拌埅鍖哄煙缁忚繃${scenes.join('銆�')}`, + _gridDes: `${item.gridLen}绫虫鏂瑰舰缃戞牸`, + _missionDes: `${item.missionList.map((m) => m.missioncode).join('銆�')}${item.missionList.length}娆 + }; + const _highRiskGridList = []; + item.highRiskGridList.forEach((g, i) => { + const p = generateGridFusionImg(g.factorType, item.gridFusionList).then( + (url) => { + const { url1, url2 } = url; + _highRiskGridList.push({ + index: i + 1, + factor: g.factorType, + // 鏍囧噯鑹茬綉鏍煎浘 + gridImgUrl1: url1, + // 瀵规瘮鑹茬綉鏍煎浘 + gridImgUrl2: url2, + factorValue: g.factorValue, + // 鍥涜嚦鑼冨洿锛岄『搴忎负鏈�灏忕粡搴︼紝鏈�澶х粡搴︼紝鏈�灏忕含搴︼紝鏈�澶х含搴� + _boundsDes: `缁忓害${g.bounds[0]}鑷�${g.bounds[1]}锛岀含搴�${g.bounds[2]}鑷�${g.bounds[3]}`, + // 娑夊強琛楅晣 + town: g.town, + _scenesDes: g.highRiskScenes.map((s) => s.name).join('銆�') + }); + } + ); + promiseList.push(p); + }); + gfbAQI.highRiskGridList = _highRiskGridList; + templateParam.gridFusionByAQIList.push(gfbAQI); }); - }; + return Promise.all(promiseList).then(() => { + return templateParam.gridFusionByAQIList; + }); + // templateParam.gridFusionByAQIList = res.data.map((item) => { + // const scenes = []; + // item.missionList.forEach((m) => { + // m.keyScene.map((s) => { + // if (scenes.indexOf(s.name) == -1) { + // scenes.push(s.name); + // } + // }); + // }); + // return { + // pollutionDegree: item.pollutionDegree, + // _areaDes: `璧拌埅鍖哄煙缁忚繃${scenes.join('銆�')}`, + // _gridDes: `${item.gridLen}绫虫鏂瑰舰缃戞牸`, + // _missionDes: `${item.missionList.map((m) => m.missioncode).join('銆�')}${item.missionList.length}娆, + // highRiskGridList: item.highRiskGridList.map(async (g, i) => { + // const { url1, url2 } = await generateGridFusionImg( + // g.factorType, + // item.gridFusionList + // ); + // return { + // index: i + 1, + // factor: g.factorType, + // // 鏍囧噯鑹茬綉鏍煎浘 + // gridImgUrl1: url1, + // // 瀵规瘮鑹茬綉鏍煎浘 + // gridImgUrl2: url2, + // factorValue: g.factorValue, + // // 鍥涜嚦鑼冨洿锛岄『搴忎负鏈�灏忕粡搴︼紝鏈�澶х粡搴︼紝鏈�灏忕含搴︼紝鏈�澶х含搴� + // _boundsDes: `缁忓害${g.bounds[0]}鑷�${g.bounds[1]}锛岀含搴�${g.bounds[2]}鑷�${g.bounds[3]}`, + // // 娑夊強琛楅晣 + // town: g.town, + // _scenesDes: g.highRiskScenes.map((s) => s.name).join('銆�') + // }; + // }) + // }; + // }); + }); +} - if (gridCellList.value.length == 0) { - gridApi - .fetchGridCell(props.groupId) - .then((res) => { - gridCellList.value = res.data; - }) - .then(() => fetchGridData()); - } else { - fetchGridData(); +async function generateGridFusionImg(factorName, dataList) { + var min = 1000000; + var max = 0; + dataList.forEach((v) => { + min = Math.min(min, getGridDataDetailFactorValue(v.data, factorName)); + max = Math.max(max, getGridDataDetailFactorValue(v.data, factorName)); + }); + + const gridData = dataList.map((v) => { + const data = getGridDataDetailFactorValue(v.data, factorName); + const grid = v.cell; + + // 鏍囧噯鑹� + const { + color: color1, + nextColor: nextColor1, + range: range1, + nextRange: nextRange1 + } = Legend.getStandardColorAndNext(factorName, data); + const ratio1 = (data - range1) / (nextRange1 - range1); + const _color1 = getColorBetweenTwoColors( + color1.map((v) => v * 255), + nextColor1.map((v) => v * 255), + ratio1 + ); + + // 瀵规瘮鑹� + const { color, nextColor, range, nextRange } = Legend.getCustomColorAndNext( + data, + min, + max + ); + const ratio = (data - range) / (nextRange - range); + const _color = getColorBetweenTwoColors( + color.map((v) => v * 255), + nextColor.map((v) => v * 255), + ratio + ); + return [ + { + centerLng: grid.longitude, + centerLat: grid.latitude, + value: _color1, + coordinates: [ + [grid.point1Lon, grid.point1Lat], + [grid.point2Lon, grid.point2Lat], + [grid.point3Lon, grid.point3Lat], + [grid.point4Lon, grid.point4Lat] + ] + }, + { + centerLng: grid.longitude, + centerLat: grid.latitude, + value: _color, + coordinates: [ + [grid.point1Lon, grid.point1Lat], + [grid.point2Lon, grid.point2Lat], + [grid.point3Lon, grid.point3Lat], + [grid.point4Lon, grid.point4Lat] + ] + } + ]; + }); + if (gridData[0] == undefined || gridData[1] == undefined) { + console.log(gridData); } + const url1 = await chartMap.generateGridMap(gridData[0]); + const url2 = await chartMap.generateGridMap(gridData[1]); + if (gridBase64Url.value == null) { + gridBase64Url.value = url1; + } + return { + url1, + url2 + }; +} + +function handleMixClick({ tags = [10, 11], factorName = 'PM25' }) { + generateGridFusion(params.value).then(() => {}); + // const fetchGridData = () => { + // gridApi.mixUnderwayGridData(props.groupId, tags).then((res) => { + // var min = 1000000; + // var max = 0; + // res.data.forEach((v) => { + // min = Math.min(min, getGridDataDetailFactorValue(v, factorName)); + // max = Math.max(max, getGridDataDetailFactorValue(v, factorName)); + // }); + + // const gridData = res.data.map((v) => { + // const data = getGridDataDetailFactorValue(v, factorName); + // const grid = gridCellList.value.find((g) => { + // return g.cellIndex == v.cellId; + // }); + // // const { color, nextColor, range, nextRange } = + // // Legend.getStandardColorAndNext('PM25', data); + // // const ratio = (data - range) / (nextRange - range); + // // const _color = getColorBetweenTwoColors( + // // color.map((v) => v * 255), + // // nextColor.map((v) => v * 255), + // // ratio + // // ); + + // // 鏍规嵁閬ユ祴鏁版嵁璁$畻缃戞牸棰滆壊 + // const { color, nextColor, range, nextRange } = + // Legend.getCustomColorAndNext(data, min, max); + // const ratio = (data - range) / (nextRange - range); + + // const _color = getColorBetweenTwoColors( + // color.map((v) => v * 255), + // nextColor.map((v) => v * 255), + // ratio + // ); + // return { + // centerLng: grid.longitude, + // centerLat: grid.latitude, + // value: _color, + // coordinates: [ + // [grid.point1Lon, grid.point1Lat], + // [grid.point2Lon, grid.point2Lat], + // [grid.point3Lon, grid.point3Lat], + // [grid.point4Lon, grid.point4Lat] + // ] + // }; + // }); + // // chartMapAmap.generateGridMap(gridData).then((url) => { + // // gridBase64Url.value = url; + // // }); + // chartMap.generateGridMap(gridData).then((url) => { + // gridBase64Url.value = url; + // }); + // }); + // }; + + // if (gridCellList.value.length == 0) { + // gridApi + // .fetchGridCell(props.groupId) + // .then((res) => { + // gridCellList.value = res.data; + // }) + // .then(() => fetchGridData()); + // } else { + // fetchGridData(); + // } } function generateDocx() { -- Gitblit v1.9.3