diff --git a/src/App.vue b/src/App.vue index 3b43ef3..df40506 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,9 +1,11 @@ diff --git a/src/assets/js/RouteManageViewer.js b/src/assets/js/RouteManageViewer.js index 4096064..76661d7 100644 --- a/src/assets/js/RouteManageViewer.js +++ b/src/assets/js/RouteManageViewer.js @@ -3,11 +3,11 @@ import * as Cesium from 'cesium' export default class RouteManageViewer { - constructor(viewer, isClose) { + constructor(viewer) { this.viewer = viewer this.scene = viewer.scene this.routeParams = { - isClose: isClose, + isClose: false, height: 0, } this.positions = [] @@ -48,7 +48,7 @@ export default class RouteManageViewer { this.viewer.entities.add(airlineEntity) degreesArr.forEach((pt,index)=>{ let vertexEntity = new Cesium.Entity({ - id: line.unicode + "-航点" + index, + id: line.unicode + "-航点" + (index+1), position: pt, point: { color: Cesium.Color.WHITE, @@ -57,6 +57,19 @@ export default class RouteManageViewer { outlineWidth: 2, disableDepthTestDistance:99000000, // heightReference:Cesium.HeightReference.CLAMP_TO_GROUND, + }, + label:{ + text: "航点 " + (index+1), + font: "2.5rem sans-serif", + fillColor: Cesium.Color.WHITE, + style: Cesium.LabelStyle.FILL, + eyeOffset: new Cartesian3(0,0,50), + horizontalOrigin: Cesium.HorizontalOrigin.RIGHT, + verticalOrigin: Cesium.VerticalOrigin.TOP, + scale: 0.4, + showBackground: true, + backgroundColor: new Cesium.Color(0.165, 0.165, 0.165, 0.99), + backgroundPadding: new Cesium.Cartesian2(15, 7), } }); this.viewer.entities.add(vertexEntity) diff --git a/src/assets/js/measureViewer.js b/src/assets/js/measureViewer.js index 05ae9fc..5a2b255 100644 --- a/src/assets/js/measureViewer.js +++ b/src/assets/js/measureViewer.js @@ -510,7 +510,6 @@ export default class MeasureViewer { this._showResultArea() this.stopAreaMeasure() }, Cesium.ScreenSpaceEventType.RIGHT_CLICK) - } _showResultArea(){ diff --git a/src/assets/js/weatherCharts.ts b/src/assets/js/weatherCharts.ts new file mode 100644 index 0000000..43d7b79 --- /dev/null +++ b/src/assets/js/weatherCharts.ts @@ -0,0 +1,153 @@ +import * as echarts from "echarts"; +import {EChartsType} from "echarts"; + +/** + * 天气图表Echarts的配置函数 + * @param wChart 图表对象 + * @param timeSeries 时间序列 + * @param temp 温度 + * @param windSpeed 风速 + * @param wind360 风向 + * @param precip 降水 + * @param humidity 湿度(暂未使用) + */ +export const setChartOptions = (wChart: EChartsType, timeSeries:string[], temp: number[], windSpeed:number[], + wind360: number[], precip:number[], humidity?:number[]) =>{ + + let windDirection = wind360.map(item=>{ + let arr = [22.5, 22.5+45, 22.5+45*2, 22.5+45*3, 22.5+45*4, 22.5+45*5, 22.5+45*6, 22.5+45*7] + let str = ['北风','东北风','东风','东南风','南风','西南风','西风','西北风'] + if(item<=arr[0] || item>arr[7]){ + return str[0]+': '+ Math.round(item)+'°' + }else if(arr[0] <=item && item<=arr[1]){ + return str[1]+' : '+ Math.round(item)+'°' + }else if(arr[1] <=item && item<=arr[2]){ + return str[2]+' : '+ Math.round(item)+'°' + }else if(arr[2] <=item && item<=arr[3]){ + return str[3]+' : '+ Math.round(item)+'°' + }else if(arr[3] <=item && item<=arr[4]){ + return str[4]+' : '+ Math.round(item)+'°' + }else if(arr[4] <=item && item<=arr[5]){ + return str[5]+' : '+ Math.round(item)+'°' + }else if(arr[5] <=item && item<=arr[6]){ + return str[6]+' : '+ Math.round(item)+'°' + }else if(arr[6] <=item && item<=arr[7]){ + return str[7]+' : '+ Math.round(item)+'°' + } + }) + let colors = ['#5470C6', '#91CC75', '#EE6666']; + wChart.setOption({ + backgroundColor:'#101014', + tooltip: { + trigger: 'axis', + axisPointer: { + type: 'cross', + } + }, + color: colors, + grid: [ + {x: '7%', y: '7%', height: '40%', left: '12%',right: '15%'}, + {x: '7%', y2: '7%', height: '38%', left: '12%',right: '15%', bottom: '10%'} + ], + legend: { + data:['风速','降水','温度'] + }, + xAxis: [ + { + show: false,//隐藏了x轴 + type: 'category', + gridIndex: 0,//对应前面grid的索引位置(第一个) + axisTick: { + alignWithLabel: true + }, + data: windDirection, + }, + { + type: 'category', + gridIndex: 1, //对应前面grid的索引位置(第二个) + axisTick: { + alignWithLabel: true + }, + data: timeSeries, + } + ], + //y轴,不管有几个x轴,几个y轴,或者图,只要找到他对应的grid图的序号索引就可以精准匹配 + yAxis: [ + { + type: 'value', + gridIndex: 1,//对应前面grid的索引位置(第二个) + splitLine: {show: false}, + position: 'right', + axisLine: { + lineStyle: { + color: colors[1] + } + }, + axisLabel: { + formatter: '{value}ml' + } + }, + { + type: 'value', + gridIndex: 1, + nameLocation: 'middle', + name: '温度', + nameTextStyle: { + padding: 25 + }, + splitLine: {show: false}, + position: 'left', + axisLine: { + lineStyle: { + color: colors[2] + } + }, + axisLabel: { + formatter: '{value}°C' + } + }, + { + type: 'value', + gridIndex: 0, + name: '风速', + nameTextStyle: { + padding: 25 + }, + position: 'left', + splitLine: {show: false}, + axisLine: { + lineStyle: { + color: colors[0] + } + }, + axisLabel: { + formatter: '{value}', + fontSize: 12,//y轴坐标轴上的字体大小 + } + } + ], + series: [ + { + name:'风速', + type: "line", + xAxisIndex: 0, + yAxisIndex: 2, + data: windSpeed, + }, + { + name:'降水', + type:'bar', + xAxisIndex: 1, + yAxisIndex: 0, + data: precip + }, + { + name:'温度', + type:'line', + xAxisIndex: 1, + yAxisIndex: 1, + data:temp + } + ] + }) +} diff --git a/src/assets/js/weatherRequest.ts b/src/assets/js/weatherRequest.ts index 3f80d26..edc082e 100644 --- a/src/assets/js/weatherRequest.ts +++ b/src/assets/js/weatherRequest.ts @@ -31,8 +31,13 @@ function query_surface_forecast(lon: number,lat:number) }) } -//查询未来24小时,指定位置和高度的高空气象变量信息 -function query_upper_forecast(lat:number, lon: number, level:number){ +/** 查询未来24小时,指定位置和高度的高空气象变量信息 + * @param lon 经度 + * @param lat 纬度 + * @param level 气压高度 hPa + */ + +function query_upper_forecast(lon: number, lat:number, level:number){ let timestamp = Date.now().toString() return axios({ method: "POST", @@ -50,5 +55,20 @@ function query_upper_forecast(lat:number, lon: number, level:number){ }) } - -export {query_upper_forecast, query_surface_forecast} +/** + * 查询高度层 + */ +function requireWeatherLevel(){ + let timestamp = Date.now().toString() + return axios({ + method: "POST", + url: "/onlinetest/htfp/weather/v1/info/queryLevelsAvailable", + headers: { + "Content-Type": "application/json", + "timestamp": timestamp, + "signature": CryptoJS.HmacSHA1('/htfp/weather/v1/info/queryLevelsAvailable', timestamp) + }, + data: { dataSource: "GFS" } + }) +} +export {query_upper_forecast, query_surface_forecast,requireWeatherLevel} diff --git a/src/components/page/RouteManagePage.vue b/src/components/page/RouteManagePage.vue index 3f92758..ee73ac3 100644 --- a/src/components/page/RouteManagePage.vue +++ b/src/components/page/RouteManagePage.vue @@ -13,12 +13,16 @@ import {EChartsType} from "echarts"; import {transLonlat2Car3} from "@/utils/map/geocomputation.ts"; import {drawEcharts_RouteDetection, profileAnalyse} from "@/utils/map/SpatialAnalysis.ts"; import RouteManageViewer from "@/assets/js/RouteManageViewer.js"; -import {query_surface_forecast} from "@/assets/js/weatherRequest.ts"; +import {query_surface_forecast, query_upper_forecast, requireWeatherLevel} from "@/assets/js/weatherRequest.ts"; +import {setChartOptions} from "@/assets/js/weatherCharts.ts"; +import type { NotificationType } from 'naive-ui' +import { useNotification } from 'naive-ui' let myChart: EChartsType = undefined let weatherChart: EChartsType = undefined let routeStore = useRouteStore() let selectedRouteCode = ref(null) +let selectedLevel = ref(null) let showPtList = ref(true) let selectedRoute = ref(newAirline()) let routesInstore = ref<{ value: string|number; label: string}[]>(null) @@ -49,14 +53,27 @@ let routeCode = [ value: 5, } ] -let levelOptions :any[] = [] +let levelOptions = ref() +const notification = useNotification() +const notify = (type: NotificationType, title: string, detail: string) =>{ + notification[type]({ + title: title, + meta: detail, + duration: 3000, + closable: true, + keepAliveOnHover: true + }) +} onBeforeUnmount(()=>{ clearInterval(timer) }) onMounted(()=>{ - getTime() + routeViewer = new RouteManageViewer(window.viewer) myChart = echarts.init(document.getElementById('echarts-profile'),'dark') weatherChart = echarts.init(document.getElementById('weatherChart'),'dark') + + timer = setInterval(getTimesInterval, 1000); + routeStore.addRoute(route) routeStore.addRoute(route2) routeStore.addRoute(route3) @@ -67,12 +84,25 @@ onMounted(()=>{ label: route.name } }) - + requireWeatherLevel().then(res=>{ + console.log(res) + // 查询失败 + if(!res.data.success){ + notify('error', '气象信息查询失败', res.data.message) + return + } + levelOptions.value = res.data.data.levelsAvailableList[0].pressureLevels.map((level,index)=>{ + return { + value: level, + label: level+'hPa/ '+ res.data.data.levelsAvailableList[0].heightLevels[index] + 'm' + } + }) + levelOptions.value.reverse() + levelOptions.value.unshift({value: -1, label: '地面'}) + selectedLevel.value = -1 + }) }) -function getTime(){ - timer = setInterval(getTimesInterval, 1000); -} function getTimesInterval() { let now = new Date(); @@ -84,7 +114,7 @@ function getTimesInterval() { let seconds = now.getSeconds().toString().padStart(2, '0'); // 秒 // 格式化时间 - nowTime.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`; + nowTime.value = `${year}-${month}-${day} ${hours}:${minutes}:${seconds}` } // 航线选择回调 @@ -92,13 +122,12 @@ function checkRoute(key:number|string){ new Promise((resolve,reject)=>{ selectedRoute.value = routeStore.flyRoute.filter(element => element.unicode === String(key))[0] if(selectedRoute.value == null) reject('未找到航线') - - routeViewer = new RouteManageViewer(window.viewer,selectedRoute.value.isClose) + routeViewer.routeParams.isClose = selectedRoute.value.isClose window.viewer.flyTo(routeViewer.addAirLine(selectedRoute.value), {duration: 2}) + queryWeather(selectedRoute.value.points[0].lon,selectedRoute.value.points[0].lat) setTimeout(()=>resolve('success'),2500) - }).then((result)=>{ - //绘制图表 + //绘制地形图表 let Cartain3 = transLonlat2Car3(selectedRoute.value.points) // 计算地形剖面,默认采样间隔为1km let res = profileAnalyse(window.viewer, Cartain3, 1000) @@ -112,6 +141,7 @@ function checkRoute(key:number|string){ console.log(orderArr,hArr) // 弹出图表窗口 drawEcharts_RouteDetection(myChart, res.distanceArray, res.elevationArray, orderArr, hArr) + }).catch((err)=>{ uiMsg.error(err) }) @@ -171,178 +201,62 @@ function handlePtSelect(key:any, index:number) { //查询上一航点的气象信息 function prevPtWeather() { routePtNumber.value -= 1 + window.viewer.flyTo(window.viewer.entities.getById(selectedRoute.value.unicode + "-航点" + (routePtNumber.value).toString()), + {duration: 1}) let lat = selectedRoute.value.points[routePtNumber.value-1].lat let lon = selectedRoute.value.points[routePtNumber.value-1].lon if(lat && lon){ - queryWeather(lon,lat) + queryWeather(lon,lat, selectedLevel.value) } } //查询下一航点的气象信息 function nextPtWeather() { routePtNumber.value += 1 + window.viewer.flyTo(window.viewer.entities.getById(selectedRoute.value.unicode + "-航点" + (routePtNumber.value).toString()), + {duration: 1}) + let lat = selectedRoute.value.points[routePtNumber.value-1].lat let lon = selectedRoute.value.points[routePtNumber.value-1].lon if(lat && lon){ - queryWeather(lon,lat) + queryWeather(lon,lat, selectedLevel.value) } - // TODO: 考虑高度 } function queryWeather(lon:number,lat:number,level?: number) { - query_surface_forecast(lon,lat) - .then(res=>{ - let weatherData = res.data.data - let timeArr = weatherData.time.map(item=>{ - let i = item.indexOf('T') - return item.slice(i+1,i+3) + '时' - }) - setChartOptions(timeArr.slice(0,8),weatherData.temp.slice(0,8),weatherData.windSpeed.slice(0,8), - weatherData.wind360.slice(0,8), weatherData.precip.slice(0,8),weatherData.humidity.slice(0,8)) - }) -} - -function setChartOptions(timeSeries:string[], temp: number[], windSpeed:number[], wind360: number[], precip:number[], humidity?:number[]) { - let windDirection = wind360.map(item=>{ - let arr = [22.5, 22.5+45, 22.5+45*2, 22.5+45*3, 22.5+45*4, 22.5+45*5, 22.5+45*6, 22.5+45*7] - let str = ['北风','东北风','东风','东南风','南风','西南风','西风','西北风'] - if(item<=arr[0] || item>arr[7]){ - return str[0]+': '+ Math.round(item)+'°' - }else if(arr[0] <=item && item<=arr[1]){ - return str[1]+' : '+ Math.round(item)+'°' - }else if(arr[1] <=item && item<=arr[2]){ - return str[2]+' : '+ Math.round(item)+'°' - }else if(arr[2] <=item && item<=arr[3]){ - return str[3]+' : '+ Math.round(item)+'°' - }else if(arr[3] <=item && item<=arr[4]){ - return str[4]+' : '+ Math.round(item)+'°' - }else if(arr[4] <=item && item<=arr[5]){ - return str[5]+' : '+ Math.round(item)+'°' - }else if(arr[5] <=item && item<=arr[6]){ - return str[6]+' : '+ Math.round(item)+'°' - }else if(arr[6] <=item && item<=arr[7]){ - return str[7]+' : '+ Math.round(item)+'°' + let response : Promise + if(level==null|| level==-1){ //地面 + response = query_surface_forecast(lon,lat) + }else{ + response = query_upper_forecast(lon,lat, level) + } + response.then(res=>{ + // 查询失败 + if(!res.data.success){ + notify('error', '气象信息查询失败', res.data.message) + return } - }) - let colors = ['#5470C6', '#91CC75', '#EE6666']; - weatherChart.setOption({ - backgroundColor:'#101014', - tooltip: { - trigger: 'axis', - axisPointer: { - type: 'cross', - } - }, - color: colors, - grid: [ - {x: '7%', y: '7%', height: '40%', left: '12%',right: '15%'}, - {x: '7%', y2: '7%', height: '38%', left: '12%',right: '15%', bottom: '10%'} - ], - legend: { - data:['风速','降水','温度'] - }, - xAxis: [ - { - show: false,//隐藏了x轴 - type: 'category', - gridIndex: 0,//对应前面grid的索引位置(第一个) - axisTick: { - alignWithLabel: true - }, - data: windDirection, - }, - { - type: 'category', - gridIndex: 1, //对应前面grid的索引位置(第二个) - axisTick: { - alignWithLabel: true - }, - data: timeSeries, - } - ], - //y轴,不管有几个x轴,几个y轴,或者图,只要找到他对应的grid图的序号索引就可以精准匹配 - yAxis: [ - { - type: 'value', - gridIndex: 1,//对应前面grid的索引位置(第二个) - splitLine: {show: false}, - position: 'right', - axisLine: { - lineStyle: { - color: colors[1] - } - }, - axisLabel: { - formatter: '{value}ml' - } - }, - { - type: 'value', - gridIndex: 1, - nameLocation: 'middle', - name: '温度', - nameTextStyle: { - padding: 25 - }, - splitLine: {show: false}, - position: 'left', - axisLine: { - lineStyle: { - color: colors[2] - } - }, - axisLabel: { - formatter: '{value}°C' - } - }, - { - type: 'value', - gridIndex: 0, - name: '风速', - nameTextStyle: { - padding: 25 - }, - position: 'left', - splitLine: {show: false}, - axisLine: { - lineStyle: { - color: colors[0] - } - }, - axisLabel: { - formatter: '{value}', - textStyle: { - fontSize: 12//y轴坐标轴上的字体大小 - } - } - } - ], - series: [ - { - name:'风速', - type: "line", - xAxisIndex: 0, - yAxisIndex: 2, - data: windSpeed, - }, - { - name:'降水', - type:'bar', - xAxisIndex: 1, - yAxisIndex: 0, - data: precip - }, - { - name:'温度', - type:'line', - xAxisIndex: 1, - yAxisIndex: 1, - data:temp - } - ] + let weatherData = res.data.data + let timeArr = weatherData.time.map(item=>{ + let i = item.indexOf('T') + return item.slice(i+1,i+3) + '时' + }) + if(!Reflect.has(weatherData,'precip')){ + weatherData.precip = Array.from({length:timeArr.length},(v,k)=>0) + } + setChartOptions(weatherChart, timeArr.slice(0,8),weatherData.temp.slice(0,8),weatherData.windSpeed.slice(0,8), + weatherData.wind360.slice(0,8), weatherData.precip.slice(0,8),weatherData.humidity.slice(0,8)) }) } +function levelChanged(key: string | number) { + selectedLevel.value = key + let lat = selectedRoute.value.points[routePtNumber.value-1].lat + let lon = selectedRoute.value.points[routePtNumber.value-1].lon + if(lat && lon){ + queryWeather(lon,lat, Number(key)) + } +}