29 changed files with 1178 additions and 275 deletions
@ -1,2 +1,18 @@ |
|||
# ams-social |
|||
|
|||
- 框架/依赖说明 |
|||
- JDK17 |
|||
- SpringBoot 3.1.7 |
|||
- sa-token 登陆认证 |
|||
- mapstruct、orika 对象拷贝 |
|||
- mybatis-plus 数据持久化 |
|||
- easy-excel excel导入导出 |
|||
- hikari 数据库连接池 |
|||
- openapi 注解生成接口文档 |
|||
- rabbitmq 消息队列 |
|||
- redis 缓存 |
|||
- actuator/prometheus 监控 |
|||
- huawei obs 对象存储 |
|||
- fastjson2/gson/jackson json |
|||
- maven-pmd-plugin 静态代码检查插件 |
|||
- 功能说明 |
@ -0,0 +1,48 @@ |
|||
package com.jiagutech.ams.controller; |
|||
|
|||
import com.alibaba.fastjson2.JSONObject; |
|||
import com.jiagutech.ams.model.common.R; |
|||
import com.jiagutech.ams.model.request.MachImage; |
|||
import com.jiagutech.ams.model.request.RealTimeData; |
|||
import com.jiagutech.ams.model.request.WorkRecord; |
|||
import com.jiagutech.ams.service.MessageDataService; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.web.bind.annotation.*; |
|||
|
|||
import java.util.List; |
|||
|
|||
@Slf4j |
|||
@RestController |
|||
@RequiredArgsConstructor |
|||
@RequestMapping("/MessageDataService") |
|||
public class MessageDataController { |
|||
|
|||
private final MessageDataService messageDataService; |
|||
|
|||
@PostMapping("/machineryRealtimeData") |
|||
public R addRealTimeData(@RequestBody List<RealTimeData> realTimeDatas) { |
|||
//if (checkSign(request,response)) {
|
|||
log.info("RealTimeData:{}", JSONObject.toJSONString(realTimeDatas)); |
|||
messageDataService.addRealTimeData(realTimeDatas); |
|||
return R.ok("success"); |
|||
|
|||
} |
|||
|
|||
@PostMapping(value = "/machineryImage", consumes = "multipart/form-data") |
|||
public R machineryImage(@ModelAttribute MachImage machImage) { |
|||
|
|||
log.info("workRecord:{}", machImage.getVehicleId()); |
|||
messageDataService.machineryImage(machImage); |
|||
return R.ok("success"); |
|||
|
|||
} |
|||
|
|||
@PostMapping("/machineryAreaDataList") |
|||
public R addWorkData(@RequestBody List<WorkRecord> workRecords) { |
|||
log.info("WorkRecord:{}", JSONObject.toJSONString(workRecords)); |
|||
messageDataService.addWorkData(workRecords); |
|||
|
|||
return R.ok("success"); |
|||
} |
|||
} |
@ -0,0 +1,15 @@ |
|||
package com.jiagutech.ams.model; |
|||
|
|||
import com.jiagutech.ams.model.request.MachImage; |
|||
import org.mapstruct.Mapper; |
|||
import org.mapstruct.factory.Mappers; |
|||
|
|||
@Mapper |
|||
public interface TrackImageMapping { |
|||
|
|||
TrackImageMapping INTANCE = Mappers.getMapper(TrackImageMapping.class); |
|||
|
|||
|
|||
TrackImageVO convertByMachImage(MachImage machImage); |
|||
|
|||
} |
@ -0,0 +1,20 @@ |
|||
package com.jiagutech.ams.model; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class ZhongNongTrack { |
|||
|
|||
private String simNo; |
|||
|
|||
private String sendTime; |
|||
|
|||
private Float velocity; |
|||
|
|||
private Double latitude; |
|||
|
|||
private Double longitude; |
|||
|
|||
private Integer direction; |
|||
|
|||
} |
@ -0,0 +1,21 @@ |
|||
package com.jiagutech.ams.model.request; |
|||
|
|||
import lombok.Data; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
@Data |
|||
public class MachImage { |
|||
|
|||
// 设备Id
|
|||
private String vehicleId; |
|||
// 时间戳
|
|||
private long timestamp; |
|||
// 纬度,最多保留7位小数(CGCS2000 坐标系)
|
|||
private double lat; |
|||
// 经度,最多保留7位小数(CGCS2000 坐标系)
|
|||
private double lng; |
|||
|
|||
private MultipartFile image; |
|||
|
|||
private String imageUrl; |
|||
} |
@ -0,0 +1,29 @@ |
|||
package com.jiagutech.ams.model.request; |
|||
|
|||
import lombok.Data; |
|||
|
|||
@Data |
|||
public class RealTimeData { |
|||
|
|||
// 设备Id
|
|||
private String vehicleId; |
|||
// 时间戳
|
|||
private long timestamp; |
|||
// 纬度,最多保留7位小数(CGCS2000 坐标系)
|
|||
private double lat; |
|||
// 经度,最多保留7位小数(CGCS2000 坐标系)
|
|||
private double lng; |
|||
// 状态[1位数字](0=不工作,1=工作,3=合格)
|
|||
private int status; |
|||
// 幅宽(米)[浮点数,最多保留两位小数]
|
|||
private Double breadth; // 可选字段
|
|||
// 速度(千米/小时<km/h>)[浮点数,最多保留一位小数]
|
|||
private Double velocity; // 可选字段
|
|||
// 耕深(厘米)[浮点数,最多保留一位小数]
|
|||
private Double deep; // 可选字段
|
|||
// 流量(脉冲值)[浮点数,最多保留一位小数]
|
|||
// 流量(脉冲值)[浮点数,最多保留一位小数]
|
|||
private Double flow; // 可选字段
|
|||
// 播种速度 粒/s转对应实体类
|
|||
private Double seeding; // 可选字段
|
|||
} |
@ -0,0 +1,41 @@ |
|||
package com.jiagutech.ams.model.request; |
|||
|
|||
import com.jiagutech.ams.model.Locus; |
|||
import lombok.Data; |
|||
|
|||
import java.util.List; |
|||
|
|||
// 主要的作业记录类
|
|||
@Data |
|||
public class WorkRecord { |
|||
// 达标面积(亩)
|
|||
private Double qualifiedWorkArea; |
|||
// 重复作业面积(亩)
|
|||
private Double repeatWorkArea; |
|||
// 作业面积(亩)
|
|||
private Double workArea; |
|||
// 开始时间,毫秒时间戳
|
|||
private Long startAt; |
|||
// 结束时间,毫秒时间戳
|
|||
private Long endAt; |
|||
// 设备Id
|
|||
private String vehicleId; |
|||
// 作业地点经度
|
|||
private Double longitude; |
|||
// 作业地点纬度
|
|||
private Double latitude; |
|||
// 耕深(厘米)
|
|||
private Double avgDeep; |
|||
// 合格率
|
|||
private Double qualifiedRate; |
|||
// 行驶里程(米)
|
|||
private Double distance; |
|||
// 作业类型,编码见附表1
|
|||
private Long module; |
|||
// 总流量(单位:L)
|
|||
private Double sumFlow; |
|||
// 总播种量 (单位:粒)
|
|||
private Integer seedingNumber; |
|||
// 地块轨迹
|
|||
private List<Locus> locus; |
|||
} |
@ -0,0 +1,75 @@ |
|||
package com.jiagutech.ams.rest; |
|||
|
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.alibaba.fastjson2.JSONObject; |
|||
import com.jiagutech.ams.model.ZhongNongTrack; |
|||
import io.swagger.v3.core.util.Json; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.beans.factory.annotation.Value; |
|||
import org.springframework.http.HttpEntity; |
|||
import org.springframework.http.HttpHeaders; |
|||
import org.springframework.http.MediaType; |
|||
import org.springframework.http.ResponseEntity; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.client.RestTemplate; |
|||
|
|||
import java.time.Instant; |
|||
import java.time.ZoneId; |
|||
import java.time.format.DateTimeFormatter; |
|||
import java.util.List; |
|||
|
|||
@Slf4j |
|||
@Component |
|||
@RequiredArgsConstructor |
|||
public class ZhongNongRestClient { |
|||
|
|||
|
|||
private final RestTemplate restTemplate; |
|||
|
|||
|
|||
@Value("${zhong-nong.client.base_url}") |
|||
private String baseUrl; |
|||
|
|||
public String getAccessToken(String keyId, String signature, long timestamp) { |
|||
// 设置请求头和请求体
|
|||
HttpHeaders headers = new HttpHeaders(); |
|||
headers.setContentType(MediaType.APPLICATION_JSON); |
|||
JSONObject body = new JSONObject(); |
|||
body.put("keyId", keyId); |
|||
body.put("signature", signature); |
|||
body.put("timestamp", timestamp); |
|||
HttpEntity<String> requestEntity = new HttpEntity<>(body.toJSONString(), headers); |
|||
ResponseEntity<String> response = restTemplate.postForEntity(baseUrl + "/ThirdParty/sso", requestEntity, String.class); |
|||
if (response.getStatusCode().is2xxSuccessful()) { |
|||
return response.getBody(); |
|||
} else { |
|||
log.error("获取token失败:{}", response); |
|||
return null; |
|||
} |
|||
|
|||
} |
|||
|
|||
|
|||
public List<ZhongNongTrack> getTracks(String accessToken, String simNo, long startTime, long endTime) { |
|||
// 设置请求头和请求体
|
|||
HttpHeaders headers = new HttpHeaders(); |
|||
headers.setContentType(MediaType.APPLICATION_JSON); |
|||
headers.set("concrete-token-id", accessToken); |
|||
JSONObject body = new JSONObject(); |
|||
body.put("simNo", simNo); |
|||
DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss") |
|||
.withZone(ZoneId.of("Asia/Shanghai")); |
|||
body.put("startTime", df.format(Instant.ofEpochMilli(startTime))); |
|||
body.put("endTime", df.format(Instant.ofEpochMilli(endTime))); |
|||
log.info(" get tracks params :{}", body); |
|||
HttpEntity<String> requestEntity = new HttpEntity<>(body.toJSONString(), headers); |
|||
ResponseEntity<String> response = restTemplate.postForEntity(baseUrl + "/MessageDataService/vehicleLocusList", requestEntity, String.class); |
|||
if (response.getStatusCode().is2xxSuccessful()) { |
|||
return JSON.parseArray(response.getBody(), ZhongNongTrack.class); |
|||
} else { |
|||
log.error("获取轨迹失败:{}", response); |
|||
return null; |
|||
} |
|||
} |
|||
} |
@ -0,0 +1,16 @@ |
|||
package com.jiagutech.ams.service; |
|||
|
|||
import com.jiagutech.ams.model.request.MachImage; |
|||
import com.jiagutech.ams.model.request.RealTimeData; |
|||
import com.jiagutech.ams.model.request.WorkRecord; |
|||
|
|||
import java.util.List; |
|||
|
|||
public interface MessageDataService { |
|||
|
|||
void addRealTimeData(List<RealTimeData> realTimeDatas); |
|||
|
|||
void machineryImage(MachImage machImage); |
|||
|
|||
void addWorkData(List<WorkRecord> workRecords); |
|||
} |
@ -0,0 +1,72 @@ |
|||
package com.jiagutech.ams.service; |
|||
|
|||
import com.alibaba.fastjson2.JSONObject; |
|||
import com.jiagutech.ams.model.Locus; |
|||
import com.jiagutech.ams.model.TrackDataJG; |
|||
import com.jiagutech.ams.model.TrackImageMapping; |
|||
import com.jiagutech.ams.model.UavSortie; |
|||
import com.jiagutech.ams.model.request.MachImage; |
|||
import com.jiagutech.ams.model.request.RealTimeData; |
|||
import com.jiagutech.ams.model.request.WorkRecord; |
|||
import com.jiagutech.ams.rest.TrackRestClient; |
|||
import com.jiagutech.ams.utils.HuaweiObs; |
|||
import lombok.Data; |
|||
import lombok.RequiredArgsConstructor; |
|||
import lombok.SneakyThrows; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.springframework.data.redis.core.StringRedisTemplate; |
|||
import org.springframework.stereotype.Service; |
|||
|
|||
import java.util.List; |
|||
import java.util.concurrent.TimeUnit; |
|||
|
|||
@Slf4j |
|||
@Service |
|||
@RequiredArgsConstructor |
|||
public class MessageDataServiceImpl implements MessageDataService { |
|||
private final StringRedisTemplate stringRedisTemplate; |
|||
private final HuaweiObs huaweiObs; |
|||
|
|||
private final JobService jobService; |
|||
|
|||
private final TrackRestClient trackRestClient; |
|||
|
|||
public void addRealTimeData(List<RealTimeData> realTimeDatas) { |
|||
for (RealTimeData realTimeData : realTimeDatas) { |
|||
TrackDataJG uavTrackDataRVO = new TrackDataJG(); |
|||
uavTrackDataRVO.setLat(realTimeData.getLat()); |
|||
uavTrackDataRVO.setLng(realTimeData.getLng()); |
|||
uavTrackDataRVO.setDroneId(realTimeData.getVehicleId()); |
|||
uavTrackDataRVO.setHvel(Float.parseFloat(String.valueOf(realTimeData.getVelocity()))); |
|||
uavTrackDataRVO.setTimeStamp(realTimeData.getTimestamp()); |
|||
String s = JSONObject.toJSONString(uavTrackDataRVO); |
|||
stringRedisTemplate.opsForValue().set("mtrack:" + realTimeData.getVehicleId(), s, 2, TimeUnit.MINUTES); |
|||
} |
|||
} |
|||
|
|||
|
|||
@SneakyThrows |
|||
public void machineryImage(MachImage machImage) { |
|||
String upload = huaweiObs.upload(machImage.getImage()); |
|||
if (upload != null) { |
|||
log.info("保存图片到ams,图片链接:{}", upload); |
|||
machImage.setImageUrl(upload); |
|||
machImage.setImage(null); |
|||
TrackImageMapping mapping = TrackImageMapping.INTANCE; |
|||
jobService.saveJobImages(mapping.convertByMachImage(machImage)); |
|||
} |
|||
|
|||
} |
|||
|
|||
@Override |
|||
public void addWorkData(List<WorkRecord> workRecords) { |
|||
for (WorkRecord workRecord : workRecords) { |
|||
List<Locus> locus = workRecord.getLocus(); |
|||
//轨迹插入
|
|||
if (locus.size() > 0) { |
|||
trackRestClient.saveTractor(locus); |
|||
} |
|||
} |
|||
} |
|||
|
|||
} |
@ -0,0 +1,264 @@ |
|||
package com.jiagutech.ams.utils; |
|||
|
|||
import cn.hutool.core.util.IdUtil; |
|||
import com.alibaba.fastjson2.JSON; |
|||
import com.obs.services.ObsClient; |
|||
import com.obs.services.model.*; |
|||
import jakarta.annotation.Resource; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
import org.apache.commons.io.IOUtils; |
|||
import org.springframework.stereotype.Component; |
|||
import org.springframework.web.multipart.MultipartFile; |
|||
|
|||
import java.io.ByteArrayInputStream; |
|||
import java.io.IOException; |
|||
import java.io.InputStream; |
|||
import java.util.Calendar; |
|||
import java.util.HashMap; |
|||
import java.util.Map; |
|||
|
|||
/** |
|||
* @author Lenovo |
|||
*/ |
|||
@Slf4j |
|||
@Component |
|||
public class HuaweiObs { |
|||
|
|||
//Access Key Id
|
|||
//private String ak = "Access Key Id";
|
|||
|
|||
//Secret Access Key
|
|||
//private String sk = "Secret Access Key";
|
|||
|
|||
//桶名称
|
|||
private String bucketName = "jg-iot"; |
|||
|
|||
// 终端节点访问Endpoint
|
|||
//private String endpoint = "Endpoint";
|
|||
|
|||
// 文件目录
|
|||
private String prifix = "/farm"; |
|||
|
|||
// 访问域名 在域名后面或文件目录前加“/”
|
|||
private String path = "/farm/"; |
|||
|
|||
|
|||
@Resource |
|||
private ObsClient obsClient; |
|||
|
|||
/** |
|||
* 文件上传 |
|||
* |
|||
* @param file |
|||
* @return |
|||
* @throws IOException |
|||
*/ |
|||
public String upload(MultipartFile file) throws IOException { |
|||
//ObsClient obsClient = null;
|
|||
|
|||
Calendar cal = Calendar.getInstance(); |
|||
int year = cal.get(Calendar.YEAR); |
|||
int month = cal.get(Calendar.MONTH); |
|||
int day = cal.get(Calendar.DATE); |
|||
String fileName = IdUtil.simpleUUID() + file.getOriginalFilename().substring(file.getOriginalFilename().indexOf(".")); |
|||
String objectName = prifix + "/" + year + "/" + month + "/" + day + "/" + fileName; |
|||
//obsClient = new ObsClient(ak, sk, endpoint);
|
|||
PutObjectResult response = obsClient.putObject(bucketName, objectName, file.getInputStream()); |
|||
log.info(JSON.toJSONString(response)); |
|||
// 可选:调用成功后,记录调用成功的HTTP状态码和服务端请求ID
|
|||
int statusCode = response.getStatusCode(); |
|||
|
|||
if (200 == statusCode) { |
|||
|
|||
return response.getObjectUrl(); |
|||
} |
|||
|
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 上传文件--流式 |
|||
* |
|||
* @param fileName 上传文件名称 |
|||
* @param is 文件流 |
|||
* @return |
|||
*/ |
|||
public String uploadFile(String fileName, InputStream is) throws IOException { |
|||
//ObsClient obsClient = null;
|
|||
try { |
|||
Calendar cal = Calendar.getInstance(); |
|||
int year = cal.get(Calendar.YEAR); |
|||
int month = cal.get(Calendar.MONTH); |
|||
int day = cal.get(Calendar.DATE); |
|||
String objectName = prifix + "/" + year + "/" + month + "/" + day + "/" + fileName; |
|||
//obsClient = new ObsClient(ak, sk, endpoint);
|
|||
HeaderResponse response = obsClient.putObject(bucketName, objectName, is); |
|||
log.info(JSON.toJSONString(response)); |
|||
// 可选:调用成功后,记录调用成功的HTTP状态码和服务端请求ID
|
|||
int statusCode = response.getStatusCode(); |
|||
if (200 == statusCode) { |
|||
String objectUrl = path + objectName; |
|||
return objectUrl; |
|||
} |
|||
} finally { |
|||
obsClient.close(); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 上传文件--字节数组 |
|||
* |
|||
* @param fileName 上传文件名称 |
|||
* @param fileType 文件路径 |
|||
* @param is 文件流 |
|||
* @return |
|||
*/ |
|||
public boolean uploadFileByte(String fileName, FileType fileType, byte[] is) { |
|||
//ObsClient obsClient = null;
|
|||
try { |
|||
String objectName = fileType.getType().concat("/").concat(fileName); |
|||
//obsClient = new ObsClient(ak, sk, endpoint);
|
|||
HeaderResponse response = obsClient.putObject(bucketName, objectName, new ByteArrayInputStream(is)); |
|||
// 可选:调用成功后,记录调用成功的HTTP状态码和服务端请求ID
|
|||
int statusCode = response.getStatusCode(); |
|||
if (200 == statusCode) { |
|||
return true; |
|||
} |
|||
obsClient.close(); |
|||
} catch (IOException e) { |
|||
log.info("文件上传失败:{}", e.getMessage(), e); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 下载文件 |
|||
* |
|||
* @param fileName 文件名称 |
|||
* @param fileType 文件路径 |
|||
* @return |
|||
*/ |
|||
public String getDownloadUrl(String fileName, FileType fileType) { |
|||
//ObsClient obsClient = null;
|
|||
//obsClient = new ObsClient(ak, sk, endpoint);
|
|||
// URL有效期,3600秒.5分钟
|
|||
long expireSeconds = 3600L; |
|||
String objectName = fileType.getType().concat("/").concat(fileName); |
|||
TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.GET, expireSeconds); |
|||
request.setBucketName(bucketName); |
|||
request.setObjectKey(objectName); |
|||
TemporarySignatureResponse response = obsClient.createTemporarySignature(request); |
|||
return response.getSignedUrl(); |
|||
} |
|||
|
|||
/** |
|||
* 获取上传地址 |
|||
* |
|||
* @param fileName 文件名称 |
|||
* @param fileType 文件路径 |
|||
* @return |
|||
*/ |
|||
public String getUploadUrl(String fileName, FileType fileType) { |
|||
try { |
|||
// 创建ObsClient实例
|
|||
//ObsClient obsClient = new ObsClient(ak, sk, endpoint);
|
|||
// URL有效期,3600秒
|
|||
long expireSeconds = 3600L; |
|||
Map<String, String> headers = new HashMap<String, String>(); |
|||
headers.put("Content-Type", "application/octet-stream"); |
|||
String objectName = fileType.getType().concat("/").concat(fileName); |
|||
TemporarySignatureRequest request = new TemporarySignatureRequest(HttpMethodEnum.PUT, expireSeconds); |
|||
request.setBucketName(bucketName); |
|||
request.setObjectKey(objectName); |
|||
request.setHeaders(headers); |
|||
TemporarySignatureResponse response = obsClient.createTemporarySignature(request); |
|||
return response.getSignedUrl(); |
|||
} catch (Exception e) { |
|||
log.error("获取上传地址异常:{}", e.getMessage(), e); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 下载文件返回字节数组 |
|||
* |
|||
* @param fileName 文件名称 |
|||
* @param fileType 文件路径 |
|||
* @return |
|||
*/ |
|||
public byte[] downloadFile(String fileName, FileType fileType) { |
|||
try { |
|||
InputStream inputStream = downloadFileInputStream(fileName, fileType); |
|||
byte[] bytes = IOUtils.toByteArray(inputStream); |
|||
return bytes; |
|||
} catch (Exception e) { |
|||
log.error("下载文件异常:{}", e.getMessage(), e); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
/** |
|||
* 上传视频 |
|||
* |
|||
* @param fileName 文件名称 |
|||
* @param fileType 文件路径 |
|||
* @return |
|||
*/ |
|||
public boolean uploadFileVideo(String fileName, FileType fileType, InputStream is) { |
|||
try { |
|||
String objectName = fileType.getType().concat("/").concat(fileName); |
|||
//ObsClient obsClient = new ObsClient(ak, sk, endpoint);
|
|||
// 添加 ContentType (添加后可在浏览器中直接浏览,而非下载链接)
|
|||
obsClient.putObject(bucketName, objectName, is); |
|||
obsClient.close(); |
|||
return true; |
|||
} catch (Exception e) { |
|||
log.error("上传视频文件异常:{}", e.getMessage(), e); |
|||
} |
|||
return false; |
|||
} |
|||
|
|||
/** |
|||
* 下载文件返回流式 |
|||
* |
|||
* @param fileName 文件名称 |
|||
* @param fileType 文件路径 |
|||
* @return |
|||
*/ |
|||
public InputStream downloadFileInputStream(String fileName, FileType fileType) { |
|||
try { |
|||
String objectName = fileType.getType().concat("/").concat(fileName); |
|||
// 用户拿到STS临时凭证后,通过其中的安全令牌(SecurityToken)和临时访问密钥(AccessKeyId和AccessKeySecret)生成OSSClient。
|
|||
//ObsClient obsClient = new ObsClient(ak, sk, endpoint);
|
|||
ObsObject obsObject = obsClient.getObject(bucketName, objectName); |
|||
obsClient.close(); |
|||
return obsObject.getObjectContent(); |
|||
} catch (Exception e) { |
|||
log.error("下载文件异常:{}", e.getMessage(), e); |
|||
} |
|||
return null; |
|||
} |
|||
|
|||
|
|||
public enum FileType { |
|||
TEST("TEST", "测试"); |
|||
|
|||
private String type; |
|||
private String desc; |
|||
|
|||
FileType(String type, String desc) { |
|||
this.type = type; |
|||
this.desc = desc; |
|||
} |
|||
|
|||
public String getType() { |
|||
return type; |
|||
} |
|||
|
|||
public String getDesc() { |
|||
return desc; |
|||
} |
|||
} |
|||
} |
|||
|
@ -0,0 +1,104 @@ |
|||
package com.jiagutech.ams.utils; |
|||
|
|||
import cn.hutool.core.collection.CollectionUtil; |
|||
import com.jiagutech.ams.model.TrackItem; |
|||
import lombok.extern.slf4j.Slf4j; |
|||
|
|||
import java.time.Instant; |
|||
import java.time.LocalDateTime; |
|||
import java.time.ZoneId; |
|||
import java.util.List; |
|||
import java.util.concurrent.atomic.AtomicReference; |
|||
|
|||
@Slf4j |
|||
public class TrackUtil { |
|||
|
|||
public static float calculateArea(List<TrackItem> trackList) { |
|||
try { |
|||
if (CollectionUtil.isEmpty(trackList)) { |
|||
return 0.0f; |
|||
} |
|||
List<FlightTrackerUtils.PointTemp> pointTemps = trackList.stream().map(item -> { |
|||
LocalDateTime localDateTime = LocalDateTime.ofInstant(Instant.ofEpochMilli(item.getTimestamp()), ZoneId.systemDefault()); |
|||
int speed = Math.round(item.getVelocity() * 5 / 18); |
|||
return new FlightTrackerUtils.PointTemp(localDateTime, item.getLat(), item.getLng(), speed); |
|||
}).toList(); |
|||
if (CollectionUtil.isEmpty(pointTemps)) { |
|||
return 0.0f; |
|||
} |
|||
return (float) FlightTrackerUtils.calculateArea(pointTemps); |
|||
} catch (Exception e) { |
|||
log.error("calculate area error", e); |
|||
return 0.0f; |
|||
} |
|||
} |
|||
|
|||
private static final double EARTH_RADIUS = 6371000; |
|||
|
|||
// 计算总作业面积
|
|||
public static double calculateArea(List<double[]> coordinates, double width) { |
|||
double totalDistance = 0.0; |
|||
|
|||
// 累加轨迹点之间的距离
|
|||
for (int i = 1; i < coordinates.size(); i++) { |
|||
double[] start = coordinates.get(i - 1); |
|||
double[] end = coordinates.get(i); |
|||
|
|||
totalDistance += haversineDistance(start[1], start[0], end[1], end[0]); |
|||
} |
|||
|
|||
// 作业面积 = 作业宽度 * 总距离 * 作业效率系数
|
|||
return width * totalDistance * 0.7 * 0.0015; // Convert to mu (亩)
|
|||
|
|||
} |
|||
|
|||
public static double haversineDistance(double lat1, double lon1, double lat2, double lon2) { |
|||
// 将纬度和经度从度转换为弧度
|
|||
double lat1Rad = Math.toRadians(lat1); |
|||
double lon1Rad = Math.toRadians(lon1); |
|||
double lat2Rad = Math.toRadians(lat2); |
|||
double lon2Rad = Math.toRadians(lon2); |
|||
|
|||
// 计算差值
|
|||
double deltaLat = lat2Rad - lat1Rad; |
|||
double deltaLon = lon2Rad - lon1Rad; |
|||
|
|||
// 应用Haversine公式
|
|||
double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) |
|||
+ Math.cos(lat1Rad) * Math.cos(lat2Rad) |
|||
* Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2); |
|||
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a)); |
|||
|
|||
// 返回距离(米)
|
|||
return EARTH_RADIUS * c; |
|||
} |
|||
// // Haversine公式计算两点之间的距离
|
|||
// public static double calculateDistance(double lat1, double lon1, double lat2, double lon2) {
|
|||
// double dLat = Math.toRadians(lat2 - lat1);
|
|||
// double dLon = Math.toRadians(lon2 - lon1);
|
|||
// double a = Math.sin(dLat / 2) * Math.sin(dLat / 2)
|
|||
// + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2))
|
|||
// * Math.sin(dLon / 2) * Math.sin(dLon / 2);
|
|||
// double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
|||
// return EARTH_RADIUS * c;
|
|||
// }
|
|||
|
|||
|
|||
public static float calcAreaByBlockData(List<TrackItem> pointTemps) { |
|||
AtomicReference<Double> breadth = new AtomicReference<>((double) 0); |
|||
List<double[]> coordinates = pointTemps.stream() |
|||
.filter(p -> p.getVelocity() > 0) |
|||
.filter(p -> p.getBreadth()!=null && p.getBreadth() > 0) |
|||
.map(item -> { |
|||
breadth.set(item.getBreadth()); |
|||
return new double[]{item.getLng(), item.getLat()}; |
|||
}).toList(); |
|||
return (float) calculateArea(coordinates, breadth.get()); |
|||
} |
|||
|
|||
public static float calcAreaByBlockDataNoFilter(List<TrackItem> pointTemps,double breadth) { |
|||
List<double[]> coordinates = pointTemps.stream() |
|||
.map(item -> new double[]{item.getLng(), item.getLat()}).toList(); |
|||
return (float) calculateArea(coordinates, breadth); |
|||
} |
|||
} |
@ -0,0 +1,30 @@ |
|||
package com.jiagutech.ams.config; |
|||
|
|||
|
|||
import com.obs.services.ObsClient; |
|||
import lombok.Data; |
|||
import lombok.NoArgsConstructor; |
|||
import org.springframework.boot.context.properties.ConfigurationProperties; |
|||
import org.springframework.context.annotation.Bean; |
|||
import org.springframework.context.annotation.Configuration; |
|||
|
|||
@Data |
|||
@Configuration |
|||
@NoArgsConstructor |
|||
@ConfigurationProperties(prefix = "huawei.obs") |
|||
public class ObsConfig { |
|||
|
|||
|
|||
private String endPoint; |
|||
private String ak; |
|||
private String sk; |
|||
|
|||
private String bucketName; |
|||
private String pathPrefix; |
|||
|
|||
@Bean |
|||
public ObsClient obsClient() { |
|||
ObsClient obsClient= new ObsClient(ak, sk, endPoint); |
|||
return obsClient; |
|||
} |
|||
} |
Loading…
Reference in new issue