阶段三:项目开发---民航功能模块实现:任务24:航空实时监控
任务描述
内 容:地图展示、飞机飞行轨迹、扇区控制。航空实时监控,是飞机每秒发送坐标,经过终端转换实时发送给塔台,为了飞机位置的精准度,传输位置的密度很大,在地图位置显示不明显。本次为了案例展示效果,对飞机位置重新进行了规划,结合百度地图,和数据清洗,展示到前台。
学 时:6学时
知识点:后端文件处理
重 点:百度地图展示、飞机动态飞行、数据获取
任务指导
1、后台从文件名为part-xxxxx的文件中读取每行飞机坐标点传输到Kafka,Spark清洗任务拉取Kafka数据对数据进行清洗,对数据坐标进行判断转换成各扇区,发送到MySQL作为转换后的数据点,这一过程在前面的数据清洗和统计任务阶段已经完成,可参见:
- BigData-Etl-KongGuan/src/main/java/com/qrsoft/etl/spark/SparkUtil.java类
- BigData-Etl-KongGuan/src/main/java/com/qrsoft/etl/spark/SparkStreamingApplication.java类
2、前端经过读取后台MySQL的数据并结合百度地图,将飞机的实时飞行状态展示出来,可按以下步骤实现:
1)引入百度地图
2)地图上添加飞机
3)飞机动态修改
4)Vue中重新绑定值
3、后台逻辑实现(BigData-KongGuan)
1)编写后台逻辑,访问数据库,查询飞行实时数据
4、完成功能测试
下面图中是原企业项目的真实展示页面,在当前项目中由于数据集的特点,实际展示效果可能会有差异。

