parent
559fdb5d96
commit
64335ed9d6
|
|
@ -6,7 +6,7 @@ import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import com.google.common.collect.Maps;
|
import com.google.common.collect.Maps;
|
||||||
import com.multictrl.common.utils.DateUtils;
|
import com.multictrl.common.utils.DateUtils;
|
||||||
import com.multictrl.common.utils.Spatial4jUtils;
|
import com.multictrl.common.utils.map.Spatial4jUtils;
|
||||||
import com.multictrl.modules.business.dao.FlightTaskDao;
|
import com.multictrl.modules.business.dao.FlightTaskDao;
|
||||||
import com.multictrl.modules.business.entity.FlightTaskEntity;
|
import com.multictrl.modules.business.entity.FlightTaskEntity;
|
||||||
import com.multictrl.modules.business.entity.MediaFileEntity;
|
import com.multictrl.modules.business.entity.MediaFileEntity;
|
||||||
|
|
|
||||||
|
|
@ -252,4 +252,31 @@ public class Utils {
|
||||||
double result = bytes / Math.pow(1024, exp);
|
double result = bytes / Math.pow(1024, exp);
|
||||||
return format(result, 2) + " " + pre + "B";
|
return format(result, 2) + " " + pre + "B";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将秒转换为分秒
|
||||||
|
*/
|
||||||
|
public static String calculateSeconds(long totalSeconds) {
|
||||||
|
// 处理负数
|
||||||
|
if (totalSeconds < 0) {
|
||||||
|
return "-" + calculateSeconds(-totalSeconds);
|
||||||
|
}
|
||||||
|
|
||||||
|
long hours = totalSeconds / 3600;
|
||||||
|
long minutes = (totalSeconds % 3600) / 60;
|
||||||
|
long seconds = totalSeconds % 60;
|
||||||
|
|
||||||
|
StringBuilder sb = new StringBuilder();
|
||||||
|
if (hours > 0) {
|
||||||
|
sb.append(hours).append('h');
|
||||||
|
}
|
||||||
|
if (minutes > 0) {
|
||||||
|
sb.append(minutes).append('m');
|
||||||
|
}
|
||||||
|
// 当所有部分都为0时,至少显示 "0s"
|
||||||
|
if (seconds > 0 || sb.isEmpty()) {
|
||||||
|
sb.append(seconds).append('s');
|
||||||
|
}
|
||||||
|
return sb.toString();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -68,11 +68,12 @@ public class TemplateUtils {
|
||||||
folder.addElement("wpml:globalUseStraightLine").setText("1");
|
folder.addElement("wpml:globalUseStraightLine").setText("1");
|
||||||
|
|
||||||
List<RouteWaypointDTO> waypointList = route.getRouteWaypointList();
|
List<RouteWaypointDTO> waypointList = route.getRouteWaypointList();
|
||||||
for (RouteWaypointDTO waypoint : waypointList) {
|
for (int i = 0; i < waypointList.size(); i++) {
|
||||||
|
RouteWaypointDTO waypoint = waypointList.get(i);
|
||||||
Element placemark = folder.addElement("Placemark");
|
Element placemark = folder.addElement("Placemark");
|
||||||
Element point = placemark.addElement("Point");
|
Element point = placemark.addElement("Point");
|
||||||
point.addElement("coordinates").setText(waypoint.getLongitude() + "," + waypoint.getLatitude());
|
point.addElement("coordinates").setText(waypoint.getLongitude() + "," + waypoint.getLatitude());
|
||||||
placemark.addElement("wpml:index").setText(waypoint.getWaypointSort() + "");
|
placemark.addElement("wpml:index").setText(i + "");
|
||||||
placemark.addElement("wpml:ellipsoidHeight").setText(waypoint.getFlightHeight() + "");
|
placemark.addElement("wpml:ellipsoidHeight").setText(waypoint.getFlightHeight() + "");
|
||||||
placemark.addElement("wpml:height").setText(waypoint.getFlightHeight() + "");
|
placemark.addElement("wpml:height").setText(waypoint.getFlightHeight() + "");
|
||||||
placemark.addElement("wpml:waypointSpeed").setText(waypoint.getFlightSpeed() + "");
|
placemark.addElement("wpml:waypointSpeed").setText(waypoint.getFlightSpeed() + "");
|
||||||
|
|
|
||||||
|
|
@ -59,11 +59,12 @@ public class WaylinesUtils {
|
||||||
|
|
||||||
//航点信息
|
//航点信息
|
||||||
List<RouteWaypointDTO> waypointList = route.getRouteWaypointList();
|
List<RouteWaypointDTO> waypointList = route.getRouteWaypointList();
|
||||||
for (RouteWaypointDTO waypoint : waypointList) {
|
for (int i = 0; i < waypointList.size(); i++) {
|
||||||
|
RouteWaypointDTO waypoint = waypointList.get(i);
|
||||||
Element placemark = folder.addElement("Placemark");
|
Element placemark = folder.addElement("Placemark");
|
||||||
Element point = placemark.addElement("Point");
|
Element point = placemark.addElement("Point");
|
||||||
point.addElement("coordinates").setText(waypoint.getLongitude() + "," + waypoint.getLatitude());
|
point.addElement("coordinates").setText(waypoint.getLongitude() + "," + waypoint.getLatitude());
|
||||||
placemark.addElement("wpml:index").setText(waypoint.getWaypointSort() + "");
|
placemark.addElement("wpml:index").setText(i + "");
|
||||||
placemark.addElement("wpml:executeHeight").setText(waypoint.getFlightHeight() + "");
|
placemark.addElement("wpml:executeHeight").setText(waypoint.getFlightHeight() + "");
|
||||||
placemark.addElement("wpml:waypointSpeed").setText(waypoint.getFlightSpeed() + "");
|
placemark.addElement("wpml:waypointSpeed").setText(waypoint.getFlightSpeed() + "");
|
||||||
//添加转弯模式/飞机偏航角模式
|
//添加转弯模式/飞机偏航角模式
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
||||||
|
package com.multictrl.common.utils.map;
|
||||||
|
|
||||||
|
import lombok.AllArgsConstructor;
|
||||||
|
import lombok.Data;
|
||||||
|
import lombok.NoArgsConstructor;
|
||||||
|
|
||||||
|
import java.util.Arrays;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 经纬度
|
||||||
|
*
|
||||||
|
* @author Sdy
|
||||||
|
* @since 1.0.0 2026/6/23
|
||||||
|
*/
|
||||||
|
|
||||||
|
@NoArgsConstructor
|
||||||
|
@AllArgsConstructor
|
||||||
|
@Data
|
||||||
|
public class GeoPoint {
|
||||||
|
|
||||||
|
private double longitude;
|
||||||
|
|
||||||
|
private double latitude;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 构造方法
|
||||||
|
*
|
||||||
|
* @param location 经纬度x,y
|
||||||
|
*/
|
||||||
|
public GeoPoint(String location) {
|
||||||
|
List<String> list = Arrays.asList(location.split(","));
|
||||||
|
this.longitude = Double.parseDouble(list.get(0));
|
||||||
|
this.latitude = Double.parseDouble(list.get(1));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,245 @@
|
||||||
|
package com.multictrl.common.utils.map;
|
||||||
|
|
||||||
|
import cn.hutool.http.HttpUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
|
import cn.hutool.json.JSONUtil;
|
||||||
|
import com.multictrl.common.exception.RenException;
|
||||||
|
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 地图数据工具
|
||||||
|
*
|
||||||
|
* @author Sdy
|
||||||
|
* @since 1.0.0 2026/6/23
|
||||||
|
*/
|
||||||
|
public class GeoUtils {
|
||||||
|
|
||||||
|
private static final int EARTH_RADIUS_IN_METERS = 6371000; // 地球半径,单位:米
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 创建wkt点
|
||||||
|
*
|
||||||
|
* @param longitude 精度
|
||||||
|
* @param latitude 维度
|
||||||
|
* @return 返回WKT格式的点,例如 "POINT(120.123456789 30.987654789)"
|
||||||
|
*/
|
||||||
|
public static String createPointWkt(Double longitude, Double latitude) {
|
||||||
|
if (longitude == null || latitude == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
// 判断是否是合法的数字
|
||||||
|
if (Double.isNaN(longitude) || Double.isNaN(latitude) ||
|
||||||
|
Double.isInfinite(longitude) || Double.isInfinite(latitude)) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
return String.format("POINT(%.9f %.9f)", longitude, latitude);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 解析 WKT 格式的 POINT 字符串,并根据指定的精度返回经度和纬度
|
||||||
|
*
|
||||||
|
* @param pointWkt WKT 格式的 POINT 字符串,例如 "POINT(120.123456789 30.987654789)"
|
||||||
|
* @return 包含经度和纬度的 GeoPoint 对象
|
||||||
|
*/
|
||||||
|
public static GeoPoint parsePointWkt(String pointWkt) {
|
||||||
|
if (pointWkt == null || !pointWkt.startsWith("POINT(") || !pointWkt.endsWith(")")) {
|
||||||
|
throw new RenException("经纬度解析错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
// 去掉 "POINT(" 和 ")",然后按空格分割
|
||||||
|
String content = pointWkt.substring(6, pointWkt.length() - 1);
|
||||||
|
String[] parts = content.split("\\s+");
|
||||||
|
|
||||||
|
if (parts.length != 2) {
|
||||||
|
throw new RenException("经纬度解析错误");
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
double longitude = Double.parseDouble(parts[0]);
|
||||||
|
double latitude = Double.parseDouble(parts[1]);
|
||||||
|
|
||||||
|
// 根据精度进行四舍五入
|
||||||
|
longitude = roundToPrecision(longitude, 9);
|
||||||
|
latitude = roundToPrecision(latitude, 9);
|
||||||
|
|
||||||
|
return new GeoPoint(longitude, latitude);
|
||||||
|
} catch (Exception e) {
|
||||||
|
throw new RenException("经纬度解析错误", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 四舍五入到指定的小数位数
|
||||||
|
*
|
||||||
|
* @param value 要四舍五入的值
|
||||||
|
* @param precision 小数点后保留的位数
|
||||||
|
* @return 四舍五入后的结果
|
||||||
|
*/
|
||||||
|
private static double roundToPrecision(double value, int precision) {
|
||||||
|
double scale = Math.pow(10, precision);
|
||||||
|
return Math.round(value * scale) / scale;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 使用 Haversine 公式计算两个经纬度之间的距离(单位:米)
|
||||||
|
*
|
||||||
|
* @param lat1 第一个点的纬度
|
||||||
|
* @param lon1 第一个点的经度
|
||||||
|
* @param lat2 第二个点的纬度
|
||||||
|
* @param lon2 第二个点的经度
|
||||||
|
* @return 距离(米)
|
||||||
|
*/
|
||||||
|
public static double calculateDistance(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 a = Math.pow(Math.sin((lat2Rad - lat1Rad) / 2), 2) +
|
||||||
|
Math.cos(lat1Rad) * Math.cos(lat2Rad) *
|
||||||
|
Math.pow(Math.sin((lon2Rad - lon1Rad) / 2), 2);
|
||||||
|
|
||||||
|
double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
|
||||||
|
return EARTH_RADIUS_IN_METERS * c;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 判断两点之间的距离是否在指定范围内(单位:米)
|
||||||
|
*
|
||||||
|
* @param lat1 第一个点的纬度
|
||||||
|
* @param lon1 第一个点的经度
|
||||||
|
* @param lat2 第二个点的纬度
|
||||||
|
* @param lon2 第二个点的经度
|
||||||
|
* @param minDistance 最小距离(单位:米)
|
||||||
|
* @param maxDistance 最大距离(单位:米)
|
||||||
|
* @return 是否在范围内
|
||||||
|
*/
|
||||||
|
public static boolean isDistanceInRange(double lat1, double lon1, double lat2, double lon2,
|
||||||
|
double minDistance, double maxDistance) {
|
||||||
|
double distance = calculateDistance(lat1, lon1, lat2, lon2);
|
||||||
|
return distance >= minDistance && distance <= maxDistance;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 将 "经度,纬度;经度,纬度" 格式的字符串转换为闭合的坐标数组(用于 GeoJSON Polygon)
|
||||||
|
*
|
||||||
|
* @param mapping 输入的坐标字符串
|
||||||
|
* @return 格式化后的 List<List<Double>>,每个内层 List 为 [经度, 纬度]
|
||||||
|
*/
|
||||||
|
public static List<List<Double>> parseCoordinates(String mapping) {
|
||||||
|
if (mapping == null || mapping.trim().isEmpty()) {
|
||||||
|
throw new RenException("面状范围不能为空");
|
||||||
|
}
|
||||||
|
|
||||||
|
String[] points = mapping.split(";");
|
||||||
|
List<List<Double>> coordinates = new ArrayList<>();
|
||||||
|
|
||||||
|
List<Double> firstPoint = null;
|
||||||
|
|
||||||
|
for (int i = 0; i < points.length; i++) {
|
||||||
|
String[] latLon = points[i].split(",");
|
||||||
|
if (latLon.length != 2) {
|
||||||
|
throw new RenException("无效坐标对: " + points[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
double lon = Double.parseDouble(latLon[0].trim());
|
||||||
|
double lat = Double.parseDouble(latLon[1].trim());
|
||||||
|
|
||||||
|
List<Double> point = List.of(lon, lat);
|
||||||
|
coordinates.add(point);
|
||||||
|
|
||||||
|
// 记录第一个点用于闭合
|
||||||
|
/*if (i == 0) {
|
||||||
|
firstPoint = point;
|
||||||
|
}*/
|
||||||
|
}
|
||||||
|
|
||||||
|
// 闭合多边形:最后一个点必须等于第一个点
|
||||||
|
/*if (firstPoint != null && !coordinates.get(coordinates.size() - 1).equals(firstPoint)) {
|
||||||
|
coordinates.add(firstPoint);
|
||||||
|
}*/
|
||||||
|
|
||||||
|
return coordinates;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 根据经纬度获取高程(单位:米)
|
||||||
|
*
|
||||||
|
* @param latitude 纬度(如 40.714728)
|
||||||
|
* @param longitude 经度(如 -73.998672)
|
||||||
|
* @return 海拔高度(米)
|
||||||
|
* @throws Exception 如果API请求失败或数据异常
|
||||||
|
*/
|
||||||
|
public static double getElevation(double latitude, double longitude) {
|
||||||
|
// 构建API请求URL(使用aster30m数据集)
|
||||||
|
String url = "https://api.opentopodata.org/v1/aster30m?locations=" + latitude + "," + longitude;
|
||||||
|
|
||||||
|
//发送HTTP请求
|
||||||
|
String response = HttpUtil.get(url);
|
||||||
|
|
||||||
|
// 解析JSON
|
||||||
|
JSONObject json = JSONUtil.parseObj(response);
|
||||||
|
if (!json.containsKey("results") || json.getJSONArray("results").isEmpty()) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// 返回第一个结果的高程值
|
||||||
|
return json.getJSONArray("results").getJSONObject(0).getDouble("elevation");
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 计算点数集合总时长
|
||||||
|
* 包含了上升时间
|
||||||
|
*/
|
||||||
|
public static long calculateFlightTime(List<JSONObject> waypoints) {
|
||||||
|
if (waypoints == null || waypoints.size() < 2) {
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
double totalSeconds = 0.0;
|
||||||
|
final double EARTH_RADIUS = 6371000.0; // 地球半径(米)
|
||||||
|
final double VERTICAL_SPEED = 2.0; // 垂直调整速度(m/s,大疆典型值)
|
||||||
|
|
||||||
|
for (int i = 1; i < waypoints.size(); i++) {
|
||||||
|
JSONObject prev = waypoints.get(i - 1);
|
||||||
|
JSONObject curr = waypoints.get(i);
|
||||||
|
|
||||||
|
// 获取位置和速度
|
||||||
|
double lat1 = prev.getDouble("latitude");
|
||||||
|
double lon1 = prev.getDouble("longitude");
|
||||||
|
double alt1 = prev.getDouble("altitude");
|
||||||
|
double lat2 = curr.getDouble("latitude");
|
||||||
|
double lon2 = curr.getDouble("longitude");
|
||||||
|
double alt2 = curr.getDouble("altitude");
|
||||||
|
double horizontalSpeed = prev.getDouble("speed"); // 水平速度(m/s)
|
||||||
|
|
||||||
|
// 1. 计算水平距离(地面投影,单位:米)
|
||||||
|
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;
|
||||||
|
|
||||||
|
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));
|
||||||
|
double groundDistance = EARTH_RADIUS * c;
|
||||||
|
|
||||||
|
// 2. 计算垂直高度差(绝对值)
|
||||||
|
double heightDiff = Math.abs(alt2 - alt1);
|
||||||
|
|
||||||
|
// 3. 计算两段飞行时间
|
||||||
|
double timeVertical = heightDiff / VERTICAL_SPEED; // 垂直调整时间
|
||||||
|
double timeHorizontal = groundDistance / horizontalSpeed; // 水平飞行时间
|
||||||
|
|
||||||
|
totalSeconds += timeVertical + timeHorizontal;
|
||||||
|
}
|
||||||
|
|
||||||
|
return Math.round(totalSeconds); // 四舍五入为整数秒
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
package com.multictrl.common.utils;
|
package com.multictrl.common.utils.map;
|
||||||
|
|
||||||
import org.locationtech.spatial4j.context.SpatialContext;
|
import org.locationtech.spatial4j.context.SpatialContext;
|
||||||
import org.locationtech.spatial4j.distance.DistanceUtils;
|
import org.locationtech.spatial4j.distance.DistanceUtils;
|
||||||
|
|
@ -11,9 +11,11 @@ import com.multictrl.common.validator.group.AddGroup;
|
||||||
import com.multictrl.common.validator.group.UpdateGroup;
|
import com.multictrl.common.validator.group.UpdateGroup;
|
||||||
import com.multictrl.modules.business.dto.RouteAiDTO;
|
import com.multictrl.modules.business.dto.RouteAiDTO;
|
||||||
import com.multictrl.modules.business.dto.RouteDTO;
|
import com.multictrl.modules.business.dto.RouteDTO;
|
||||||
|
import com.multictrl.modules.business.dto.RouteEstimateTimeDTO;
|
||||||
import com.multictrl.modules.business.dto.UploadRouteDTO;
|
import com.multictrl.modules.business.dto.UploadRouteDTO;
|
||||||
import com.multictrl.modules.business.service.RouteService;
|
import com.multictrl.modules.business.service.RouteService;
|
||||||
import io.swagger.v3.oas.annotations.Operation;
|
import io.swagger.v3.oas.annotations.Operation;
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||||
import lombok.RequiredArgsConstructor;
|
import lombok.RequiredArgsConstructor;
|
||||||
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
import org.apache.shiro.authz.annotation.RequiresPermissions;
|
||||||
|
|
@ -125,13 +127,33 @@ public class RouteController {
|
||||||
@Parameter(name = "dockSn", description = "机库SN"),
|
@Parameter(name = "dockSn", description = "机库SN"),
|
||||||
@Parameter(name = "accurateImport", description = "是否精准导入"),
|
@Parameter(name = "accurateImport", description = "是否精准导入"),
|
||||||
@Parameter(name = "globalRthHeight", description = "返航高度"),
|
@Parameter(name = "globalRthHeight", description = "返航高度"),
|
||||||
@Parameter(name = "file", description = "航线文件")
|
@Parameter(name = "file", description = "航线文件",
|
||||||
|
schema = @Schema(type = "string", format = "binary"))
|
||||||
})
|
})
|
||||||
@RequiresPermissions("bus:route:upload")
|
@RequiresPermissions("bus:route:upload")
|
||||||
public Result<Object> upload(UploadRouteDTO dto) {
|
public Result<Object> upload(@Parameter(hidden = true) UploadRouteDTO dto) {
|
||||||
ValidatorUtils.validateEntity(dto);
|
ValidatorUtils.validateEntity(dto);
|
||||||
routeService.upload(dto);
|
routeService.upload(dto);
|
||||||
|
|
||||||
return new Result<>();
|
return new Result<>();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@PostMapping("/getWaypointEstimateTime")
|
||||||
|
@Operation(summary = "航点航线预计飞行时间")
|
||||||
|
public Result<String> getWaypointEstimateTime(@RequestBody RouteEstimateTimeDTO dto) {
|
||||||
|
ValidatorUtils.validateEntity(dto);
|
||||||
|
String estimateTime = routeService.getWaypointEstimateTime(dto);
|
||||||
|
|
||||||
|
return new Result<String>().ok(estimateTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@PutMapping("/updateGlobalRthHeight")
|
||||||
|
@Operation(summary = "修改返航高度")
|
||||||
|
@LogOperation("修改返航高度")
|
||||||
|
public Result<Object> updateGlobalRthHeight(@RequestParam Long routeId,
|
||||||
|
@RequestParam Integer globalRthHeight) {
|
||||||
|
routeService.updateGlobalRthHeight(routeId, globalRthHeight);
|
||||||
|
|
||||||
|
return new Result<>();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,34 @@
|
||||||
|
package com.multictrl.modules.business.dto;
|
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema;
|
||||||
|
import jakarta.validation.constraints.NotBlank;
|
||||||
|
import jakarta.validation.constraints.NotNull;
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 航线预计飞行时长参数
|
||||||
|
*
|
||||||
|
* @author Sdy
|
||||||
|
* @since 1.0.0 2026/6/23
|
||||||
|
*/
|
||||||
|
@Data
|
||||||
|
@Schema(name = "航线预计飞行时长参数")
|
||||||
|
public class RouteEstimateTimeDTO {
|
||||||
|
|
||||||
|
@NotBlank(message = "机库SN不能为空")
|
||||||
|
@Schema(description = "机库SN")
|
||||||
|
private String dockSn;
|
||||||
|
|
||||||
|
@Schema(description = "全局航线过渡速度")
|
||||||
|
@NotNull(message = "全局航线过渡速度不能为空")
|
||||||
|
private Double globalTransitionalSpeed;
|
||||||
|
|
||||||
|
@NotBlank(message = "高度模式不能为空")
|
||||||
|
@Schema(description = "高度模式")
|
||||||
|
private String heightModel;
|
||||||
|
|
||||||
|
@Schema(description = "航线航点信息")
|
||||||
|
private List<RouteWaypointDTO> waypointList;
|
||||||
|
}
|
||||||
|
|
@ -4,6 +4,7 @@ import com.multictrl.common.page.PageData;
|
||||||
import com.multictrl.common.service.CrudService;
|
import com.multictrl.common.service.CrudService;
|
||||||
import com.multictrl.modules.business.dto.RouteAiDTO;
|
import com.multictrl.modules.business.dto.RouteAiDTO;
|
||||||
import com.multictrl.modules.business.dto.RouteDTO;
|
import com.multictrl.modules.business.dto.RouteDTO;
|
||||||
|
import com.multictrl.modules.business.dto.RouteEstimateTimeDTO;
|
||||||
import com.multictrl.modules.business.dto.UploadRouteDTO;
|
import com.multictrl.modules.business.dto.UploadRouteDTO;
|
||||||
import com.multictrl.modules.business.entity.RouteEntity;
|
import com.multictrl.modules.business.entity.RouteEntity;
|
||||||
|
|
||||||
|
|
@ -40,4 +41,10 @@ public interface RouteService extends CrudService<RouteEntity, RouteDTO> {
|
||||||
|
|
||||||
//上传航线
|
//上传航线
|
||||||
void upload(UploadRouteDTO dto);
|
void upload(UploadRouteDTO dto);
|
||||||
|
|
||||||
|
//获取航点航线预计飞行时间
|
||||||
|
String getWaypointEstimateTime(RouteEstimateTimeDTO dto);
|
||||||
|
|
||||||
|
//修改返航高度
|
||||||
|
void updateGlobalRthHeight(Long routeId, Integer globalRthHeight);
|
||||||
}
|
}
|
||||||
|
|
@ -17,6 +17,7 @@ import com.multictrl.common.page.PageData;
|
||||||
import com.multictrl.common.service.impl.CrudServiceImpl;
|
import com.multictrl.common.service.impl.CrudServiceImpl;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
import com.multictrl.common.utils.*;
|
import com.multictrl.common.utils.*;
|
||||||
|
import com.multictrl.common.utils.map.Spatial4jUtils;
|
||||||
import com.multictrl.modules.business.dao.DockDeviceDao;
|
import com.multictrl.modules.business.dao.DockDeviceDao;
|
||||||
import com.multictrl.modules.business.dao.FlightTaskDao;
|
import com.multictrl.modules.business.dao.FlightTaskDao;
|
||||||
import com.multictrl.modules.business.dao.ZhimouCallbackDao;
|
import com.multictrl.modules.business.dao.ZhimouCallbackDao;
|
||||||
|
|
|
||||||
|
|
@ -2,8 +2,10 @@ package com.multictrl.modules.business.service.impl;
|
||||||
|
|
||||||
import cn.hutool.core.collection.CollUtil;
|
import cn.hutool.core.collection.CollUtil;
|
||||||
import cn.hutool.core.io.FileUtil;
|
import cn.hutool.core.io.FileUtil;
|
||||||
|
import cn.hutool.json.JSONObject;
|
||||||
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
|
||||||
import cn.hutool.core.util.StrUtil;
|
import cn.hutool.core.util.StrUtil;
|
||||||
|
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
||||||
import com.multictrl.common.constant.BusinessConstant;
|
import com.multictrl.common.constant.BusinessConstant;
|
||||||
import com.multictrl.common.exception.ErrorCode;
|
import com.multictrl.common.exception.ErrorCode;
|
||||||
import com.multictrl.common.exception.ExceptionUtils;
|
import com.multictrl.common.exception.ExceptionUtils;
|
||||||
|
|
@ -12,6 +14,9 @@ import com.multictrl.common.page.PageData;
|
||||||
import com.multictrl.common.service.impl.CrudServiceImpl;
|
import com.multictrl.common.service.impl.CrudServiceImpl;
|
||||||
import com.multictrl.common.utils.ConvertUtils;
|
import com.multictrl.common.utils.ConvertUtils;
|
||||||
import com.multictrl.common.utils.JsonUtils;
|
import com.multictrl.common.utils.JsonUtils;
|
||||||
|
import com.multictrl.common.utils.Utils;
|
||||||
|
import com.multictrl.common.utils.map.GeoUtils;
|
||||||
|
import com.multictrl.common.utils.map.Spatial4jUtils;
|
||||||
import com.multictrl.common.utils.kmz.KmzUtils;
|
import com.multictrl.common.utils.kmz.KmzUtils;
|
||||||
import com.multictrl.common.utils.kmz.WpmlKmlParser;
|
import com.multictrl.common.utils.kmz.WpmlKmlParser;
|
||||||
import com.multictrl.common.validator.AssertUtils;
|
import com.multictrl.common.validator.AssertUtils;
|
||||||
|
|
@ -25,6 +30,7 @@ import com.multictrl.modules.business.entity.RouteAiEntity;
|
||||||
import com.multictrl.modules.business.entity.RouteEntity;
|
import com.multictrl.modules.business.entity.RouteEntity;
|
||||||
import com.multictrl.modules.business.entity.RouteWaypointEntity;
|
import com.multictrl.modules.business.entity.RouteWaypointEntity;
|
||||||
import com.multictrl.modules.business.entity.WaypointActionEntity;
|
import com.multictrl.modules.business.entity.WaypointActionEntity;
|
||||||
|
import com.multictrl.modules.business.service.DJIBaseService;
|
||||||
import com.multictrl.modules.business.service.MinioService;
|
import com.multictrl.modules.business.service.MinioService;
|
||||||
import com.multictrl.modules.business.service.RouteService;
|
import com.multictrl.modules.business.service.RouteService;
|
||||||
import com.multictrl.modules.security.user.SecurityUser;
|
import com.multictrl.modules.security.user.SecurityUser;
|
||||||
|
|
@ -53,6 +59,7 @@ public class RouteServiceImpl extends CrudServiceImpl<RouteDao, RouteEntity, Rou
|
||||||
private final WaypointActionDao waypointActionDao;
|
private final WaypointActionDao waypointActionDao;
|
||||||
private final MinioService minioService;
|
private final MinioService minioService;
|
||||||
private final RouteAiDao routeAiDao;
|
private final RouteAiDao routeAiDao;
|
||||||
|
private final DJIBaseService djiBaseService;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public QueryWrapper<RouteEntity> getWrapper(Map<String, Object> params) {
|
public QueryWrapper<RouteEntity> getWrapper(Map<String, Object> params) {
|
||||||
|
|
@ -236,7 +243,7 @@ public class RouteServiceImpl extends CrudServiceImpl<RouteDao, RouteEntity, Rou
|
||||||
byte[] waylines = kmzInfo.get("waylines.wpml");
|
byte[] waylines = kmzInfo.get("waylines.wpml");
|
||||||
WpmlKmlParser.parseWpmlFile(waylines, routeDTO, dto.getAccurateImport());
|
WpmlKmlParser.parseWpmlFile(waylines, routeDTO, dto.getAccurateImport());
|
||||||
|
|
||||||
log.debug("解析结果:{}", JsonUtils.toJsonString(dto));
|
log.debug("解析结果:{}", JsonUtils.toJsonString(routeDTO));
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
log.error("解析结果失败:{}", ExceptionUtils.getErrorStackTrace(e));
|
log.error("解析结果失败:{}", ExceptionUtils.getErrorStackTrace(e));
|
||||||
// throw new RenException(ErrorCode.FILE_PARSER_ERROR);
|
// throw new RenException(ErrorCode.FILE_PARSER_ERROR);
|
||||||
|
|
@ -244,7 +251,9 @@ public class RouteServiceImpl extends CrudServiceImpl<RouteDao, RouteEntity, Rou
|
||||||
}
|
}
|
||||||
|
|
||||||
routeDTO.setRouteName(name);
|
routeDTO.setRouteName(name);
|
||||||
routeDTO.setGlobalRthHeight(dto.getGlobalRthHeight());
|
if (dto.getAccurateImport()) {
|
||||||
|
routeDTO.setGlobalRthHeight(dto.getGlobalRthHeight());
|
||||||
|
}
|
||||||
String path = dto.getDockSn() + "/" + originalFilename;
|
String path = dto.getDockSn() + "/" + originalFilename;
|
||||||
try {
|
try {
|
||||||
minioService.uploadFile(file.getInputStream(), BusinessConstant.ROUTE_KMZ_BUCKET, path);
|
minioService.uploadFile(file.getInputStream(), BusinessConstant.ROUTE_KMZ_BUCKET, path);
|
||||||
|
|
@ -259,17 +268,69 @@ public class RouteServiceImpl extends CrudServiceImpl<RouteDao, RouteEntity, Rou
|
||||||
//航点动作处理
|
//航点动作处理
|
||||||
List<RouteWaypointDTO> routeWaypointList = routeDTO.getRouteWaypointList();
|
List<RouteWaypointDTO> routeWaypointList = routeDTO.getRouteWaypointList();
|
||||||
handleWaypointAction(routeWaypointList);
|
handleWaypointAction(routeWaypointList);
|
||||||
|
//计算总距离
|
||||||
|
double total = 0;
|
||||||
|
for (int i = 0; i < routeWaypointList.size() - 1; i++) {
|
||||||
|
RouteWaypointDTO p1 = routeWaypointList.get(i);
|
||||||
|
RouteWaypointDTO p2 = routeWaypointList.get(i + 1);
|
||||||
|
total += Spatial4jUtils.distance(p1.getLatitude(), p1.getLongitude(), p2.getLatitude(), p2.getLongitude());
|
||||||
|
}
|
||||||
|
//计算预计飞行时长
|
||||||
|
RouteEstimateTimeDTO timeDTO = new RouteEstimateTimeDTO();
|
||||||
|
timeDTO.setDockSn(dto.getDockSn());
|
||||||
|
timeDTO.setGlobalTransitionalSpeed(routeDTO.getGlobalTransitionalSpeed());
|
||||||
|
timeDTO.setHeightModel(routeDTO.getHeightModel());
|
||||||
|
timeDTO.setWaypointList(routeWaypointList);
|
||||||
|
String estimateTime = getWaypointEstimateTime(timeDTO);
|
||||||
|
|
||||||
//保存信息
|
//保存信息
|
||||||
RouteEntity routeEntity = ConvertUtils.sourceToTarget(routeDTO, currentModelClass());
|
RouteEntity routeEntity = ConvertUtils.sourceToTarget(routeDTO, currentModelClass());
|
||||||
routeEntity.setDeptId(SecurityUser.getDeptId());
|
routeEntity.setDeptId(SecurityUser.getDeptId());
|
||||||
routeEntity.setWaypointNum(routeWaypointList.size());
|
routeEntity.setWaypointNum(routeWaypointList.size());
|
||||||
|
routeEntity.setTotalDistance(total);
|
||||||
|
routeEntity.setExpectFlightTime(estimateTime);
|
||||||
baseDao.insert(routeEntity);
|
baseDao.insert(routeEntity);
|
||||||
|
|
||||||
//保存航点信息
|
//保存航点信息
|
||||||
saveWaypoint(routeEntity.getId(), routeWaypointList);
|
saveWaypoint(routeEntity.getId(), routeWaypointList);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWaypointEstimateTime(RouteEstimateTimeDTO dto) {
|
||||||
|
String dockSn = dto.getDockSn();
|
||||||
|
LatLonDTO dockLocation = djiBaseService.getDockLocation(dockSn);
|
||||||
|
List<RouteWaypointDTO> waypointList = dto.getWaypointList();
|
||||||
|
List<JSONObject> waypoints = new ArrayList<>();
|
||||||
|
JSONObject jsonObject = null;
|
||||||
|
if (dockLocation != null) {
|
||||||
|
jsonObject = new JSONObject();
|
||||||
|
jsonObject.set("longitude", dockLocation.getLongitude());
|
||||||
|
jsonObject.set("latitude", dockLocation.getLatitude());
|
||||||
|
jsonObject.set("altitude", dockLocation.getHeight());
|
||||||
|
jsonObject.set("speed", dto.getGlobalTransitionalSpeed());
|
||||||
|
waypoints.add(jsonObject);
|
||||||
|
}
|
||||||
|
for (RouteWaypointDTO waypointDTO : waypointList) {
|
||||||
|
JSONObject object = new JSONObject();
|
||||||
|
object.set("longitude", waypointDTO.getLongitude());
|
||||||
|
object.set("latitude", waypointDTO.getLatitude());
|
||||||
|
object.set("altitude", "relativeToStartPoint".equals(dto.getHeightModel()) ?
|
||||||
|
waypointDTO.getFlightHeight() + (dockLocation != null ? dockLocation.getHeight() : 0) : waypointDTO.getFlightHeight());
|
||||||
|
object.set("speed", waypointDTO.getFlightSpeed());
|
||||||
|
waypoints.add(object);
|
||||||
|
}
|
||||||
|
|
||||||
|
long totalTravelTime = GeoUtils.calculateFlightTime(waypoints);
|
||||||
|
return Utils.calculateSeconds(totalTravelTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void updateGlobalRthHeight(Long routeId, Integer globalRthHeight) {
|
||||||
|
baseDao.update(new UpdateWrapper<RouteEntity>()
|
||||||
|
.set("global_rth_height", globalRthHeight)
|
||||||
|
.eq("id", routeId));
|
||||||
|
}
|
||||||
|
|
||||||
//判断航线是否存在
|
//判断航线是否存在
|
||||||
private void checkRouteExist(Long id, String routeName) {
|
private void checkRouteExist(Long id, String routeName) {
|
||||||
RouteEntity uavRouteEntity = baseDao.selectOne(new QueryWrapper<RouteEntity>()
|
RouteEntity uavRouteEntity = baseDao.selectOne(new QueryWrapper<RouteEntity>()
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue