From c9bbee8bb47d6f383f9699b59c046ddc0cb464e9 Mon Sep 17 00:00:00 2001
From: feiyu02 <risaku@163.com>
Date: 星期三, 03 七月 2024 17:49:49 +0800
Subject: [PATCH] 1. 新增走航报告自动道路识别模块

---
 src/main/kotlin/com/flightfeather/uav/biz/dataprocess/TrackSegment.kt                    |  113 +++++++++++++++++++++++++++++++++++++
 src/main/kotlin/com/flightfeather/uav/lightshare/web/RealTimeDataController.kt           |    8 ++
 src/main/kotlin/com/flightfeather/uav/lightshare/service/RealTimeDataService.kt          |    2 
 src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt |   11 +++
 src/main/kotlin/com/flightfeather/uav/common/location/CoordinateUtil.kt                  |   32 +++++++++-
 5 files changed, 162 insertions(+), 4 deletions(-)

diff --git a/src/main/kotlin/com/flightfeather/uav/biz/dataprocess/TrackSegment.kt b/src/main/kotlin/com/flightfeather/uav/biz/dataprocess/TrackSegment.kt
new file mode 100644
index 0000000..abd56ec
--- /dev/null
+++ b/src/main/kotlin/com/flightfeather/uav/biz/dataprocess/TrackSegment.kt
@@ -0,0 +1,113 @@
+package com.flightfeather.uav.biz.dataprocess
+
+import com.flightfeather.uav.common.location.CoordinateUtil
+import com.flightfeather.uav.domain.entity.BaseRealTimeData
+import java.math.BigDecimal
+import kotlin.math.abs
+
+/**
+ * 璧拌埅杞ㄨ抗鍒嗗壊鍒嗙被
+ * @date 2024/7/3
+ * @author feiyu02
+ */
+object TrackSegment {
+
+    // 鍧愭爣鐐归棿鏈�灏忚窛绂伙紝鍗曚綅绫�
+    private const val MIN_DISTANCE = 10
+
+    // 涓ゆ潯鐩寸嚎澶硅涓�90搴︽椂锛岃涓哄瀭鐩淬�傚疄闄呮儏鍐典腑锛岃搴﹀厑璁告湁涓�瀹氬亸宸紝鍏佽鍋忓樊瑙掑害
+    private const val VERTICAL_OFFSET_DEG = 22.5
+
+    /**
+     * 鎸夌収閬撹矾瀵硅蛋鑸建杩硅繘琛屽垎鍓�
+     * 姝ゅ绠�鍖栬涓哄綋杩炵画鐨勫墠鍚庝袱娈佃建杩硅秼鍚戜簬涓�鏉$洿绾匡紙骞宠锛夋椂锛屽嵆涓哄湪鍚屼竴鏉¢亾璺笂锛涘綋鍏舵洿瓒嬪悜浜庡瀭鐩存椂锛屽嵆璁や负杞﹁締杩涜浜嗚浆鍚戯紝杩涘叆浜嗘柊鐨勯亾璺�
+     * 鍏蜂綋鐨勮绠楁柟娉曚负锛�
+     * 1. 姹傚嚭姣忎釜鍧愭爣鐐圭浉瀵逛簬鍓嶄竴涓潗鏍囩偣鍦ㄧ粡绾害鍧愭爣绯讳笅鍋忚浆鐨勮搴︼紙褰撳墠鍚庝袱涓潗鏍囩偣鐨勮窛绂昏繃杩戞椂锛岀洿鎺ヨ涓哄叾鍚屽睘浜庡悓涓�璺锛夛紱
+     * 2. 渚濇姣旇緝鍓嶅悗涓や釜鍋忚浆瑙掑害鐨勫樊鍊硷紝褰撳樊鍊艰搴﹀湪[45掳, 135掳]鎴朳225掳, 315掳]鑼冨洿鍐呮椂锛岃涓鸿溅杈嗚繘琛屼簡杞悜锛岃繘鍏ユ柊鐨勯亾璺紝鍚﹀垯渚濇棫澶勪簬鍚屼竴閬撹矾涓�
+     */
+    fun segmentWithRoad(data: List<BaseRealTimeData>): List<List<BaseRealTimeData>> {
+        val records = mutableListOf<MutableList<BaseRealTimeData>>()
+        if (data.isEmpty()) return records
+        // 璁板綍涓婁竴缁勬帴杩戝钩琛岀殑鍧愭爣鐐圭殑鍋忚浆瑙掑害
+        val lastDegList = mutableListOf<Double>()
+        // 璁板綍涓婁竴缁勮窛绂绘帴杩戠殑鍧愭爣鐐�
+        val closeList = mutableListOf<BaseRealTimeData>()
+        records.add(mutableListOf())
+        data.forEachIndexed { i, d ->
+            if (records.size == 33) {
+                println(records.size)
+            }
+            var isSame = false
+            if (i > 0) {
+                // 鍓嶄竴涓湁鏁堢洃娴嬬偣
+                val lastData = data[i - 1]
+                // 纭繚涓ょ偣鍧愭爣鍚堟硶
+                if ((lastData.longitude != null && lastData.longitude != BigDecimal.ZERO)
+                    && (lastData.latitude != null && lastData.latitude != BigDecimal.ZERO)
+                    && (d.longitude != null && d.longitude != BigDecimal.ZERO)
+                    && (d.latitude != null && d.latitude != BigDecimal.ZERO)
+                ) {
+                    // 璁$畻涓ょ偣璺濈锛岃繃杩戞椂鐩存帴璁や负鏄悓涓�涓偣锛屽悓涓�璺
+                    var distance = CoordinateUtil.calculateDistance(
+                        lastData.longitude!!.toDouble(), lastData.latitude!!.toDouble(),
+                        d.longitude!!.toDouble(), d.latitude!!.toDouble(),
+                    )
+                    // 璺濈杩囪繎鏃�, 灏嗚窛绂绘浛鎹负褰撳墠鐐瑰拰杩戠偣闆嗗悎涓殑绗竴涓偣鐨勮窛绂�
+                    if (distance < MIN_DISTANCE && closeList.isNotEmpty()) {
+                        // 濡傛灉宸茬粡鏈夎窛绂昏繃杩戠殑鐐归泦鍚堬紝鍒欒繕闇�瑕佸拰绗竴涓偣杩涜璺濈鍒ゆ柇锛�
+                        // 瑙e喅褰撹溅杈嗚椹堕�熷害杩囦綆鏃讹紝杩炵画鐐圭殑璺濈閮借繃杩戝鑷撮兘鍒ゅ畾涓哄悓涓�鐐圭殑闂
+                        val firstCloseData = closeList[0]
+                        distance = CoordinateUtil.calculateDistance(
+                            firstCloseData.longitude!!.toDouble(), firstCloseData.latitude!!.toDouble(),
+                            d.longitude!!.toDouble(), d.latitude!!.toDouble())
+                    }
+
+                    if (distance >= MIN_DISTANCE) {
+                        val deg = CoordinateUtil.getAngle(
+                            lastData.longitude!!.toDouble(), lastData.latitude!!.toDouble(),
+                            d.longitude!!.toDouble(), d.latitude!!.toDouble(),
+                        )
+                        isSame = if (lastDegList.isNotEmpty()) {
+                            var bool = true
+                            // 鍑虹幇瑙掑害鎺ヨ繎鍨傜洿鐘舵�佺殑娆℃暟
+                            var unSameCount = 0
+                            // 姣旇緝褰撳墠鏂逛綅瑙掑拰涓婁竴缁勬瘡涓柟浣嶈鐨勫樊鍊兼槸鍚﹂兘澶勪簬鑼冨洿鍐�
+                            for (lastDeg in lastDegList) {
+                                val diffDeg = abs(deg - lastDeg)
+                                if (diffDeg in (90.0 - VERTICAL_OFFSET_DEG)..(90.0 + VERTICAL_OFFSET_DEG)
+                                    || diffDeg in (270.0 - VERTICAL_OFFSET_DEG)..(270.0 + VERTICAL_OFFSET_DEG)
+                                ) {
+                                    unSameCount++
+                                }
+                            }
+                            // 褰撴帴杩戝瀭鐩寸殑瑙掑害瓒呰繃涓婁竴缁勫钩琛岃搴︾殑涓�鍗婃椂锛岃涓轰粠璇ョ偣杞ㄨ抗杞集锛堟秷闄や釜鍒潗鏍囩偣鐢变簬瀹氫綅璇樊瀵艰嚧鐨勯敊璇奖鍝嶏級
+                            bool = unSameCount < (lastDegList.size / 3 + 1)
+                            // 褰撳嚭鐜拌浆寮偣鏃讹紝娓呯┖鍘嗗彶瑙掑害锛屽苟涓旇垗寮冭浆寮偣鐩稿浜庡墠涓�涓偣鐨勮搴︼紙瑙e喅涓�绉嶆瀬绔儏鍐碉紝褰撹繛缁嚭鐜拌浆寮偣鏃讹紝褰撳墠鍧愭爣鐐逛細琚崟鐙垎鍓蹭负涓�娈碉級
+                            if (!bool) lastDegList.clear()
+                            bool
+                        } else {
+                            // 褰撳潗鏍囩偣褰㈡垚鏈夋晥璺緞鏃讹紝璁板綍涓轰笂涓�涓潗鏍囩偣
+                            lastDegList.add(deg)
+                            true
+                        }
+                    } else {
+                        closeList.add(d)
+                        isSame = true
+                    }
+                }
+                // 鍚﹀垯璁や负鍚屼竴璺
+                else {
+                    isSame = true
+                }
+            } else {
+                isSame = true
+            }
+
+            if (!isSame)
+                records.add(mutableListOf())
+            records.last().add(d)
+        }
+
+        return records
+    }
+}
\ No newline at end of file
diff --git a/src/main/kotlin/com/flightfeather/uav/common/location/CoordinateUtil.kt b/src/main/kotlin/com/flightfeather/uav/common/location/CoordinateUtil.kt
index 227c76e..185c2f1 100644
--- a/src/main/kotlin/com/flightfeather/uav/common/location/CoordinateUtil.kt
+++ b/src/main/kotlin/com/flightfeather/uav/common/location/CoordinateUtil.kt
@@ -1,13 +1,12 @@
 package com.flightfeather.uav.common.location
 
