From 208731a6c41b644186bb68c93c19ef7026284e3b Mon Sep 17 00:00:00 2001
From: zhaowenyu <35589726@qq.com>
Date: Thu, 12 Mar 2026 16:04:43 +0800
Subject: [PATCH] 2026-03-09 优化标注功能 增加GeoCalculator计算工具类,算出面、圆的水平面积和水平周长,线的水平距离和直线距离。在调取按标注id获取标注详细信息接口给出,不入库。
---
ard-sample/src/main/java/com/ard/sample/annotation/util/GeoCalculator.java | 203 +++++++++++++++++++++++++++++++++
ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationDTO.java | 14 ++
ard-admin/src/main/java/com/ard/web/controller/sample/AnnotationController.java | 120 +++++++++++++++++--
3 files changed, 324 insertions(+), 13 deletions(-)
diff --git a/ard-admin/src/main/java/com/ard/web/controller/sample/AnnotationController.java b/ard-admin/src/main/java/com/ard/web/controller/sample/AnnotationController.java
index 864dd6b..22441b5 100644
--- a/ard-admin/src/main/java/com/ard/web/controller/sample/AnnotationController.java
+++ b/ard-admin/src/main/java/com/ard/web/controller/sample/AnnotationController.java
@@ -2,11 +2,13 @@
import com.ard.sample.annotation.model.dto.AnnotationDTO;
import com.ard.sample.annotation.model.dto.AnnotationDirectoryDTO;
+import com.ard.sample.annotation.model.dto.AnnotationPointsDTO;
import com.ard.sample.annotation.model.entity.AnnotationDirectoryEntity;
import com.ard.sample.annotation.model.entity.AnnotationEntity;
import com.ard.sample.annotation.service.IAnnotationDirectoryService;
import com.ard.sample.annotation.service.IAnnotationService;
import com.ard.sample.annotation.util.ConvertUtil;
+import com.ard.sample.annotation.util.GeoCalculator;
import com.ard.sample.annotation.util.JsonSerializeUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
@@ -19,6 +21,7 @@
import java.util.HashMap;
import java.util.List;
import java.util.Map;
+import java.util.stream.Collectors;
@Api(tags = "标注管理")
@Slf4j
@@ -284,21 +287,20 @@
/**
* 新增:获取全部目录及下属标注数据
*/
- @ApiOperation("获取全部目录及下属标注数据")
+ @ApiOperation("获取全部目录及下属标记数据")
@GetMapping("/directory/all")
public ResponseEntity<Map<String, Object>> listAllDirectoryWithAnnotations() {
List<AnnotationDirectoryDTO> directoryList = directoryService.listAllDirectoryWithAnnotations();
Map<String, Object> result = new HashMap<>();
result.put("code", 200);
- result.put("msg", "查询全部目录及标注成功");
+ result.put("msg", "查询全部目录及标记成功");
result.put("data", directoryList);
return ResponseEntity.ok(result);
}
- // 新增:按标注ID查询详细信息(核心补充接口,返回格式统一为ResponseEntity<Map>)
- @ApiOperation("按标注ID获取标注详细信息")
+ @ApiOperation("按标注ID获取标记详细信息,包含线的长度,面、圆的面积")
@GetMapping("/{id}")
public ResponseEntity<Map<String, Object>> getAnnotationDetailById(@PathVariable String id) {
Map<String, Object> resultMap = new HashMap<>();
@@ -306,43 +308,135 @@
// 1. 参数校验
if (id == null || id.isEmpty()) {
resultMap.put("code", 400);
- resultMap.put("msg", "标注ID不能为空");
+ resultMap.put("msg", "标记ID不能为空");
resultMap.put("data", null);
return new ResponseEntity<>(resultMap, HttpStatus.BAD_REQUEST);
}
- // 2. 查询标注实体
+ // 2. 查询标记数据
AnnotationEntity annotationEntity = annotationService.getAnnotationById(id);
if (annotationEntity == null) {
resultMap.put("code", 404);
- resultMap.put("msg", "标注不存在或已删除");
+ resultMap.put("msg", "标记不存在或已删除");
resultMap.put("data", null);
return new ResponseEntity<>(resultMap, HttpStatus.NOT_FOUND);
}
- // 3. 实体转DTO(反序列化坐标数据)
+ // 3. 转换为DTO并反序列化坐标
AnnotationDTO annotationDTO = AnnotationDTO.builder().build();
org.springframework.beans.BeanUtils.copyProperties(annotationEntity, annotationDTO);
+ List<AnnotationPointsDTO> coordinateList = null;
+ List<GeoCalculator.Coordinate> geoPoints = new java.util.ArrayList<>();
try {
- annotationDTO.setCoordinateInfo(JsonSerializeUtil.deserialize(annotationEntity.getCoordinateInfo()));
+ coordinateList = JsonSerializeUtil.deserialize(annotationEntity.getCoordinateInfo());
+ annotationDTO.setCoordinateInfo(coordinateList);
+
+ // 转换为工具类坐标
+ if (coordinateList != null && !coordinateList.isEmpty()) {
+ geoPoints = coordinateList.stream()
+ .map(p -> new GeoCalculator.Coordinate(p.getLongitude(), p.getLatitude()))
+ .collect(Collectors.toList());
+ }
} catch (Exception e) {
- log.error("反序列化标注坐标失败,标注ID:{}", id, e);
+ log.error("反序列化坐标失败,标注ID:{}", id, e);
annotationDTO.setCoordinateInfo(null);
}
- // 4. 统一返回格式
+ // 4. 按标注类型计算所有参数
+ String annotationType = annotationEntity.getAnnotationType();
+ switch (annotationType) {
+ case "2": // line 线:水平距离、直线距离
+ annotationDTO.setLineHorizontalDistance(GeoCalculator.calculatePolylineHorizontalDistance(geoPoints));
+ annotationDTO.setLineStraightDistance(GeoCalculator.calculatePolylineStraightDistance(geoPoints));
+ break;
+
+ case "3": // polygon 面:水平面积、水平周长
+ annotationDTO.setPolygonHorizontalArea(GeoCalculator.calculatePolygonHorizontalArea(geoPoints));
+ annotationDTO.setPolygonHorizontalPerimeter(GeoCalculator.calculatePolygonHorizontalPerimeter(geoPoints));
+ break;
+
+ case "4": // circle 圆:水平面积、水平周长(仅需半径)
+ Double radius = annotationDTO.getCoordinateInfo().get(0).getRadius(); //annotationEntity.getRadius();
+ if (radius == null) radius = 0.0;
+ annotationDTO.setCircleHorizontalArea(GeoCalculator.calculateCircleHorizontalArea(radius));
+ annotationDTO.setCircleHorizontalPerimeter(GeoCalculator.calculateCircleHorizontalPerimeter(radius));
+ break;
+
+ case "1": // point 点:无计算项,赋值0
+ annotationDTO.setLineHorizontalDistance(0.00);
+ annotationDTO.setLineStraightDistance(0.00);
+ annotationDTO.setPolygonHorizontalArea(0.00);
+ annotationDTO.setPolygonHorizontalPerimeter(0.00);
+ annotationDTO.setCircleHorizontalArea(0.00);
+ annotationDTO.setCircleHorizontalPerimeter(0.00);
+ break;
+
+ default: // 未知类型
+ log.warn("未知标记类型:{},不计算地理参数", annotationType);
+ break;
+ }
+
+ // 5. 返回结果
resultMap.put("code", 200);
resultMap.put("msg", "查询成功");
resultMap.put("data", annotationDTO);
return new ResponseEntity<>(resultMap, HttpStatus.OK);
} catch (Exception e) {
- // 全局异常捕获
- log.error("按ID查询标注详情异常,标注ID:{}", id, e);
+ log.error("查询标记详情异常,ID:{}", id, e);
resultMap.put("code", 500);
resultMap.put("msg", "查询失败:" + e.getMessage());
resultMap.put("data", null);
return new ResponseEntity<>(resultMap, HttpStatus.INTERNAL_SERVER_ERROR);
}
}
+
+// // 新增:按标注ID查询详细信息(核心补充接口,返回格式统一为ResponseEntity<Map>)
+// @ApiOperation("按标注ID获取标注详细信息")
+// @GetMapping("/{id}")
+// public ResponseEntity<Map<String, Object>> getAnnotationDetailById(@PathVariable String id) {
+// Map<String, Object> resultMap = new HashMap<>();
+// try {
+// // 1. 参数校验
+// if (id == null || id.isEmpty()) {
+// resultMap.put("code", 400);
+// resultMap.put("msg", "标注ID不能为空");
+// resultMap.put("data", null);
+// return new ResponseEntity<>(resultMap, HttpStatus.BAD_REQUEST);
+// }
+//
+// // 2. 查询标注实体
+// AnnotationEntity annotationEntity = annotationService.getAnnotationById(id);
+// if (annotationEntity == null) {
+// resultMap.put("code", 404);
+// resultMap.put("msg", "标注不存在或已删除");
+// resultMap.put("data", null);
+// return new ResponseEntity<>(resultMap, HttpStatus.NOT_FOUND);
+// }
+//
+// // 3. 实体转DTO(反序列化坐标数据)
+// AnnotationDTO annotationDTO = AnnotationDTO.builder().build();
+// org.springframework.beans.BeanUtils.copyProperties(annotationEntity, annotationDTO);
+// try {
+// annotationDTO.setCoordinateInfo(JsonSerializeUtil.deserialize(annotationEntity.getCoordinateInfo()));
+// } catch (Exception e) {
+// log.error("反序列化标注坐标失败,标注ID:{}", id, e);
+// annotationDTO.setCoordinateInfo(null);
+// }
+//
+// // 4. 统一返回格式
+// resultMap.put("code", 200);
+// resultMap.put("msg", "查询成功");
+// resultMap.put("data", annotationDTO);
+// return new ResponseEntity<>(resultMap, HttpStatus.OK);
+//
+// } catch (Exception e) {
+// // 全局异常捕获
+// log.error("按ID查询标注详情异常,标注ID:{}", id, e);
+// resultMap.put("code", 500);
+// resultMap.put("msg", "查询失败:" + e.getMessage());
+// resultMap.put("data", null);
+// return new ResponseEntity<>(resultMap, HttpStatus.INTERNAL_SERVER_ERROR);
+// }
+// }
}
diff --git a/ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationDTO.java b/ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationDTO.java
index 415c79f..2a553af 100644
--- a/ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationDTO.java
+++ b/ard-sample/src/main/java/com/ard/sample/annotation/model/dto/AnnotationDTO.java
@@ -1,6 +1,7 @@
package com.ard.sample.annotation.model.dto;
import com.baomidou.mybatisplus.annotation.TableField;
+import com.fasterxml.jackson.annotation.JsonInclude;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
@@ -12,6 +13,7 @@
@AllArgsConstructor
@NoArgsConstructor
@Builder
+@JsonInclude(JsonInclude.Include.NON_NULL)
public class AnnotationDTO {
private String id;
@@ -40,4 +42,16 @@
private Long createTime;
private Long updateTime;
+
+ // 线相关
+ private Double lineHorizontalDistance; // 线的水平距离(米)
+ private Double lineStraightDistance; // 线的直线距离(米)
+
+ // 面相关
+ private Double polygonHorizontalArea; // 面的水平面积(平方米)
+ private Double polygonHorizontalPerimeter; // 面的水平周长(米)
+
+ // 圆相关
+ private Double circleHorizontalArea; // 圆的水平面积(平方米)
+ private Double circleHorizontalPerimeter; // 圆的水平周长(米)
}
diff --git a/ard-sample/src/main/java/com/ard/sample/annotation/util/GeoCalculator.java b/ard-sample/src/main/java/com/ard/sample/annotation/util/GeoCalculator.java
new file mode 100644
index 0000000..b39025e
--- /dev/null
+++ b/ard-sample/src/main/java/com/ard/sample/annotation/util/GeoCalculator.java
@@ -0,0 +1,203 @@
+package com.ard.sample.annotation.util;
+
+import lombok.extern.slf4j.Slf4j;
+import java.util.List;
+
+/**
+ * 地理坐标计算工具类(WGS84坐标系)
+ * 支持:
+ * 1. 线:水平距离(累加)、直线距离(起点→终点)
+ * 2. 面:水平面积、水平周长
+ * 3. 圆:水平面积(πr²)、水平周长(2πr)
+ */
+@Slf4j
+public class GeoCalculator {
+ // WGS84椭球参数
+ private static final double WGS84_A = 6378137.0; // 地球长半轴(米)
+ private static final double RAD = Math.PI / 180.0; // 角度转弧度
+ private static final double PI = Math.PI; // 圆周率
+
+ // ====================== 通用工具方法 ======================
+ /**
+ * 保留两位小数(四舍五入)
+ */
+ public static double keepTwoDecimal(double value) {
+ return Math.round(value * 100.0) / 100.0;
+ }
+
+ /**
+ * 计算两点间的水平距离(米)- 经纬度投影到水平面
+ */
+ private static double calculateTwoPointHorizontalDistance(Coordinate p1, Coordinate p2) {
+ if (p1 == null || p2 == null) return 0.0;
+ // 转换为弧度
+ double lon1 = p1.getLongitude() * RAD;
+ double lat1 = p1.getLatitude() * RAD;
+ double lon2 = p2.getLongitude() * RAD;
+ double lat2 = p2.getLatitude() * RAD;
+
+ // 计算纬度对应的地球切面半径
+ double R = WGS84_A * Math.cos(lat1);
+ // 经度差对应的水平距离
+ double dx = R * (lon2 - lon1);
+ // 纬度差对应的水平距离
+ double dy = WGS84_A * (lat2 - lat1);
+ // 水平直线距离(勾股定理)
+ return Math.sqrt(dx * dx + dy * dy);
+ }
+
+ /**
+ * 计算两点间的直线地理距离(米)- Haversine公式
+ */
+ private static double calculateTwoPointStraightDistance(Coordinate p1, Coordinate p2) {
+ if (p1 == null || p2 == null) return 0.0;
+ double lon1 = p1.getLongitude() * RAD;
+ double lat1 = p1.getLatitude() * RAD;
+ double lon2 = p2.getLongitude() * RAD;
+ double lat2 = p2.getLatitude() * RAD;
+
+ double deltaLat = lat2 - lat1;
+ double deltaLon = lon2 - lon1;
+
+ double a = Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2)
+ + Math.cos(lat1) * Math.cos(lat2)
+ * Math.sin(deltaLon / 2) * Math.sin(deltaLon / 2);
+ double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
+ return WGS84_A * c;
+ }
+
+ // ====================== 线(折线)计算 ======================
+ /**
+ * 计算折线的水平距离(累加,米)
+ */
+ public static double calculatePolylineHorizontalDistance(List<Coordinate> points) {
+ if (points == null || points.size() < 2) {
+ log.warn("折线水平距离计算失败:顶点数不足2个");
+ return 0.0;
+ }
+ double totalHorizontal = 0.0;
+ for (int i = 0; i < points.size() - 1; i++) {
+ totalHorizontal += calculateTwoPointHorizontalDistance(points.get(i), points.get(i + 1));
+ }
+ return keepTwoDecimal(totalHorizontal);
+ }
+
+ /**
+ * 计算折线的直线距离(起点→终点,米)
+ */
+ public static double calculatePolylineStraightDistance(List<Coordinate> points) {
+ if (points == null || points.size() < 2) {
+ log.warn("折线直线距离计算失败:顶点数不足2个");
+ return 0.0;
+ }
+ Coordinate start = points.get(0);
+ Coordinate end = points.get(points.size() - 1);
+ return keepTwoDecimal(calculateTwoPointStraightDistance(start, end));
+ }
+
+ // ====================== 面(多边形)计算 ======================
+ /**
+ * 计算多边形的水平面积(平方米)
+ */
+ public static double calculatePolygonHorizontalArea(List<Coordinate> points) {
+ if (points == null || points.size() < 3) {
+ log.warn("多边形水平面积计算失败:顶点数不足3个");
+ return 0.0;
+ }
+ // 闭合多边形
+ List<Coordinate> closedPoints = new java.util.ArrayList<>(points);
+ Coordinate first = closedPoints.get(0);
+ Coordinate last = closedPoints.get(closedPoints.size() - 1);
+ if (!isCoordinateEqual(first, last)) {
+ closedPoints.add(first);
+ }
+
+ double area = 0.0;
+ int n = closedPoints.size() - 1;
+ for (int i = 0; i < n; i++) {
+ Coordinate p1 = closedPoints.get(i);
+ Coordinate p2 = closedPoints.get(i + 1);
+ // 水平投影面积公式(高斯平面投影)
+ area += (p1.getLongitude() * p2.getLatitude() - p2.getLongitude() * p1.getLatitude());
+ }
+ // 转换为实际面积(修正系数)
+ area = Math.abs(area) * 0.5 * WGS84_A * WGS84_A * RAD * RAD;
+ return keepTwoDecimal(area);
+ }
+
+ /**
+ * 计算多边形的水平周长(米)
+ */
+ public static double calculatePolygonHorizontalPerimeter(List<Coordinate> points) {
+ if (points == null || points.size() < 3) {
+ log.warn("多边形水平周长计算失败:顶点数不足3个");
+ return 0.0;
+ }
+ // 闭合多边形
+ List<Coordinate> closedPoints = new java.util.ArrayList<>(points);
+ Coordinate first = closedPoints.get(0);
+ Coordinate last = closedPoints.get(closedPoints.size() - 1);
+ if (!isCoordinateEqual(first, last)) {
+ closedPoints.add(first);
+ }
+
+ double perimeter = 0.0;
+ int n = closedPoints.size() - 1;
+ for (int i = 0; i < n; i++) {
+ perimeter += calculateTwoPointHorizontalDistance(closedPoints.get(i), closedPoints.get(i + 1));
+ }
+ return keepTwoDecimal(perimeter);
+ }
+
+ // ====================== 圆计算 ======================
+ /**
+ * 计算圆的水平面积(平方米)- π×r²
+ */
+ public static double calculateCircleHorizontalArea(double radius) {
+ if (radius <= 0) {
+ log.warn("圆形水平面积计算失败:半径非法({}米)", radius);
+ return 0.0;
+ }
+ return keepTwoDecimal(PI * radius * radius);
+ }
+
+ /**
+ * 计算圆的水平周长(米)- 2πr
+ */
+ public static double calculateCircleHorizontalPerimeter(double radius) {
+ if (radius <= 0) {
+ log.warn("圆形水平周长计算失败:半径非法({}米)", radius);
+ return 0.0;
+ }
+ return keepTwoDecimal(2 * PI * radius);
+ }
+
+ // ====================== 辅助方法 ======================
+ /**
+ * 判断两个坐标是否相等(容错1e-6)
+ */
+ private static boolean isCoordinateEqual(Coordinate p1, Coordinate p2) {
+ if (p1 == null || p2 == null) return false;
+ double lonDiff = Math.abs(p1.getLongitude() - p2.getLongitude());
+ double latDiff = Math.abs(p1.getLatitude() - p2.getLatitude());
+ return lonDiff < 1e-6 && latDiff < 1e-6;
+ }
+
+ // ====================== 坐标实体类 ======================
+ public static class Coordinate {
+ private double longitude; // 经度
+ private double latitude; // 纬度
+
+ public Coordinate() {}
+ public Coordinate(double longitude, double latitude) {
+ this.longitude = longitude;
+ this.latitude = latitude;
+ }
+
+ // Getter & Setter
+ public double getLongitude() { return longitude; }
+ public void setLongitude(double longitude) { this.longitude = longitude; }
+ public double getLatitude() { return latitude; }
+ public void setLatitude(double latitude) { this.latitude = latitude; }
+ }
+}
\ No newline at end of file
--
Gitblit v1.9.3