Riku
2025-05-14 cf160e28026ed1ed8bea82701d66e79a1085c503
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
package com.flightfeather.uav.biz.dataprocess
 
import com.flightfeather.uav.common.location.TrackSegment
import com.flightfeather.uav.common.net.AMapService
import com.flightfeather.uav.domain.entity.BaseRealTimeData
import com.flightfeather.uav.domain.entity.Mission
import com.flightfeather.uav.domain.entity.SegmentInfo
import com.flightfeather.uav.domain.entity.avg
import com.flightfeather.uav.domain.repository.RealTimeDataRep
import com.flightfeather.uav.domain.repository.SegmentInfoRep
import org.springframework.stereotype.Component
import java.math.BigDecimal
 
/**
 * 走航轨迹按照路段进行分割
 * @date 2024/7/4
 * @author feiyu02
 */
@Component
class RoadSegment(
    private val realTimeDataRep: RealTimeDataRep,
    private val segmentInfoRep: SegmentInfoRep,
) {
 
    fun handle(mission: Mission) {
        // 获取数据分段
        val data = mutableListOf<BaseRealTimeData>()
        realTimeDataRep.fetchData(mission).forEach {
            // 去除无效经纬度
            if ((it.longitude != null && it.longitude != BigDecimal.ZERO)
                && (it.latitude != null && it.latitude != BigDecimal.ZERO)
            ) {
                data.add(it)
            }
        }
        val sData = TrackSegment.segmentWithRoad(data)
        // 根据每个分段的GPS坐标均值通过高德API转换为高德坐标
        val avgGPS = mutableListOf<Pair<Double, Double>>()
        sData.forEach {
            val d = it.avg()
            if (d.longitude != null && d.latitude != null) {
                avgGPS.add(d.longitude!!.toDouble() to d.latitude!!.toDouble())
            }
        }
        val gdGPS = mutableListOf<Pair<Double, Double>>()
        val _avgGPS = prepareForConvert(avgGPS)
        _avgGPS.forEach {
            gdGPS.addAll(AMapService.coordinateConvert(it))
        }
        // 通过高德API查询坐标对应的路段
        val segmentInfoList = mutableListOf<SegmentInfo>()
        gdGPS.forEachIndexed { i, pair ->
            Thread.sleep(400)
            val address = AMapService.reGeo(pair)
            segmentInfoList.add(SegmentInfo().apply {
                missionCode = mission.missionCode
                deviceCode = mission.deviceCode
                startTime = sData[i][0].dataTime
                endTime = sData[i].last().dataTime
                provinceName = address.province
                cityCode = address.citycode
                districtCode = address.adcode
                districtName = address.district
                townCode = address.towncode
                towmName = address.township
                street = address.street
            })
        }
        // 结果入库
        saveResult(segmentInfoList)
    }
 
    /**
     * 坐标转换分段准备
     * 高德API的坐标转换接口一次访问最多支持40对坐标
     */
    private fun prepareForConvert(gpsList: List<Pair<Double, Double>>): List<List<Pair<Double, Double>>> {
        val res = mutableListOf<List<Pair<Double, Double>>>()
        val maxLen = 40
        var start = 0
        var end = start + maxLen
        while (end < gpsList.size) {
            res.add(gpsList.subList(start, end))
            start += maxLen
            end += maxLen
        }
        if (start < gpsList.size) {
            res.add(gpsList.subList(start, gpsList.size))
        }
        return res
    }
 
    /**
     * 结果入库
     * 入库之前,将连续并属于同一道路的记录合并
     */
    private fun saveResult(segmentInfoList:List<SegmentInfo>) {
        val res = mutableListOf<SegmentInfo>()
        segmentInfoList.forEach { s ->
            // 判断当前记录和上个记录是否属于同一条道路
            if (res.isNotEmpty()) {
                val lastOne = res.last()
                // 若属于同一道路,合并
                if (lastOne.street == s.street) {
                    lastOne.endTime = s.endTime
                } else {
                    res.add(s)
                }
            } else {
                res.add(s)
            }
        }
        segmentInfoRep.insert(res)
    }
}