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()) { // å¦æå·²ç»æè·ç¦»è¿è¿çç¹éåï¼åè¿éè¦å第ä¸ä¸ªç¹è¿è¡è·ç¦»å¤æï¼ // è§£å³å½è½¦è¾è¡é©¶é度è¿ä½æ¶ï¼è¿ç»ç¹çè·ç¦»é½è¿è¿å¯¼è´é½å¤å®ä¸ºåä¸ç¹çé®é¢ 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) // å½åºç°è½¬å¼¯ç¹æ¶ï¼æ¸ 空åå²è§åº¦ï¼å¹¶ä¸èå¼è½¬å¼¯ç¹ç¸å¯¹äºåä¸ä¸ªç¹çè§åº¦ï¼è§£å³ä¸ç§æç«¯æ åµï¼å½è¿ç»åºç°è½¬å¼¯ç¹æ¶ï¼å½ååæ ç¹ä¼è¢«åç¬åå²ä¸ºä¸æ®µï¼ 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 } } 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 @@ } /** * 计ç®åæ ç¹p2ç¸å¯¹äºp1çæ¹ä½è§ * @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 } /** * 纬度ç¸åæ¶ * è·ç¦»è½¬æ¢ä¸ºç»åº¦ */ 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 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 { 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) } }