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