阶段三:项目开发---民航功能模块实现:任务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 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()}} &ensp;{{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

  • 启动前端程序

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/784789.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

AI系统的PyTorch:TextGrad框架基于文本梯度实现大语言模型AI系统自优化!

AI系统的PyTorch&#xff1a;TextGrad框架基于文本梯度实现大语言模型AI系统自优化&#xff01; 原创 旺知识 旺知识 2024年07月07日 16:21 广东 人工智能&#xff08;AI&#xff09;正在经历一场范式转变&#xff0c;这一转变是由系统协调多个大型语言模型&#xff08;LLMs&…

51 单片机[7]:计时器

一、定时器 1. 定时器介绍 51单片机的定时器属于单片机的内部资源&#xff0c;其电路的连接和运转均在单片机内部完成。 定时器作用&#xff1a; &#xff08;1&#xff09;用于计时系统&#xff0c;可实现软件计时&#xff0c;或者使程序每隔一固定时间完成一项操作 &#…

【零基础】学JS之APIS(基于黑马)

喝下这碗鸡汤 披盔戴甲,一路勇往直前! 1. 什么是事件 事件是在编程时系统内发生的动作或者发生的事情 比如用户在网页上单击一个按钮 2. 什么是事件监听? 就是让程序检测是否有事件产生&#xff0c;一旦有事件触发&#xff0c;就立即调用一个函数做出响应&#xff0c;也称为 注…

【人工智能】—基于成都市各区(市)县租房价格预测建模研究

引言 随着城市化进程的加速&#xff0c;人口流动日益频繁&#xff0c;租房市场作为城市生活的重要组成部分&#xff0c;其价格波动对居民生活质量和城市经济发展具有显著影响。成都市&#xff0c;作为中国西部地区的经济、文化、交通和科技中心&#xff0c;近年来吸引了大量人…

5.Python学习:面向对象

1.面向对象和面向过程的区别 以下五子棋为例&#xff1a; 2.类和实例 &#xff08;1&#xff09;类是抽象的模板&#xff0c;实例是根据模板创建出来的具体的对象 &#xff08;2&#xff09;比如人类就是一个类&#xff0c;刘亦菲就是人类的一个实例 2.1 新建类和类的实例…

王老师 linux c++ 通信架构 笔记(三)安装 xftp、

&#xff08;11&#xff09;调整 xshell 终端的字体大小&#xff0c;默认字体大小是 9 &#xff1a; &#xff08;12&#xff09; 共享文件夹 hgfs 的含义&#xff1a; &#xff08;13&#xff09;安装 xftp &#xff0c; 傻瓜式安装&#xff0c;出了修改下默认安装位置。 操作…

上位机图像处理和嵌入式模块部署(mcu项目2:串口日志记录器)

【 声明&#xff1a;版权所有&#xff0c;欢迎转载&#xff0c;请勿用于商业用途。 联系信箱&#xff1a;feixiaoxing 163.com】 淘宝上面有一个商品蛮好玩的&#xff0c;那就是日志记录器。说是记录器&#xff0c;其实就是一个模块&#xff0c;这个模块的输入是一个ttl串口&am…

18.动态规划之斐波那契数列模型1

1.第N个斐波那契数 1137. 第 N 个泰波那契数 - 力扣&#xff08;LeetCode&#xff09; 做题流程 1. 状态表示&#xff1a; 这道题可以【根据题目的要求】直接定义出状态表示&#xff1a; dp[i] 表示&#xff1a;第 i 个泰波那契数的值。 2. 状态转移方程&#xff1a; …

Social to Sales全链路,数说故事专享会开启出海新视角

————瞎出海&#xff0c;必出局 TikTok&#xff0c;这个充满活力的短视频平台&#xff0c;已经成为全球范围内不可忽视的电商巨头。就在6月8日&#xff0c;TikTok美区带货直播诞生了首个“百万大场”。在此之前&#xff0c;百万GMV被视为一道难以逾越的高墙。以TikTok为首的…

Zabbix分布式监控

目录 分布式监控架构 实现分布式监控的步骤 优点和应用场景 安装Zabbix_Proxy Server端Web页面配置 测试 Zabbix 的分布式监控架构允许在大规模和地理上分散的环境中进行高效的监控。通过分布式监控&#xff0c;Zabbix 可以扩展其监控能力&#xff0c;支持大量主机和设备…

Android - 云游戏本地悬浮输入框实现

一、简述 云游戏输入法分两种情况,以云化原神为例,分为 云端输入法 和 本地输入法,运行效果如下: 云端输入法本地输入法云端输入法 就是运行在云端设备上的输入法,对于不同客户端来说(Android、iPhone),运行效果一致。 本地输入法 则是运行在用户侧设备上的输入法,对…

WordPress开发进群V2主题源码,多种引流方法,引私域二次变现

WordPress开发进群V2主题源码&#xff0c;多种引流方法&#xff0c;引私域二次变现 全新前端UI界面&#xff0c;多种前端交互特效让页面不再单调&#xff0c;进群页面群成员数&#xff0c;群成员头像名称&#xff0c;每次刷新页面随机更新不重复&#xff0c;最下面评论和点赞也…

C语言编程3:运算符,运算符的基本用法

C语言3&#x1f525;&#xff1a;运算符&#xff0c;运算符的基本用法 一、运算符&#x1f33f; &#x1f387;1.1 定义 运算符是指进行运算的动作&#xff0c;比如加法运算符"“&#xff0c;减法运算符”-" 算子是指参与运算的值&#xff0c;这个值可能是常数&a…

4.动态SQL(if,choose,where,set,trim,foreach遍历)的使用+$和#的区别

文章目录 动态sql一、动态sql1.if条件判断2、choose、when、otherwise3、where标签4、set标签5、trim标签1)替代where标签效果2) 生成set标签效果 6、foreach迭代遍历1)属性 7.SQL标签-提取重用的SQL代码片段8、bind标签9.MyBatis中${}和#{}的区别: 动态sql 一、动态sql 常见…