任务实现
1、从Kafka中读取飞机数据,并进行清洗
此步骤在前面的“使用Spark清洗统计业务数据并保存到数据库中”任务阶段应该已经完成。如果没有完成,请参考源代码自行完成。核心类主要有三个:SparkStreamingApplication类、SparkUtil类和MapManager类,以及一些辅助类。
- BigData-Etl-KongGuan/src/main/java/com/qrsoft/etl/spark/SparkStreamingApplication.java类的作用是实时读取Kafka中所有Topic的数据,然后进入到不同的处理分支程序中进行数据清洗和存储,处理“实时飞行的航迹数据”的分支的代码如下:
SparkUtil sparkUtil = new SparkUtil();
try {switch (topName) {case Constants.TASK_RADAR:sparkUtil.TaskRadarStr(taskRadar);break;
- 进入 Constants.TASK_RADAR 分支后,会调用SparkUtil类中的TaskRadarStr方法来处理数据,BigData-Etl-KongGuan/src/main/java/com/qrsoft/etl/spark/SparkUtil.java类中相关的核心代码如下:
/*** 业务处理* @param strs 航迹数据*/public void TaskRadarStr(String strs){System.out.println(strs);String[] str = strs.split(",");logger.info(str.toString());try {//判断是哪个扇区 sectionG sectionK sectionEString sectionVal = "";MapManager mapMan = new MapManager();double lat = Double.valueOf(str[8]);double lng = Double.valueOf(str[9]);if(mapMan.isInRectangleArea(lat,lng,sectionG[0],sectionG[1],sectionG[2],sectionG[3])){sectionVal = "G";}else if(mapMan.isInRectangleArea(lat,lng,sectionK[0],sectionK[1],sectionK[2],sectionK[3])){sectionVal = "K";}else if(mapMan.isInRectangleArea(lat,lng,sectionE[0],sectionE[1],sectionE[2],sectionE[3])){sectionVal = "E";};System.out.println("=========================================================");System.out.println("========================"+sectionVal+"=================================");System.out.println("=========================================================");MultiRadar mr = new MultiRadar();//MultiRadar mr = new MultiRadar(str[1],str[12],str[11],str[0],str[17],str[15],str[18],str[19],str[9],str[8],str[7],str[13],str[14],str[10],str[3],str[6],str[4],str[16],str[5],str[2],sectionVal);mr.setAcid(str[0]);mr.setAreaSource(str[1]);mr.setClimbordownSpeed(str[2]);mr.setDirection(str[3]);mr.setFcu(str[4]);mr.setFlyStatus(str[5]);mr.setRadarCFL(str[6]);mr.setRadarHeight(str[7]);mr.setRadarLatitude(str[8]);mr.setRadarLongTitude(str[9]);mr.setRadarSpeed(str[10]);mr.setRadarType(str[11]);mr.setSendRadarTime(str[12]);mr.setSpeedX(str[13]);mr.setSpeedY(str[14]);mr.setSsrCode(str[15]);mr.setTime(str[16]);mr.setTrackNumber(str[17]);mr.setZhiJiaoX(str[18]);mr.setZhiJiaoY(str[19]);mr.setSection(sectionVal);//根据航班号,查询是否已经开始对该航迹进行统计MultiRadarDao dao = new MultiRadarDao();boolean bool = dao.isExistThisRadar(mr.getAcid());if(bool) {//存在,修改数据库中该航迹dao.updateAnRadarMsg(mr);}else{//尚未进行统计 创建一个统计信息dao.createAnRadarMsg(mr);}}catch (Exception e){e.printStackTrace();logger.info(" MultiRadar错误数据: [{}]", strs);}}
- 在处理“实时飞行的航迹数据”时,会使用到一个辅助类MapManager,该类的功能包括:判断飞机是否在指定的矩形区域内、判断飞机是否在指定的经纬度范围内,核心代码如下:
public class MapManager {/*** 是否在矩形区域内* * @param lat 测试点经度* @param lng 测试点纬度* @param minLat 纬度范围限制1* @param maxLat 纬度范围限制2* @param minLng 经度限制范围1* @param maxLng 经度范围限制2*/public boolean isInRectangleArea(double lat,double lng,double minLat, double maxLat,double minLng,double maxLng){if(this.isInRange(lat, minLat, maxLat)){//如果在纬度的范围内if(minLng*maxLng>0){if(this.isInRange(lng, minLng, maxLng)){return true;}else {return false;}}else {if(Math.abs(minLng)+Math.abs(maxLng)<180){if(this.isInRange(lng, minLng, maxLng)){return true;}else {return false;}}else{double left = Math.max(minLng, maxLng);double right = Math.min(minLng, maxLng);if(this.isInRange(lng, left, 180)||this.isInRange(lng, right,-180)){return true;}else {return false;}}}}else{return false;}}/*** 判断是否在经纬度范围内* * @param point* @param left* @param right*/public boolean isInRange(double point, double left,double right){if(point>=Math.min(left, right)&&point<=Math.max(left, right)){return true;}else {return false;}}
}
- 其他辅助类,请参考源代码。
2、打开前端Vue项目kongguan_web,完成前端Vue页面(src/views/Home/Map.vue)设计
- 在Vue页面 src/views/Home/Map.vue 中引入百度地图,首先添加百度地图背景图,并在地图上飞机,飞机相当于在地图上添加mark点
其中bm-marker是飞机,bm-label是飞机旁边显示的标签,通过v-for标签循环绑定数据, 例如:v-for="item in caseList",caseList是在下边的数据获取步骤中赋值的。
<template><div style="height: 100%"><baidu-map :center="center" :zoom="zoom" style="height:100%" @click="getClickInfo":scroll-wheel-zoom='true' :map-style="mapStyle"><bm-marker v-for="item in caseList" :key="item.id":position="{lng: item.radarLongtitude, lat: item.radarLatitude}" :rotation="Number(item.direction)":icon="{url: urlz(item.id), size: {width: 100, height: 75}}"><bm-label :position="{lng: item.radarLongtitude, lat: item.radarLatitude}":content="item.acid":labelStyle="{color: 'gray', fontSize : '8px',backgroundColor: 'rgba(0,0,0,0)',border:0}"title="Hover me"/></bm-marker><!-- 缩放控件,注册此组件才会显示拖放进度 --><bm-navigation anchor="BMAP_ANCHOR_TOP_LEFT"></bm-navigation></baidu-map>
... 接下文 ...
- 在Vue页面中添加扇区管理的页面设计:
页面中包含"G"、"K"、"E"三个扇区的按钮,并绑定了click事件,当点击其中任意一个扇区对应的按钮时会触发click事件,执行clickData方法,clickData方法在后面的步骤中定义,主要是根据传入的不同的参数("G"、"K"、"E"),获取不同扇区的数据。
... 接上文 ...<div class="allStatistics box"><img src="../../assets/images/nl.png" width="45px" height="45px" style="position: absolute;right: 90px;top: 75px"><img src="../../assets/images/gj.png" width="45px" height="45px"style="position: absolute ;right: 290px;top: 70px"><div style=" margin-left: 70px;margin-top: 12px"><div>当前时间:{{new Date().getFullYear()}}-{{new Date().getMonth()}}-{{new Date().getDate()}}  {{newDate().getHours()}}:{{new Date().getMinutes()}}:{{new Date().getSeconds()}}</div><div style="margin-left: -17px">当前位置:{{this.center.lng}} {{this.center.lat}}</div></div><div style=" font-weight: bold;position: absolute;top: 145px;left: 45px"><span style="color: #2a58f4">轨迹数:</span><span style="color: #2a58f4">{{count}}</span><span style="color: #f17140;margin-left: 52px">告警数:</span><span style="color: #f17140">{{count1}}</span></div></div><div class="sectors1 box"><div class="title">当前用户: <span class="npc">管理员 G</span></div><div ><el-button :type="isActive==='G'?'success':'primary'" style="margin-left: 16px" @click="clickData('G')">G</el-button><el-button :type="isActive==='K'?'success':'primary'" :class="isActive === 2?'active':''"@click="clickData('K')">k</el-button><el-button :type="isActive==='E'?'success':'primary'" :class="isActive === 3?'active':''"@click="clickData('E')">E</el-button></div></div><div class="sectors2 box"><div class="title">扇区状态栏</div><div><el-button :type="isActive==='G'?'success':'primary'" style="margin-left: 16px" @click="clickData('G')">G</el-button><el-button :type="isActive==='K'?'success':'primary'" :class="isActive === 2?'active':''"@click="clickData('K')">k</el-button><el-button :type="isActive==='E'?'success':'primary'" :class="isActive === 3?'active':''"@click="clickData('E')">E</el-button></div></div><div class="simi_box box"><div class="similarity"><el-tag effect="dark"><span class="tag-group__title" style="">相似航班数提醒</span></el-tag><div v-for="(it,index) in atcList" :key="index" style="height: 45px; font-weight: bold;"><spanstyle="margin-left: 25px">{{it.gjSector}}</span><spanstyle="margin-left: 45px">{{it.gj}}</span></div></div><div class="similarity"><el-tag effect="dark"><span class="tag-group__title" style="">管制指令纠错</span></el-tag><div v-for="(it,index) in warnList" :key="index"><div style="height: 45px;text-align: center"><spanstyle="width: 100%; font-weight: bold;">{{it.gj_acids}}</span></div><div style="height: 45px;text-align: center;"><table style="height: 45px"><tr><td style="width: 150px; border: #2a58f4 3px solid; border-left: none">{{it.gj_name}}</td><td style="width: 83px; border: #2a58f4 3px solid">{{it.gj_track_num1}}</td><td style="width: 83px; border: #2a58f4 3px solid">{{it.gj_track_num2}}</td><td style="width: 83px; border: #2a58f4 3px solid; border-right: none">{{it.gj_distinct}}</td></tr></table></div></div></div></div></div>
</template>
- 导入访问服务端的api路由
<script>import {findLocusCount,findMultRadar,findWarnSimilarOfATC,findWarnSimilarOfATCCount,findWarnTp} from "@/api/map/map";
... 接下文 ...
- 初始化数据,在地图上设置一个初始点,并设置样式
... 接上文 ...export default {name: 'TestBaiDu',data() {return {center: {lng: 118.78995, lat: 36.62934},zoom: 8,url1: require("../../assets/images/fj.png"),url2: require("../../assets/images/hfj.png"),markerPoint: {lng: 116.404, lat: 39.915},caseList: [],warnList: [],atcList: [],count: 1,count1: 1,isActive: 'G',mapStyle: {styleJson: [{"featureType": "water","elementType": "all","stylers": {"color": "#285ea5"}},{"featureType": "land","elementType": "all","stylers": {"color": "#0c3c7f"}},{"featureType": "road","elementType": "all","stylers": {"visibility": "off"}},{"featureType": "point","elementType": "all","stylers": {"visibility": "off"}},{"featureType": "all","elementType": "labels.text.fill","stylers": {"color": "#2da0c6","visibility": "off"}}]},timer: null,}},
... 接下文 ...
- 获取数据
其中loadData方法是用来获取实时飞行数据,loadWarn方法是获取告警信息,clickData方法是响应扇区按钮的点击事件,查询不同扇区对应的数据。
... 接上文 ...mounted() {this.loadWarn();this.loadData();this.clickData();this.timeOut();},beforeDestroy() { //页面关闭时清除定时器 window.clearInterval(this.timer);this.timer = null;},destroyed() {window.clearInterval(this.timer);this.timer = null; },methods: {urlz(data){for(let i=0;i<this.warnList.length;i++){var value1 = this.warnList[i].gj_track_num1;var value2 = this.warnList[i].gj_track_num2;if(value1 == data || value2 == data){return this.url2;}}return this.url1;},getClickInfo(e) {this.center.lng = e.point.lngthis.center.lat = e.point.lat},loadData() {findMultRadar().then(data => {if (data.isSuccess) {this.caseList = data.result;this.caseList.forEach(it => {it.count = it.areaSource + "," + it.trackNumber})} else {this.$message.error("数据获取失败");}})},loadWarn(){findWarnTp().then(data => {if (data.isSuccess) {this.warnList = data.result;} else {this.warnList.error("数据获取失败");}})},clickData(data) {if (data == null) {this.isActive = 'G'data = 'G'} else {this.isActive = data}findLocusCount(data).then(data => {if (data.isSuccess) {this.count = data.result;} else {this.warnList.error("数据获取失败");}}),findWarnSimilarOfATC(data).then(data => {if (data.isSuccess) {this.atcList = data.result;} else {this.atcList.error("数据获取失败");}}),findWarnSimilarOfATCCount(data).then(data => {if (data.isSuccess) {this.count1 = data.result;} else {this.count1.error("数据获取失败");}})},
... 接下文 ...
- 创建一个定时器,定时获取数据,以更新飞机的位置
... 接上文 ...timeOut(){// 需要在一开始就先调用一遍该方法,否则在开始的5s内是没有数据的 if (this.timer) {window.clearInterval(this.timer)} else {this.timer = window.setInterval(() => {this.loadData();}, 9000)}},}}
</script>
... 接下文 ...
- 页面样式
... 接上文 ...
<style>.sectors1 { top: 20px; right: 450px; background: #fff; width: 220px; height: 80px; margin-top: 30px; }.sectors2 { top: 20px; right: 690px; background: #fff; width: 220px; height: 80px; margin-top: 30px; }.box { position: absolute; margin-top: 70px; }.allStatistics { background: #fff; width: 400px; height: 200px; top: 60px; right: 20px; margin-top: 30px; }.similarity { background: #fff; overflow: hidden; border-radius: 5px 5px 0 0; margin-bottom: 20px; }.simi_box { top: 300px; right: 20px; width: 400px; height: 400px; margin-top: 18px; }.npc { color: #2a58f4; }.title { color: #575757; text-align: center; font-weight: bold; }.active { background-color: #00b700; }.el-button { height: 25px; }.el-tag { width: 400px; border-radius: 0px; }.common-right{ padding-top: 60px; padding-right: 0; padding-left: 0; }
</style>
- 在src/api/目录下创建map目录,然后创建api路由文件 src/api/map/map.js,用于访问服务端相应的Controller(主要是通过findMultRadar()方法“查询综合航迹数据”并显示航迹图,还会涉及到“管制指令纠错”、“根据扇区名称获取该扇区航班数”、“根据扇区号查询相似航班”、“根据扇区号查询相似航班告警总数”等数据的展示)
import request from "../../utils/request";//综合航迹数据查询相关的服务器端请求的根路径
const baseUrl="/api/multiRadar"
//年度统计查询相关的服务器端请求的根路径
const warUrl ="/api/warnFlightHistory"
//航班告警查询相关的服务器端请求的根路径
const warSimUrl = "/api/warnSimilarHistory"
//扇区操作查询相关的服务器端请求的根路径
const atcUrl = "/api/atc"//查询综合航迹数据
export function findMultRadar(){return request({url:baseUrl+"/findMultRadar",method: "get",})
}
//管制指令纠错
export function findWarnTp(){return request({url:warUrl+"/findWarnTp",method: "get",})
}
//根据扇区名称获取该扇区航班数
export function findLocusCount(data){return request({url:atcUrl+"/findLocusCount?planSectorName="+data,method: "get",})
}
//根据扇区号查询相似航班
export function findWarnSimilarOfATC(data){return request({url:warSimUrl+"/findWarnSimilarOfATC?sectorName="+data,method: "get",})
}
//根据扇区号查询相似航班告警总数
export function findWarnSimilarOfATCCount(data){return request({url:warSimUrl+"/findWarnSimilarOfATCCount?sectorName="+data,method: "get",})
}
- 修改 src/router/index.js 路由文件,添加Map.vue页面的路由跳转
... 略 ...{path: '/',component: Layout,redirect: '/map',children: [{path: 'map',component: resolve => require(['@/views/Home/Map'], resolve),name: 'map',meta: { title: 'map' }}]},
... 略 ...
- src/router/index.js文件的完整内容如下:
import Vue from 'vue'
import Router from 'vue-router'const originalPush = Router.prototype.push;
Router.prototype.push = function push(location) {return originalPush.call(this, location).catch(err => err)
}Vue.use(Router)
/* Layout */
import Layout from '@/views/Layout/Layout'
const router = new Router({base: process.env.BASE_URL,mode: 'history',routes: [{path: "/login",component: resolve => require(['@/views/Login/Login'], resolve),hidden: true,meta: {auth: true}},{path: '/',component: Layout,redirect: '/home',children: [{path: 'home',component: resolve => require(['@/views/Home/Index'], resolve),name: 'home',meta: { title: 'home' }}]},{path: '/',component: Layout,redirect: '/map',children: [{path: 'map',component: resolve => require(['@/views/Home/Map'], resolve),name: 'map',meta: { title: 'map' }}]},]
})// 导航守卫
// 使用 router.beforeEach 注册一个全局前置守卫,判断用户是否登陆
router.beforeEach((to, from, next) => {if (to.path === '/login') {next();} else {let token = localStorage.getItem('Authorization');if (token === null || token === '') {next('/login');} else {next();}}
});
export default router
- 确保 src/App.vue 文件的内容如下:
<template><div id="app"><router-view/></div>
</template><script>
export default {name: 'App',
}
</script><style>
html,body,#app{height: 100%;
}
</style>
3、打开后端项目BigData-KongGuan,完成后台逻辑实现
- 编写以下Controller类,来处理客户端发送过来的请求,涉及以下几个类:
| 类/接口 | 作用 |
|---|---|
| com.qrsoft.controller.AtcController | 扇区操作类:处理客户端的 /api/atc 相关的扇区操作请求 |
| com.qrsoft.controller.MultiRadarController | 综合航迹数据:处理客户端的 /api/multiRadar 相关的综合航迹数据查询请求 |
| com.qrsoft.controller.WarnFlightHistoryController | 年度告警统计:处理客户端的 /api/warnFlightHistory 相关的年度统计查询请求 |
| com.qrsoft.controller.WarnSimilarHistoryController | 航班告警:处理客户端的 /api/warnSimilarHistory 相关的航班告警查询请求 |
1)在com.qrsoft.controller.AtcController类中主要调用其中的 findLocusCount() 方法,用于根据扇区名称获取该扇区航班数。
/**
* 根据扇区名称获取该扇区航班数
*/
@ApiOperation(value = "根据扇区名称获取该扇区航班数")
@GetMapping("/findLocusCount")
public Result findLocusCount(@RequestParam String planSectorName){return service.findLocusCount(planSectorName);
}
AtcController类的完整内容如下:
@Api(tags = "扇区操作类")
@RestController
@RequestMapping("/api/atc")
public class AtcController {@Autowiredprivate AtcService service;/*** 获取各扇区航班数*/@ApiOperation(value = "获取各扇区航班数")@GetMapping("/findSectorSortie")public Result findSectorSortie(){return service.findSectorSortie();}/*** 根据扇区名称获取该扇区航班数*/@ApiOperation(value = "根据扇区名称获取该扇区航班数")@GetMapping("/findLocusCount")public Result findLocusCount(@RequestParam String planSectorName){return service.findLocusCount(planSectorName);}/*** 扇区架次数动态统计(饼状图)*/@ApiOperation(value = "扇区架次数动态统计(饼状图)")@GetMapping("/findATCTime")public Result findATCTime(){return service.findATCTime();}
}
2)在com.qrsoft.controller.MultiRadarController类中主要调用findMultRadar()方法,用于综合航迹数据查询,MultiRadarController类的内容如下:
@Api(tags = "综合航迹数据")
@RestController
@RequestMapping("/api/multiRadar")
public class MultiRadarController {@Autowiredprivate MultiRadarService service;/*** 查询综合航迹数据*/@GetMapping("/findMultRadar")public Result findMultRadar(){return service.findMultRadar();}
}
3)在com.qrsoft.controller.WarnFlightHistoryController类中主要调用 findWarnTp() 方法,用于查询“管制指令纠错”的数据。
/*** 管制指令纠错*/
@ApiOperation(value = "管制指令纠错")
@GetMapping("/findWarnTp")
public Result findWarnTp(){return service.findWarnTp();
}
WarnFlightHistoryController类的完整内容如下:
@Api(tags = "年度统计")
@RestController
@RequestMapping("/api/warnFlightHistory")
public class WarnFlightHistoryController {@Autowiredprivate WarnFlightHistoryService service;/*** 年度警告分类统计*/@ApiOperation(value = "年度警告分类统计")@GetMapping("/annualWarningStatisticsByCategory")public Result annualWarningStatisticsByCategory(){return service.annualWarningStatisticsByCategory();}/*** 年度警告区域统计*/@ApiOperation(value = "年度警告区域统计")@GetMapping("/annualWarningAreaStatistics")public Result annualWarningAreaStatistics(){return service.annualWarningAreaStatistics();}/*** 管制指令纠错*/@ApiOperation(value = "管制指令纠错")@GetMapping("/findWarnTp")public Result findWarnTp(){return service.findWarnTp();}
}
4)创建com.qrsoft.controller.WarnSimilarHistoryController类,在类中主要调用findWarnSimilarOfATC()和findWarnSimilarOfATCCount()方法,用于“根据扇区号查询相似航班告警”和“根据扇区号查询相似航班告警总数”。
/**
* 根据扇区号查询相似航班告警
*/
@ApiOperation(value = "根据扇区号查询相似航班")
@GetMapping("/findWarnSimilarOfATC")
public Result findWarnSimilarOfATC(@RequestParam String sectorName){return service.findWarnSimilarOfATC(sectorName);
}
/**
* 根据扇区号查询相似航班告警总数
*/
@ApiOperation(value = "根据扇区号查询相似航班告警总数")
@GetMapping("/findWarnSimilarOfATCCount")
public Result findWarnSimilarOfATCCount(@RequestParam String sectorName){return service.findWarnSimilarOfATCCount(sectorName);
}
WarnSimilarHistoryController类的完整代码如下:
package com.qrsoft.controller;import com.qrsoft.common.Result;
import com.qrsoft.service.WarnSimilarHistoryService;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;@Api(tags = "航班告警")
@RestController
@RequestMapping("/api/warnSimilarHistory")
public class WarnSimilarHistoryController {@Autowiredprivate WarnSimilarHistoryService service;/*** 查询相似航班告警*/@ApiOperation(value = "查询相似航班告警")@GetMapping("/findWarnSimilarHistory")public Result findWarnSimilarHistory(){return service.findWarnSimilarHistory();}/*** 根据扇区号查询相似航班告警*/@ApiOperation(value = "根据扇区号查询相似航班")@GetMapping("/findWarnSimilarOfATC")public Result findWarnSimilarOfATC(@RequestParam String sectorName){return service.findWarnSimilarOfATC(sectorName);}/*** 根据扇区号查询相似航班告警总数*/@ApiOperation(value = "根据扇区号查询相似航班告警总数")@GetMapping("/findWarnSimilarOfATCCount")public Result findWarnSimilarOfATCCount(@RequestParam String sectorName){return service.findWarnSimilarOfATCCount(sectorName);}
}
- 编写以下Controller对应的Service类,包括以下几个类:
| 类/接口 | 作用 |
|---|---|
| com.qrsoft.service.AtcService | 扇区操作的业务模块处理类 |
| com.qrsoft.service.MultiRadarService | 综合航迹数据查询的业务模块处理类 |
| com.qrsoft.service.WarnFlightHistoryService | 年度告警数据查询的业务模块处理类 |
| com.qrsoft.service.WarnSimilarHistoryService | 航班告警数据查询的业务模块处理类 |
1)com.qrsoft.service.AtcService类的内容如下:
@Service
public class AtcService extends ServiceImpl<AtcMapper, Atc> {@Autowiredprivate MultiRadarService multiRadarService;/*** 查询所有扇区航班架次*/public Result findSectorSortie() {List<Atc> sectorSortie = baseMapper.findSectorSortie();return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS, sectorSortie);}/*** 根据扇区号查询架次*/public Result findLocusCount(String planSectorName) {QueryWrapper<MultiRadar> queryWrapper = new QueryWrapper<>();queryWrapper.eq("section",planSectorName);int count = multiRadarService.count(queryWrapper);return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS, count);}/*** 扇区架次数动态统计(饼状图)*/public Result findATCTime() {List<String> sectorName = new ArrayList<>();sectorName.add("K");sectorName.add("S");sectorName.add("E");sectorName.add("P");sectorName.add("G");List<String> executeTime = baseMapper.findATCTime();List list = new ArrayList();for (int i = 0; executeTime.size() > i; i++) {ArrayList<Object> objects = new ArrayList<>();for (int j = 0; sectorName.size() > j; j++) {Atc atcTime2 = baseMapper.findATCTime2(executeTime.get(i), sectorName.get(j));HashMap<String, Object> map = new HashMap<>();if (atcTime2.getPlanSectorName() != null) {map.put(atcTime2.getPlanSectorName(), atcTime2.getCount());}else {map.put(sectorName.get(j),0);}objects.add(map);}list.add(objects);}return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS, list);}
}
2)创建com.qrsoft.service.MultiRadarService类,类中包含一个findMultRadar()方法,用于查询综合航迹数据,内容如下:
package com.qrsoft.service;import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qrsoft.common.Result;
import com.qrsoft.common.ResultConstants;
import com.qrsoft.entity.MultiRadar;
import com.qrsoft.mapper.MultiRadarMapper;
import org.springframework.stereotype.Service;import java.util.List;@Service
public class MultiRadarService extends ServiceImpl<MultiRadarMapper, MultiRadar> {/*** 查询综合航迹数据*/public Result findMultRadar(){List<MultiRadar> multiRadars = baseMapper.selectList(null);return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS,multiRadars);}
}
3)com.qrsoft.service.WarnFlightHistoryService类的内容如下:
@Service
public class WarnFlightHistoryService extends ServiceImpl<WarnFlightHistoryMapper, WarnFlightHistory> {/*** 年度警告区域统计*/public Result annualWarningAreaStatistics(){List<WarnFlightHistory> warnFlightHistories = baseMapper.annualWarningAreaStatistics();return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS,warnFlightHistories);}/*** 年度警告分类统计*/public Result annualWarningStatisticsByCategory(){List<WarnFlightHistory> warnFlightHistories = baseMapper.annualWarningStatisticsByCategory();return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS,warnFlightHistories);}/*** 管制指令纠错*/public Result findWarnTp(){List<HashMap<String, Object>> result = new ArrayList<>();List<HashMap<String, Object>> warnTp = baseMapper.findWarnTp();for (HashMap<String,Object> hm :warnTp){String gj_acids = (String)hm.get("gj_acids");String[] split = gj_acids.split("-");System.out.println(split.length);if(split.length>=2) {Integer warn = baseMapper.getWarn(split[0], split[1]);if(warn >=2){result.add(hm);}}}return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS,result);}
}
4)创建com.qrsoft.service.WarnSimilarHistoryService类,内容如下:
package com.qrsoft.service;import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.qrsoft.common.Result;
import com.qrsoft.common.ResultConstants;
import com.qrsoft.entity.MultiRadar;
import com.qrsoft.entity.WarnSimilarHistory;
import com.qrsoft.mapper.MultiRadarMapper;
import com.qrsoft.mapper.WarnSimilarHistoryMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;@Service
public class WarnSimilarHistoryService extends ServiceImpl<WarnSimilarHistoryMapper, WarnSimilarHistory> {@Autowiredprivate MultiRadarMapper multiRadarMapper;/*** 查询相似航班告警*/public Result findWarnSimilarHistory(){List<WarnSimilarHistory> warnSimilarHistory = baseMapper.findWarnSimilarHistory();return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS,warnSimilarHistory);}/*** 根据扇区号查询相似航班*/public Result findWarnSimilarOfATC(String sectorName){QueryWrapper<MultiRadar> queryWrapper = new QueryWrapper<>();queryWrapper.eq("section",sectorName);List<MultiRadar> list = multiRadarMapper.selectList(queryWrapper);List<Map<String,String>> result = new ArrayList<>();System.out.println(list);for(MultiRadar m1 :list){for(MultiRadar m2:list){String acid1 = m1.getAcid();String substring = acid1.substring(0,3);String acid2 = m2.getAcid();if(acid2.startsWith(substring)){if(acid1.equals(acid2)){break;}HashMap<String, String> res = new HashMap<>();res.put("gj", acid1 + "-" + acid2);res.put("gjSector",sectorName);result.add(res);break;}}}return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS,result);}/*** 查询航班数量*/public Result findWarnSimilarOfATCCount(String sectorName){Result warnSimilarOfATC = this.findWarnSimilarOfATC(sectorName);Object result = warnSimilarOfATC.getResult();List<?> result1 = (List<?>) result;int size = result1.size();return new Result(ResultConstants.SUCCESS, ResultConstants.C_SUCCESS,size);}
}
- 编写对应的Mapper数据访问类,包括以下几个类:
| 类/接口 | 作用 |
|---|---|
| com.qrsoft.mapper.AtcMapper | 扇区操作的数据访问类 |
| com.qrsoft.mapper.MultiRadarMapper | 综合航迹数据查询的数据访问类 |
| com.qrsoft.mapper.WarnFlightHistoryMapper | 年度告警数据查询的数据访问类 |
| com.qrsoft.mapper.WarnSimilarHistoryMapper | 航班告警数据查询的数据访问类 |
1)com.qrsoft.mapper.AtcMapper类的内容如下:
@Mapper
public interface AtcMapper extends BaseMapper<Atc> {@Select("select PLAN_SECTOR_NAME,COUNT(*) as count from atc_number GROUP BY PLAN_SECTOR_NAME;")List<Atc> findSectorSortie();@Select("select EXECUTE_DATE from atc_number group by EXECUTE_DATE order by EXECUTE_DATE desc limit 19;")List<String> findATCTime();@Select("select PLAN_SECTOR_NAME,count(*) as count from atc_number where EXECUTE_DATE = #{executeTime} and PLAN_SECTOR_NAME = #{sectorName}")Atc findATCTime2(String executeTime,String sectorName);
}
2)com.qrsoft.mapper.MultiRadarMapper类的内容如下:
@Mapper
public interface MultiRadarMapper extends BaseMapper<MultiRadar> {}
3)com.qrsoft.mapper.WarnFlightHistoryMapper类的内容如下:
@Mapper
public interface WarnFlightHistoryMapper extends BaseMapper<WarnFlightHistory> {@Select("SELECT gj_sector,COUNT(*) as gjCount FROM warnflighthistory_number GROUP BY gj_sector ORDER BY sum(count) desc LIMIT 11;")List<WarnFlightHistory> annualWarningAreaStatistics();@Select("select gj_name,count(*) as gjCount from warnflighthistory_number group by gj_name;")List<WarnFlightHistory> annualWarningStatisticsByCategory();@Select("select gj_type,gj_id,gj_msg_type,gj_track_num1,gj_track_num2,gj_distinct,gj_radian,gj_name,gj_distinct_bz,gj_city,gj_date,gj_acids,gj_num1_long,gj_num1_lat,gj_num2_long,gj_num2_lat from warntp_number;")List<HashMap<String,Object>> findWarnTp();@Select("select count(*) from multiradar_number where `ACID` IN (#{acid},#{bcid});")Integer getWarn(@Param("acid") String acid, @Param("bcid") String bcid);
}
4)创建com.qrsoft.mapper.WarnSimilarHistoryMapper类,内容如下:
package com.qrsoft.mapper;import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.qrsoft.entity.WarnSimilarHistory;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.annotations.Select;import java.util.List;@Mapper
public interface WarnSimilarHistoryMapper extends BaseMapper<WarnSimilarHistory> {@Select("SELECT gj_sector,gj_acid FROM warnsimilarhistory_number ORDER BY id DESC LIMIT 4;")List<WarnSimilarHistory> findWarnSimilarHistory();@Select("SELECT gj_sector,gj_acid FROM warnsimilarhistory_number where gj_sector = #{sectorName} ORDER BY id DESC LIMIT 4;")List<WarnSimilarHistory> findWarnSimilarOfATC(@Param("sectorName") String sectorName);
}
- 涉及的数据实体类包括(在前面的任务中已经创建过,此处只需确认一下是否存在):
| 类/接口 | 作用 |
|---|---|
| com.qrsoft.entity.Atc | 扇区对应的实体类 |
| com.qrsoft.entity.MultiRadar | 雷达对应的实体类 |
| com.qrsoft.entity.WarnFlightHistory | 年度告警飞行历史记录对应的实体类 |
| com.qrsoft.entity.WarnSimilarHistory | 航班告警历史记录对应的实体类 |
| com.qrsoft.entity.Company | 航空公司信息表对应的实体类 |
| com.qrsoft.common.Result | 返回结果类 |
| com.qrsoft.common.ResultConstants | 返回常量结果类 |
1)需要创建com.qrsoft.entity.WarnSimilarHistory类,用于航班告警历史记录对应的实体类,内容如下:
package com.qrsoft.entity;import com.baomidou.mybatisplus.annotation.TableField;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("warnsimilarhistory_number")
public class WarnSimilarHistory implements Serializable {private Integer id;@TableField(value = "gj_type")private String gjType;@TableField(value = "gj_id")private String gjDd;@TableField(value = "gj_msg_type")private String gjMsgType;@TableField(value = "gj_num")private String gjNum;@TableField(value = "gj_track_num")private String gjTrackNum;@TableField(value = "gj_sector")private String gjSector;@TableField(value = "gj_acid")private String gjAcid;@TableField(value = "gj_status")private String gjStatus;@TableField(value = "gj_city")private String gjCity;@TableField(value = "gj_date")private String gjDate;@TableField(value = "count")private Integer count;}
2)其他实体类在前面的任务中已经创建,这里不需要重复创建。
4、功能测试
- 启动后端程序:BigData-KongGuan