-import kotlin.math.PI
-import kotlin.math.cos
-import kotlin.math.sin
+import kotlin.math.*
 
 object CoordinateUtil {
 
     private const val Ea = 6378137 //璧ら亾鍗婂緞
     private const val Eb = 6356725 //鏋佸崐寰�
+    private const val EARTH_RADIUS = 6371000 // 鍦扮悆鍗婂緞锛屽崟浣嶄负绫�
 
     /**
      * 鏍规嵁鍧愭爣鐐广�佽窛绂诲拰瑙掑害锛岃幏鍙栧彟涓�涓潗鏍�
@@ -26,6 +25,33 @@
     }
 
     /**
+     * 璁$畻鍧愭爣鐐筽2鐩稿浜巔1鐨勬柟浣嶈
+     * @return 瑙掑害
+     */
+    fun getAngle(lon1: Double, lat1: Double, lon2: Double, lat2: Double): Double {
+        val deg2rad = PI / 180
+        val dlat = (lat2 - lat1) * deg2rad
+        val dlon = (lon2 - lon1) * deg2rad
+        val y = sin(dlon) * cos(lat2 * deg2rad)
+        val x = cos(lat1 * deg2rad) * sin(lat2 * deg2rad) - sin(lat1 * deg2rad) * cos(lat2 * deg2rad) * cos(dlon)
+        val angel = atan2(y, x)
+        return (angel * 180 / PI + 360) % 360
+    }
+
+    /**
+     * 璁$畻鍧愭爣鐐逛箣闂磋窛绂�
+     */
+    fun calculateDistance(lon1: Double, lat1: Double, lon2: Double, lat2: Double): Double {
+        val dLat = Math.toRadians(lat2 - lat1)
+        val dLon = Math.toRadians(lon2 - lon1)
+        val a = (sin(dLat / 2) * sin(dLat / 2)
+                + (cos(Math.toRadians(lat1)) * cos(Math.toRadians(lat2))
+                * sin(dLon / 2) * sin(dLon / 2)))
+        val c = 2 * atan2(sqrt(a), sqrt(1 - a))
+        return EARTH_RADIUS * c
+    }
+
+    /**
      * 绾害鐩稿悓鏃�
      * 璺濈杞崲涓虹粡搴�
      */
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/service/RealTimeDataService.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/service/RealTimeDataService.kt
index c5e725e..c9e7f16 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/service/RealTimeDataService.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/RealTimeDataService.kt
@@ -14,6 +14,8 @@
 
     fun getNextData(deviceCode: String, updateTime: String, page: Int?, perPage: Int?): BaseResponse<List<DataVo>>
 
+    fun getSegmentData(missionCode: String): List<List<DataVo>>
+
     fun importData(file: MultipartFile): BaseResponse<DataImportResult>
 
     fun importJinanData(code:String, file: MultipartFile): DataImportResult
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt
index f90a5e6..ffe64d4 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt
@@ -6,8 +6,11 @@
 import com.flightfeather.uav.common.utils.ExcelUtil
 import com.flightfeather.uav.common.utils.FileExchange
 import com.flightfeather.uav.biz.dataprocess.AverageUtil
+import com.flightfeather.uav.biz.dataprocess.TrackSegment
 import com.flightfeather.uav.domain.entity.*
 import com.flightfeather.uav.domain.mapper.*
+import com.flightfeather.uav.domain.repository.MissionRep
+import com.flightfeather.uav.domain.repository.RealTimeDataRep
 import com.flightfeather.uav.lightshare.bean.*
 import com.flightfeather.uav.lightshare.service.RealTimeDataService
 import com.flightfeather.uav.model.epw.EPWDataPrep
@@ -40,6 +43,8 @@
     private val realTimeDataGridOptMapper: RealTimeDataGridOptMapper,
     private val realTimeDataGridMinMapper: RealTimeDataGridMinMapper,
     private val missionMapper: MissionMapper,
+    private val missionRep: MissionRep,
+    private val realTimeDataRep: RealTimeDataRep,
 ) : RealTimeDataService {
 
     @Value("\${filePath}")
@@ -181,6 +186,12 @@
         example.orderBy("dataTime")
     }
 