React -- useState状态更新异步特性——导致获取值为旧值的问题

useState状态异步更新 问题导致的原因解决办法进一步分析后续遇到的新问题 问题 const [isSelecting, setIsSelecting] useState(false);useEffect(() > {const handleKeyDown (event) > {if (event.key Escape) {if(isSelectingRef){//.......setIsSelecting(!isSele…

js使用proxy代理监听控制事件

本文为proxy代理的实例应用&#xff0c;有关代理的内容可以参考&#xff1a; js语法---理解反射Reflect对象和代理Proxy对象 监听事件 要监听dom元素的事件&#xff0c;我们会采用回调触发的方式来执行操作&#xff0c; 而触发事件的过程很明显是一个异步操作&#xff0c;异…

Oracle中EXIT Statement用于终止循环语句的关键字

Oracle的EXIT Statement是PL/SQL编程语言中用于终止循环语句的关键字。它有两种主要形式&#xff1a;无条件EXIT和条件EXIT WHEN。以下是对Oracle EXIT Statement的详细解释&#xff1a; 1. 无条件EXIT 语法&#xff1a;EXIT; 作用&#xff1a;无条件地终止当前循环。当程序执…

【咨询】企业数字档案馆(室)建设方案-模版范例

导读&#xff1a;本模版来源某国有大型医药行业集团企业数字档案馆&#xff08;室&#xff09;建设方案&#xff08;一期300W、二期250W&#xff09;&#xff0c;本人作为方案的主要参与者&#xff0c;总结其中要点给大家参考。 目录 1、一级提纲总览 2、项目概述 3、总体规…

办公必备——ONLYOFFICE8.1版本桌面编辑器

一、介绍ONLYOFFICE ONLYOFFICE是一款免费的开源办公软件&#xff0c;它可以让你创建、编辑和分享文档、表格和演示文稿。就像微软的Office一样&#xff0c;但它是完全免费的&#xff0c;而且可以在多种设备上使用&#xff0c;包括电脑和手机。它还支持多人同时在线编辑文档&am…

SpringCloud 负载均衡

目录 一、负载均衡 1、问题 2、什么是负载均衡 服务端负载均衡 客户端负载均衡 二、Spring Cloud LoadBalance 1、使用 Spring Cloud LoadBalance 2、负载均衡策略 3、LoadBalancer 原理 一、负载均衡 1、问题 我们来看一下前面写的代码&#xff1a; List<Serv…