- 启动后端程序:BigData-Etl-KongGuan

- 启动前端程序

- 页面显示效果(下面图中是原企业项目的真实展示页面,在当前项目中由于数据集的特点,实际展示效果可能会有差异)

相关文章:
阶段三:项目开发---民航功能模块实现:任务24:航空实时监控
任务描述 内 容:地图展示、飞机飞行轨迹、扇区控制。航空实时监控,是飞机每秒发送坐标,经过终端转换实时发送给塔台,为了飞机位置的精准度,传输位置的密度很大,在地图位置显示不明显。本次为了案例展示效…...
手机容器化 安装docker
旧手机-基于Termux容器化 1、安装app 在手机上安装Termux或ZeroTermux(Termux扩展) 1.1 切换源 注:可以将termux进行换源,最好采用国内源,例如:清华源等 更新包列表和升级包(可选࿰…...
科普文:深入理解Mybatis
概叙 (1) JDBC JDBC(Java Data Base Connection,java数据库连接)是一种用于执行SQL语句的Java API,可以为多种关系数据库提供统一访问,它由一组用Java语言编写的类和接口组成.JDBC提供了一种基准,据此可以构建更高级的工具和接口,使数据库开发人员能够编写数据库应用程序。 优点…...
称重传感器有哪些种类
有关称重传感器的知识,称重传感器是众多传感器产品中的一种,也是很常用的传感器之一,那么称重传感器有哪些种类,称重传感器的分类方式是什么样的,一起来了解下。 称重传感器的分类 主要有六种称重传感器类型…...
程序员鱼皮的保姆级写简历指南第四弹,优秀简历参考
大家好,我是程序员鱼皮。做知识分享这些年来,我看过太多简历、也帮忙修改过很多的简历,发现很多同学是完全不会写简历的、会犯很多常见的问题,不能把自己的优势充分展示出来,导致措施了很多面试机会,实在是…...
UML建模案例分析-时序图和类图的对应关系
概念 简单地说,类图定义了系统中的对象,时序图定义了对象之间的交互。 例子 一个电子商务系统,会员可通过电子商务系统购买零件。具体功能需求如下: 会员请求结账时,系统验证会员的账户是否处于登录状态࿱…...
Java版Flink使用指南——从RabbitMQ中队列中接入消息流
大纲 创建RabbitMQ队列新建工程新增依赖编码设置数据源配置读取、处理数据完整代码 打包、上传和运行任务测试 工程代码 在《Java版Flink使用指南——安装Flink和使用IntelliJ制作任务包》一文中,我们完成了第一个小型Demo的编写。例子中的数据是代码预先指定的。而…...
Python酷库之旅-第三方库Pandas(013)
目录 一、用法精讲 31、pandas.read_feather函数 31-1、语法 31-2、参数 31-3、功能 31-4、返回值 31-5、说明 31-6、用法 31-6-1、数据准备 31-6-2、代码示例 31-6-3、结果输出 32、pandas.DataFrame.to_feather函数 32-1、语法 32-2、参数 32-3、功能 32-4、…...
Linux 高级 Shell 脚本编程:掌握 Shell 脚本精髓,提升工作效率
【Linux】 高级 Shell 脚本编程:掌握 Shell 脚本精髓,提升工作效率 Shell 脚本编程是 Linux 系统管理员和开发人员的必备技能。通过学习高级 Shell 脚本编程,你可以编写更高效、更灵活和更易于维护的脚本。本文将介绍 Shell 脚本编程中的函数…...
【ARMv8/v9 GIC 系列 1.5 -- Enabling the distribution of interrupts】
请阅读【ARM GICv3/v4 实战学习 】 文章目录 Enabling the distribution of interruptsGIC Distributor 中断组分发控制CPU Interface 中断组分发控制Physical LPIs 的启用Summary Enabling the distribution of interrupts 在ARM GICv3和GICv4体系结构中,中断分发…...
《mysql篇》--索引事务
索引 索引的介绍 索引是帮助MySQL高效获取数据的数据结构,是一种特殊的文件,包含着对数据表里所有记录的引用指针,因为索引本身也比较大,所以索引一般是存储在磁盘上的,索引的种类有很多,不过如果没有特殊…...
科研绘图系列:R语言STAMP图(STAMP Plot)
介绍 STAMP图(STAMP plot)并非一个广泛认知的、具有特定名称的图表类型,而是可能指在STAMP(Statistical Analysis of Metagenomic Profiles:“STAMP: statistical analysis of taxonomic and functional profiles”)软件使用过程中生成的各种统计和可视化图表的总称。ST…...
运维团队如何应对动环监控与IT监控分离的挑战
IT与机房动环监控的一体化是当下及未来的必然趋势,这一模式显著节省了运维过程中的时间与成本。一体化平台不仅消除了频繁切换系统的繁琐,更在一个统一界面上实现了多元化的管理运维功能,极大地提升了工作效率。 在机房升级或新建项目中&…...
深入解析大数据核心概念:数据平台、数据中台、数据湖与数据仓库的异同与应用
大数据领域内的诸多概念常常让人困惑,其中数据平台、数据中台、数据湖和数据仓库是最为关键的几个。 1. 数据平台 定义: 数据平台是一个综合性的技术框架,旨在支持整个数据生命周期的管理和使用。它包含数据采集、存储、处理、分析和可视化…...
开发指南040-业务操作日志
平台所有业务操作都存储在核心库,以便统一分析处理。各业务微服务通过feign调用核心日志服务。底层提供了API: <dependency><groupId>org.qlm</groupId><artifactId>qlm-api</artifactId><version>1.0-SNAPSHOT<…...
如何构建数据驱动的企业?爬虫管理平台是关键桥梁吗?
一、数据驱动时代:为何选择爬虫管理平台? 在信息爆炸的今天,数据驱动已成为企业发展的核心战略之一。爬虫管理平台,作为数据采集的第一站,它的重要性不言而喻。这类平台通过自动化手段,从互联网的各个角落…...
多线程Thread
线程Thread简介 任务、线程、金城、多线程 多任务:短时间切换不同得任务 多线程:通过同一条道路,增加道多条道路,提高使用率,解决堵塞问题 普通方法调多线程只有主线一台执行路径是主线程调run()方法,方…...
计算机网络之WPAN 和 WLAN
上一篇文章内容:无线局域网 1.WPAN(无线个人区域网) WPAN 是以个人为中心来使用的无线个人区域网,它实际上就是一个低功率、小范围、低速率和低价格的电缆替代技术。 (1) 蓝牙系统(Bluetooth) &#…...
TikTok海外运营,云手机多种变现方法
从现阶段来看,TikTok 的用户基数不断增长,已然成为全球创业者和品牌的全新竞争舞台。其用户数量近乎 20 亿,年轻用户占据主导,市场渗透率也逐年提高。不管是大型企业、著名品牌,还是个体创业者,都绝不能小觑…...
kubekey在ubuntu24实现kubernetes快速安装
基于Ubunut24.04安装 设置主机名 hostnamectl set-hostname kkmain hostnamectl set-hostname kknode1 hostnamectl set-hostname kknode2关闭swap sudo swapoff -a sudo sed -i s/.*swap.*/#&/ /etc/fstab安装kubekey export KKZONEcn curl -sfL https://get-kk.kubes…...
第19节 Node.js Express 框架
Express 是一个为Node.js设计的web开发框架,它基于nodejs平台。 Express 简介 Express是一个简洁而灵活的node.js Web应用框架, 提供了一系列强大特性帮助你创建各种Web应用,和丰富的HTTP工具。 使用Express可以快速地搭建一个完整功能的网站。 Expre…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
深入剖析AI大模型:大模型时代的 Prompt 工程全解析
今天聊的内容,我认为是AI开发里面非常重要的内容。它在AI开发里无处不在,当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗",或者让翻译模型 "将这段合同翻译成商务日语" 时,输入的这句话就是 Prompt。…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
Redis相关知识总结(缓存雪崩,缓存穿透,缓存击穿,Redis实现分布式锁,如何保持数据库和缓存一致)
文章目录 1.什么是Redis?2.为什么要使用redis作为mysql的缓存?3.什么是缓存雪崩、缓存穿透、缓存击穿?3.1缓存雪崩3.1.1 大量缓存同时过期3.1.2 Redis宕机 3.2 缓存击穿3.3 缓存穿透3.4 总结 4. 数据库和缓存如何保持一致性5. Redis实现分布式…...
8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂
蛋白质结合剂(如抗体、抑制肽)在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上,高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术,但这类方法普遍面临资源消耗巨大、研发周期冗长…...
376. Wiggle Subsequence
376. Wiggle Subsequence 代码 class Solution { public:int wiggleMaxLength(vector<int>& nums) {int n nums.size();int res 1;int prediff 0;int curdiff 0;for(int i 0;i < n-1;i){curdiff nums[i1] - nums[i];if( (prediff > 0 && curdif…...
令牌桶 滑动窗口->限流 分布式信号量->限并发的原理 lua脚本分析介绍
文章目录 前言限流限制并发的实际理解限流令牌桶代码实现结果分析令牌桶lua的模拟实现原理总结: 滑动窗口代码实现结果分析lua脚本原理解析 限并发分布式信号量代码实现结果分析lua脚本实现原理 双注解去实现限流 并发结果分析: 实际业务去理解体会统一注…...
Java入门学习详细版(一)
大家好,Java 学习是一个系统学习的过程,核心原则就是“理论 实践 坚持”,并且需循序渐进,不可过于着急,本篇文章推出的这份详细入门学习资料将带大家从零基础开始,逐步掌握 Java 的核心概念和编程技能。 …...
3403. 从盒子中找出字典序最大的字符串 I
3403. 从盒子中找出字典序最大的字符串 I 题目链接:3403. 从盒子中找出字典序最大的字符串 I 代码如下: class Solution { public:string answerString(string word, int numFriends) {if (numFriends 1) {return word;}string res;for (int i 0;i &…...