+    override fun getSegmentData(missionCode: String): List<List<DataVo>> {
+        val mission = missionRep.findOne(missionCode) ?: throw BizException("浠诲姟涓嶅瓨鍦�")
+        val data = realTimeDataRep.fetchData(mission)
+        return TrackSegment.segmentWithRoad(data).map { it.map { b -> b.toDataVo() } }
+    }
+
     override fun importData(file: MultipartFile): BaseResponse<DataImportResult> {
         val f = ByteArrayInputStream(file.bytes)
         fileExchange.exchangeBoatData("0c0000000001", f).forEach {
diff --git a/src/main/kotlin/com/flightfeather/uav/lightshare/web/RealTimeDataController.kt b/src/main/kotlin/com/flightfeather/uav/lightshare/web/RealTimeDataController.kt
index 8c8034c..bdeb3b0 100644
--- a/src/main/kotlin/com/flightfeather/uav/lightshare/web/RealTimeDataController.kt
+++ b/src/main/kotlin/com/flightfeather/uav/lightshare/web/RealTimeDataController.kt
@@ -38,7 +38,7 @@
         @RequestPart("excel") file: MultipartFile,
     ) = realTimeDataService.importData(file)
 
-    @ApiOperation(value = "瀵煎叆闈欏畨鍖虹敓鎬佺幆澧冪洃娴嬬珯鐨勮蛋琛屾暟鎹�")
+    @ApiOperation(value = "瀵煎叆闈欏畨鍖虹敓鎬佺幆澧冪洃娴嬬珯鐨勮蛋鑸暟鎹�")
     @PostMapping("/import/jinan")
     fun importJinanData(
         @ApiParam("璁惧id") @RequestParam("code") code: String,
@@ -48,4 +48,10 @@
     @ApiOperation(value = "涓嬭浇闈欏畨鍖虹敓鎬佺幆澧冪洃娴嬬珯璧拌鏁版嵁瀵煎叆妯℃澘")
     @PostMapping("/import/jinan/download/template")
     fun downloadTemplate(@ApiIgnore response: HttpServletResponse) = realTimeDataService.downloadTemplate(response)
+
+    @ApiOperation(value = "鑾峰彇鎸夌収璺鍒嗗壊鐨勮蛋鑸暟鎹�")
+    @GetMapping("/sec/segment")
+    fun getSegmentData(
+        @ApiParam("浠诲姟id") @RequestParam("missionCode") missionCode: String,
+    ) = resPack { realTimeDataService.getSegmentData(missionCode) }
 }
\ No newline at end of file

--
Gitblit v1.9.3