任务描述
内 容:地图展示、飞机飞行轨迹、扇区控制。航空实时监控,是飞机每秒发送坐标,经过终端转换实时发送给塔台,为了飞机位置的精准度,传输位置的密度很大,在地图位置显示不明显。本次为了案例展示效果,对飞机位置重新进行了规划,结合百度地图,和数据清洗,展示到前台。
学 时: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 sectionE
String 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()}}  {{new
Date().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;"><span
style="margin-left: 25px">{{it.gjSector}}</span><span
style="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"><span
style="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.lng
this.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 {
@Autowired
private 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 {
@Autowired
private 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 {
@Autowired
private 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 {
@Autowired
private 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> {
@Autowired
private 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> {
@Autowired
private 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
- 启动前端程序
- 页面显示效果(下面图中是原企业项目的真实展示页面,在当前项目中由于数据集的特点,实际展示效果可能会有差异)