pom.xml
@@ -97,7 +97,7 @@ <dependency> <groupId>com.google.code.gson</groupId> <artifactId>gson</artifactId> <version>2.8.5</version> <version>2.8.6</version> </dependency> <!--å页--> src/main/kotlin/com/flightfeather/uav/biz/dataprocess/RoadSegment.kt
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,67 @@ 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 = AMapService.coordinateConvert(avgGPS) // éè¿é«å¾·APIæ¥è¯¢åæ 对åºç路段 val segmentInfoList = mutableListOf<SegmentInfo>() gdGPS.forEachIndexed { i, pair -> 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 }) } // ç»æå ¥åº segmentInfoRep.insert(segmentInfoList) } } src/main/kotlin/com/flightfeather/uav/common/location/TrackSegment.kt
ÎļþÃû´Ó src/main/kotlin/com/flightfeather/uav/biz/dataprocess/TrackSegment.kt ÐÞ¸Ä @@ -1,9 +1,11 @@ package com.flightfeather.uav.biz.dataprocess package com.flightfeather.uav.common.location import com.flightfeather.uav.common.location.CoordinateUtil import com.flightfeather.uav.domain.entity.BaseRealTimeData import java.math.BigDecimal import kotlin.math.abs import kotlin.math.atan import kotlin.math.cos import kotlin.math.sin /** * èµ°èªè½¨è¿¹åå²åç±» @@ -13,10 +15,10 @@ object TrackSegment { // åæ ç¹é´æå°è·ç¦»ï¼åä½ç±³ private const val MIN_DISTANCE = 10 private const val MIN_DISTANCE = 6 // 两æ¡ç´çº¿å¤¹è§ä¸º90度æ¶ï¼è®¤ä¸ºåç´ãå®é æ åµä¸ï¼è§åº¦å 许æä¸å®åå·®ï¼å 许åå·®è§åº¦ private const val VERTICAL_OFFSET_DEG = 22.5 private const val VERTICAL_OFFSET_DEG = 45 /** * æç §é路对走èªè½¨è¿¹è¿è¡åå² @@ -34,13 +36,13 @@ val closeList = mutableListOf<BaseRealTimeData>() records.add(mutableListOf()) data.forEachIndexed { i, d -> if (records.size == 33) { if (records.size == 23) { println(records.size) } var isSame = false if (i > 0) { // åä¸ä¸ªææçæµç¹ val lastData = data[i - 1] var lastData = data[i - 1] // ç¡®ä¿ä¸¤ç¹åæ åæ³ if ((lastData.longitude != null && lastData.longitude != BigDecimal.ZERO) && (lastData.latitude != null && lastData.latitude != BigDecimal.ZERO) @@ -57,6 +59,7 @@ // å¦æå·²ç»æè·ç¦»è¿è¿çç¹éåï¼åè¿éè¦å第ä¸ä¸ªç¹è¿è¡è·ç¦»å¤æï¼ // è§£å³å½è½¦è¾è¡é©¶é度è¿ä½æ¶ï¼è¿ç»ç¹çè·ç¦»é½è¿è¿å¯¼è´é½å¤å®ä¸ºåä¸ç¹çé®é¢ val firstCloseData = closeList[0] // lastData = closeList.toList().avg() distance = CoordinateUtil.calculateDistance( firstCloseData.longitude!!.toDouble(), firstCloseData.latitude!!.toDouble(), d.longitude!!.toDouble(), d.latitude!!.toDouble()) @@ -69,27 +72,43 @@ ) isSame = if (lastDegList.isNotEmpty()) { var bool = true // åºç°è§åº¦æ¥è¿åç´ç¶æçæ¬¡æ° var unSameCount = 0 // æ¯è¾å½åæ¹ä½è§åä¸ä¸ç»æ¯ä¸ªæ¹ä½è§ç差弿¯å¦é½å¤äºèå´å for (lastDeg in lastDegList) { val diffDeg = abs(deg - lastDeg) // // åºç°è§åº¦æ¥è¿åç´ç¶æçæ¬¡æ° // 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) val avgDeg = avgDegree(lastDegList) val diffDeg = abs(deg - avgDeg) 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 = false } } // 彿¥è¿åç´çè§åº¦è¶ è¿ä¸ä¸ç»å¹³è¡è§åº¦çä¸åæ¶ï¼è®¤ä¸ºä»è¯¥ç¹è½¨è¿¹è½¬å¼¯ï¼æ¶é¤ä¸ªå«åæ ç¹ç±äºå®ä½è¯¯å·®å¯¼è´çé误影åï¼ bool = unSameCount < (lastDegList.size / 3 + 1) // å½åºç°è½¬å¼¯ç¹æ¶ï¼æ¸ 空åå²è§åº¦ï¼å¹¶ä¸èå¼è½¬å¼¯ç¹ç¸å¯¹äºåä¸ä¸ªç¹çè§åº¦ï¼è§£å³ä¸ç§æç«¯æ åµï¼å½è¿ç»åºç°è½¬å¼¯ç¹æ¶ï¼å½ååæ ç¹ä¼è¢«åç¬åå²ä¸ºä¸æ®µï¼ if (!bool) lastDegList.clear() if (!bool) { lastDegList.clear() } else { lastDegList.add(deg) } bool } else { // å½åæ ç¹å½¢æææè·¯å¾æ¶ï¼è®°å½ä¸ºä¸ä¸ä¸ªåæ ç¹ lastDegList.add(deg) true } closeList.clear() } else { closeList.add(d) isSame = true @@ -110,4 +129,40 @@ return records } /** * æ±è½¬åè§åº¦çåå¼ */ private fun avgDegree(degList: List<Double>): Double { if (degList.isEmpty()) return .0 //éç¨åä½ç¢éæ³æ±ååå¼ var u = .0//ä¸è¥¿æ¹ä½åéæ»å var v = .0//ååæ¹ä½åéæ»å var c = 0//æ°æ®è®¡æ° degList.forEach { val r = Math.toRadians(it) u += sin(r) v += cos(r) c++ } val avgU = u / c val avgV = v / c var a = atan(avgU / avgV) a = Math.toDegrees(a) /** * avgU>0;avgV>0: çå®è§åº¦å¤äºç¬¬ä¸è±¡éï¼ä¿®æ£å¼ä¸º+0° * avgU>0;avgV<0: çå®è§åº¦å¤äºç¬¬äºè±¡éï¼ä¿®æ£å¼ä¸º+180° * avgU<0;avgV<0: çå®è§åº¦å¤äºç¬¬ä¸è±¡éï¼ä¿®æ£å¼ä¸º+180° * avgU<0;avgV>0: çå®è§åº¦å¤äºç¬¬å象éï¼ä¿®æ£å¼ä¸º+360° */ a += if (avgV > 0) { if (avgU > 0) 0 else 360 } else { 180 } return a } } src/main/kotlin/com/flightfeather/uav/common/net/AMapService.kt
@@ -1,19 +1,106 @@ package com.flightfeather.uav.common.net import com.flightfeather.uav.common.exception.BizException import com.google.gson.Gson import com.google.gson.JsonElement import com.google.gson.JsonObject import com.google.gson.JsonParser import java.nio.charset.Charset /** * é«å¾·å°å¾Webæå¡API */ object AMapService { private const val TAG = "AMapService" private const val KEY = "520c5e5cf44c7793104e500cbf0ed711" private val httpMethod = HttpMethod("restapi.amap.com", 443, true) private val KEY = "" data class AMapAddress( val country: String, val province: String, val city: String, val citycode: String, val district: String, val adcode: String, val township: String, val towncode: String, val street: String, ) /** * å°çéç¼ç * @param location åæ ç¹ * @return æå¨è¡é */ fun reGeo(location:List<Pair<Double, Double>>) { httpMethod.get("v3/geocode/regeo", listOf( fun reGeo(location:Pair<Double, Double>):AMapAddress { val res = httpMethod.get("/v3/geocode/regeo", listOf( "key" to KEY, "location" to "${location.first},${location.second}" )) val obj = handleRes(res) try { val a = obj["regeocode"].asJsonObject["addressComponent"].asJsonObject return AMapAddress( a["country"].asString, a["province"].asString, "", a["citycode"].asString, a["district"].asString, a["adcode"].asString, a["township"].asString, a["towncode"].asString, a["streetNumber"].asJsonObject["street"].asString, ) } catch (e: Exception) { throw BizException("é«å¾·APIåæ è½¬æ¢é误ï¼${e.message}") } } /** * åæ è½¬æ¢ * @param locations åå§åæ * @param coordsys ååæ ç³»ï¼å¯éå¼ï¼gps;mapbar;baidu;autonavi(ä¸è¿è¡è½¬æ¢) */ fun coordinateConvert(locations: List<Pair<Double, Double>>, coordsys:String="gps"): List<Pair<Double, Double>> { val res = httpMethod.get("/v3/assistant/coordinate/convert", listOf( "key" to KEY, "locations" to locations.joinToString("|") { "${it.first},${it.second}" }, "coordsys" to coordsys )) val obj = handleRes(res) try { return obj["locations"].asString.split(";").map { val l = it.split(",") l[0].toDouble() to l[1].toDouble() } } catch (e: Exception) { throw BizException("é«å¾·APIåæ è½¬æ¢é误ï¼${e.message}") } } private fun handleRes(res: HttpMethod.MyResponse):JsonObject { if (res.success) { // val str = if (res.m.responseCharSet == "utf-8") { // res.m.responseBodyAsString // } else { // String(res.m.responseBody, Charset.forName("utf-8")) // } val str = res.m.responseBodyAsString val json = JsonParser.parseString(str) return resCheck(json) } else { throw BizException("é«å¾·APIç½è·¯é¾æ¥é误ï¼ç¶æç ï¼${res.m.statusCode}") } } private fun resCheck(json: JsonElement): JsonObject { if (!json.isJsonObject) throw BizException("é«å¾·API失败ï¼è¿åå¼ä¸æ¯ä¸ä¸ªobject") val jo = json.asJsonObject if (jo["status"].asInt != 1) throw BizException("é«å¾·API失败ï¼é误${jo["info"]}") return jo } } src/main/kotlin/com/flightfeather/uav/common/net/HttpMethod.kt
@@ -19,7 +19,7 @@ data class MyResponse( val success: Boolean, val m: HttpMethodBase val m: HttpMethodBase, ) object Head { src/main/kotlin/com/flightfeather/uav/domain/entity/SegmentInfo.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,244 @@ package com.flightfeather.uav.domain.entity; import java.util.Date; import javax.persistence.*; @Table(name = "segment_info") public class SegmentInfo { @Id private Integer id; @Column(name = "mission_code") private String missionCode; @Column(name = "device_code") private String deviceCode; @Column(name = "start_time") private Date startTime; @Column(name = "end_time") private Date endTime; @Column(name = "district_code") private String districtCode; @Column(name = "district_name") private String districtName; @Column(name = "town_code") private String townCode; @Column(name = "towm_name") private String towmName; @Column(name = "province_code") private String provinceCode; @Column(name = "province_name") private String provinceName; @Column(name = "city_code") private String cityCode; @Column(name = "city_name") private String cityName; private String street; /** * @return id */ public Integer getId() { return id; } /** * @param id */ public void setId(Integer id) { this.id = id; } /** * @return mission_code */ public String getMissionCode() { return missionCode; } /** * @param missionCode */ public void setMissionCode(String missionCode) { this.missionCode = missionCode == null ? null : missionCode.trim(); } /** * @return device_code */ public String getDeviceCode() { return deviceCode; } /** * @param deviceCode */ public void setDeviceCode(String deviceCode) { this.deviceCode = deviceCode == null ? null : deviceCode.trim(); } /** * @return start_time */ public Date getStartTime() { return startTime; } /** * @param startTime */ public void setStartTime(Date startTime) { this.startTime = startTime; } /** * @return end_time */ public Date getEndTime() { return endTime; } /** * @param endTime */ public void setEndTime(Date endTime) { this.endTime = endTime; } /** * @return district_code */ public String getDistrictCode() { return districtCode; } /** * @param districtCode */ public void setDistrictCode(String districtCode) { this.districtCode = districtCode == null ? null : districtCode.trim(); } /** * @return district_name */ public String getDistrictName() { return districtName; } /** * @param districtName */ public void setDistrictName(String districtName) { this.districtName = districtName == null ? null : districtName.trim(); } /** * @return town_code */ public String getTownCode() { return townCode; } /** * @param townCode */ public void setTownCode(String townCode) { this.townCode = townCode == null ? null : townCode.trim(); } /** * @return towm_name */ public String getTowmName() { return towmName; } /** * @param towmName */ public void setTowmName(String towmName) { this.towmName = towmName == null ? null : towmName.trim(); } /** * @return province_code */ public String getProvinceCode() { return provinceCode; } /** * @param provinceCode */ public void setProvinceCode(String provinceCode) { this.provinceCode = provinceCode == null ? null : provinceCode.trim(); } /** * @return province_name */ public String getProvinceName() { return provinceName; } /** * @param provinceName */ public void setProvinceName(String provinceName) { this.provinceName = provinceName == null ? null : provinceName.trim(); } /** * @return city_code */ public String getCityCode() { return cityCode; } /** * @param cityCode */ public void setCityCode(String cityCode) { this.cityCode = cityCode == null ? null : cityCode.trim(); } /** * @return city_name */ public String getCityName() { return cityName; } /** * @param cityName */ public void setCityName(String cityName) { this.cityName = cityName == null ? null : cityName.trim(); } /** * @return street */ public String getStreet() { return street; } /** * @param street */ public void setStreet(String street) { this.street = street == null ? null : street.trim(); } } src/main/kotlin/com/flightfeather/uav/domain/mapper/SegmentInfoMapper.java
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,9 @@ package com.flightfeather.uav.domain.mapper; import com.flightfeather.uav.domain.MyMapper; import com.flightfeather.uav.domain.entity.SegmentInfo; import org.apache.ibatis.annotations.Mapper; @Mapper public interface SegmentInfoMapper extends MyMapper<SegmentInfo> { } src/main/kotlin/com/flightfeather/uav/domain/repository/SegmentInfoRep.kt
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,18 @@ package com.flightfeather.uav.domain.repository import com.flightfeather.uav.domain.entity.SegmentInfo import com.flightfeather.uav.domain.mapper.SegmentInfoMapper import org.springframework.stereotype.Repository /** * 轨迹åéæ°æ®åºæä½ * @date 2024/7/4 * @author feiyu02 */ @Repository class SegmentInfoRep(private val segmentInfoMapper: SegmentInfoMapper) { fun insert(list: List<SegmentInfo>):Int { return segmentInfoMapper.insertList(list) } } src/main/kotlin/com/flightfeather/uav/lightshare/service/impl/RealTimeDataServiceImpl.kt
@@ -6,7 +6,7 @@ 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.common.location.TrackSegment import com.flightfeather.uav.domain.entity.* import com.flightfeather.uav.domain.mapper.* import com.flightfeather.uav.domain.repository.MissionRep src/main/resources/generator/generatorConfig.xml
@@ -47,7 +47,7 @@ </javaClientGenerator> <!-- è¦çæç表 tableNameæ¯æ°æ®åºä¸ç表åæè§å¾å domainObjectNameæ¯å®ä½ç±»å--> <!-- <table tableName="air_real_time_data" domainObjectName="RealTimeData" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>--> <table tableName="mission" domainObjectName="Mission" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/> <!-- <table tableName="mission" domainObjectName="Mission" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>--> <!-- <table tableName="el_minutevalue" domainObjectName="ElectricMinuteValue" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>--> <!-- <table tableName="el_company_device" domainObjectName="CompanyDevice" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>--> <!-- <table tableName="co_complaint" domainObjectName="Complaint" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>--> @@ -60,5 +60,7 @@ <!-- <table tableName="real_time_data_grid_min" domainObjectName="RealTimeDataGridMin" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>--> <!-- <table tableName="real_time_data_grid_opt" domainObjectName="RealTimeDataGridOpt" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>--> <!-- <table tableName="scene_info" domainObjectName="SceneInfo" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/>--> <table tableName="segment_info" domainObjectName="SegmentInfo" enableCountByExample="false" enableUpdateByExample="false" enableDeleteByExample="false" enableSelectByExample="false" selectByExampleQueryId="false"/> </context> </generatorConfiguration> src/main/resources/mapper/SegmentInfoMapper.xml
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,30 @@ <?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.flightfeather.uav.domain.mapper.SegmentInfoMapper"> <resultMap id="BaseResultMap" type="com.flightfeather.uav.domain.entity.SegmentInfo"> <!-- WARNING - @mbg.generated --> <id column="id" jdbcType="INTEGER" property="id" /> <result column="mission_code" jdbcType="VARCHAR" property="missionCode" /> <result column="device_code" jdbcType="VARCHAR" property="deviceCode" /> <result column="start_time" jdbcType="TIMESTAMP" property="startTime" /> <result column="end_time" jdbcType="TIMESTAMP" property="endTime" /> <result column="district_code" jdbcType="VARCHAR" property="districtCode" /> <result column="district_name" jdbcType="VARCHAR" property="districtName" /> <result column="town_code" jdbcType="VARCHAR" property="townCode" /> <result column="towm_name" jdbcType="VARCHAR" property="towmName" /> <result column="province_code" jdbcType="VARCHAR" property="provinceCode" /> <result column="province_name" jdbcType="VARCHAR" property="provinceName" /> <result column="city_code" jdbcType="VARCHAR" property="cityCode" /> <result column="city_name" jdbcType="VARCHAR" property="cityName" /> <result column="street" jdbcType="VARCHAR" property="street" /> </resultMap> <sql id="Base_Column_List"> <!-- WARNING - @mbg.generated --> id, mission_code, device_code, start_time, end_time, district_code, district_name, town_code, towm_name, province_code, province_name, city_code, city_name, street </sql> </mapper> src/test/kotlin/com/flightfeather/uav/biz/dataprocess/RoadSegmentTest.kt
¶Ô±ÈÐÂÎļþ @@ -0,0 +1,28 @@ package com.flightfeather.uav.biz.dataprocess import com.flightfeather.uav.common.exception.BizException import com.flightfeather.uav.domain.repository.MissionRep import org.junit.Test import org.junit.Assert.* import org.junit.runner.RunWith import org.springframework.beans.factory.annotation.Autowired import org.springframework.boot.test.context.SpringBootTest import org.springframework.test.context.junit4.SpringRunner @RunWith(SpringRunner::class) @SpringBootTest class RoadSegmentTest { @Autowired lateinit var roadSegment: RoadSegment @Autowired lateinit var missionRep: MissionRep @Test fun handle() { val mission = missionRep.findOne("SH-CN-20240514") ?: throw BizException("ä»»å¡ä¸åå¨") roadSegment.handle(mission) } }