package com.jiagutech.ams.utils; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import org.apache.commons.csv.CSVFormat; import org.apache.commons.csv.CSVParser; import org.apache.commons.csv.CSVRecord; import org.locationtech.jts.geom.*; import org.locationtech.proj4j.*; import java.io.FileReader; import java.io.IOException; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.format.DateTimeFormatter; import java.util.ArrayList; import java.util.List; public class FlightTrackerUtils { private static final double DEG_TO_RAD = Math.PI / 180; private static final double RAD_TO_DEG = 180 / Math.PI; // 架次最小轨迹点数量 private static final double MIN_POINT_COUNT = 10; // 架次最小轨迹点密度 private static final double MIN_POINT_DENSITY = 1; // 架次轨迹最大速度 dm/s private static final double MAX_SPEED = 100; // 架次轨迹最大间隔距离 单位:米 private static final double MAX_DISTANCE = 100; // 架次轨迹最大时间间隔 单位:秒 private static final double MAX_DURATION = 90; public static class PointTemp { LocalDateTime timestamp; double lat; double lon; int spd; public PointTemp(LocalDateTime timestamp, double lat, double lon, int spd) { this.timestamp = timestamp; this.lat = lat; this.lon = lon; this.spd = spd; } } public static class SortieTemp{ long startTime; long endTime; double area; long duration; String startGeom; public long getDuration() { return (endTime - startTime) / 1000; } public long getStartTime() { return startTime; } public long getEndTime() { return endTime; } public double getArea() { return area; } public String getStartGeom() { return startGeom; } public String toJson(){ ObjectMapper mapper = new ObjectMapper(); try { return mapper.writeValueAsString(this); } catch (JsonProcessingException e) { e.printStackTrace(); } return null; } } public static double haversineDistance(double lat1, double lon1, double lat2, double lon2) { GeometryFactory geometryFactory = new GeometryFactory(); Coordinate coord1 = fromLatLngToPoint(lat1, lon1); Coordinate coord2 = fromLatLngToPoint(lat2, lon2); Point point1 = geometryFactory.createPoint(coord1); Point point2 = geometryFactory.createPoint(coord2); return point1.distance(point2); } public static List> readAndProcessFlightData(String filePath) throws IOException { List points = new ArrayList<>(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); try (CSVParser parser = CSVParser.parse(new FileReader(filePath), CSVFormat.DEFAULT.withHeader())) { for (CSVRecord record : parser) { LocalDateTime timestamp = LocalDateTime.parse(record.get("ts"), formatter); double lat = Double.parseDouble(record.get("lat")); double lon = Double.parseDouble(record.get("lon")); int spd = Integer.parseInt(record.get("spd")); points.add(new PointTemp(timestamp, lat, lon, spd)); } } points = removeStationaryPoints(points,0.5); List> segments = new ArrayList<>(); List currentSegment = new ArrayList<>(); for (int i = 0; i < points.size() - 1; i++) { PointTemp p1 = points.get(i); PointTemp p2 = points.get(i + 1); double distance = haversineDistance(p1.lat, p1.lon, p2.lat, p2.lon); long timeDifference = java.time.Duration.between(p1.timestamp, p2.timestamp).getSeconds(); // double speed = distance / timeDifference; if (p1.spd <= MAX_SPEED) { if (distance <= MAX_DISTANCE && timeDifference <= MAX_DURATION) { currentSegment.add(p1); } else { currentSegment.add(p1); segments.add(currentSegment); currentSegment = new ArrayList<>(); } } } currentSegment.add(points.get(points.size() - 1)); segments.add(currentSegment); return segments; } public static List> readAndProcessFlightData(List points) { points = removeStationaryPoints(points,0.5); List> segments = new ArrayList<>(); List currentSegment = new ArrayList<>(); for (int i = 0; i < points.size() - 1; i++) { PointTemp p1 = points.get(i); PointTemp p2 = points.get(i + 1); double distance = haversineDistance(p1.lat, p1.lon, p2.lat, p2.lon); long timeDifference = java.time.Duration.between(p1.timestamp, p2.timestamp).getSeconds(); // double speed = distance / timeDifference; if (p1.spd <= MAX_SPEED) { if (distance <= 100 && timeDifference <= 90) { currentSegment.add(p1); } else { currentSegment.add(p1); segments.add(currentSegment); currentSegment = new ArrayList<>(); } } } currentSegment.add(points.get(points.size() - 1)); segments.add(currentSegment); return segments; } public static Coordinate fromLatLngToPoint(double lat, double lng) { double x = lng * 20037508.34 / 180; double y = Math.log(Math.tan((90 + lat) * DEG_TO_RAD / 2)) / DEG_TO_RAD; y = y * 20037508.34 / 180; return new Coordinate(x, y); } // public static double calculateArea(List points) { // if (points.size() < 3) { // return 0; // } // // GeometryFactory geometryFactory = new GeometryFactory(); // Coordinate[] coordinates = new Coordinate[points.size() + 1]; // // for (int i = 0; i < points.size(); i++) { // PointTemp point = points.get(i); // coordinates[i] = fromLatLngToPoint(point.lat, point.lon); // } // coordinates[points.size()] = coordinates[0]; // // Polygon polygon = geometryFactory.createPolygon(coordinates); // double area = polygon.getArea(); // // return area * 0.0001; // Convert to mu (亩) // } public static double calculateArea(List points) { if (points.size() < 3) { return 0; } GeometryFactory geometryFactory = new GeometryFactory(); Coordinate[] coordinates = new Coordinate[points.size() + 1]; for (int i = 0; i < points.size(); i++) { PointTemp point = points.get(i); ProjCoordinate projCoord = fromLatLngToUTM(point.lat, point.lon); coordinates[i] = new Coordinate(projCoord.x, projCoord.y); } coordinates[points.size()] = coordinates[0]; LinearRing shell = geometryFactory.createLinearRing(coordinates); Polygon polygon = geometryFactory.createPolygon(shell); double area = polygon.getArea(); return area * 0.0015; // Convert to mu (亩) } public static List removeStationaryPoints(List points, double threshold) { List filteredPoints = new ArrayList<>(); filteredPoints.add(points.get(0)); for (int i = 1; i < points.size(); i++) { PointTemp previousPoint = points.get(i - 1); PointTemp currentPoint = points.get(i); double distance = haversineDistance(previousPoint.lat, previousPoint.lon, currentPoint.lat, currentPoint.lon); if (distance >= threshold) { filteredPoints.add(currentPoint); } } return filteredPoints; } // public static Point toScreenCoordinates(PointTemp point, double minLat, double maxLat, double minLon, double maxLon, int width, int height) { // int x = (int) ((point.lon - minLon) / (maxLon - minLon) * width); // int y = (int) ((maxLat - point.lat) / (maxLat - minLat) * height); // return new Point(x, y); // } // public static void saveTrajectoryToImage(List group, String filename) { // int width = 800; // int height = 600; // // // 计算最小和最大经纬度 // double minLat = Double.MAX_VALUE; // double maxLat = Double.MIN_VALUE; // double minLon = Double.MAX_VALUE; // double maxLon = Double.MIN_VALUE; // for (PointTemp point : group) { // minLat = Math.min(minLat, point.lat); // maxLat = Math.max(maxLat, point.lat); // minLon = Math.min(minLon, point.lon); // maxLon = Math.max(maxLon, point.lon); // // } // // // 创建 BufferedImage // BufferedImage image = new BufferedImage(width, height, BufferedImage.TYPE_INT_RGB); // Graphics2D g = image.createGraphics(); // // // 设置背景和轨迹颜色 // g.setColor(Color.WHITE); // g.fillRect(0, 0, width, height); // g.setColor(Color.BLUE); // // // 绘制轨迹 // for (int i = 0; i < group.size() - 1; i++) { // PointTemp point1 = group.get(i); // PointTemp point2 = group.get(i + 1); // Point screenPoint1 = toScreenCoordinates(point1, minLat, maxLat, minLon, maxLon, width, height); // Point screenPoint2 = toScreenCoordinates(point2, minLat, maxLat, minLon, maxLon, width, height); // g.drawLine(screenPoint1.x, screenPoint1.y, screenPoint2.x, screenPoint2.y); // } // // // // 保存图像为 PNG 文件 // try { // File pngFile = new File(filename); //// pngFile.deleteOnExit(); // // ImageIO.write(image, "png", pngFile); // } catch (IOException e) { // System.out.printf("保存图像文件失败: %s", e.getMessage()); // e.printStackTrace(); // } // } public static ProjCoordinate fromLatLngToUTM(double lat, double lon) { CRSFactory crsFactory = new CRSFactory(); CoordinateReferenceSystem wgs84 = crsFactory.createFromName("EPSG:4326"); CoordinateReferenceSystem utm = crsFactory.createFromName("EPSG:32633"); // Choose the appropriate UTM zone BasicCoordinateTransform transform = new BasicCoordinateTransform(wgs84, utm); ProjCoordinate srcCoord = new ProjCoordinate(lon, lat); ProjCoordinate dstCoord = new ProjCoordinate(); transform.transform(srcCoord, dstCoord); return dstCoord; } public static List getPointsTempFromCsv(String path) throws Exception { List points = new ArrayList<>(); DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS"); try (CSVParser parser = CSVParser.parse(new FileReader(path), CSVFormat.DEFAULT.withHeader())) { for (CSVRecord record : parser) { LocalDateTime timestamp = LocalDateTime.parse(record.get("ts"), formatter); double lat = Double.parseDouble(record.get("lat")); double lon = Double.parseDouble(record.get("lon")); int spd = Integer.parseInt(record.get("spd")); points.add(new PointTemp(timestamp, lat, lon, spd)); } } return points; } public static List pointToSortie(List points){ List sorties = new ArrayList<>(); List> segments = readAndProcessFlightData(points); for (int i = 0; i < segments.size(); i++) { double area = calculateArea(segments.get(i)); if (segments.get(i).size() < MIN_POINT_COUNT || segments.get(i).size() / area < MIN_POINT_DENSITY){ continue; } SortieTemp sortieTemp = new SortieTemp(); sortieTemp.startTime = segments.get(i).get(0).timestamp.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); sortieTemp.endTime = segments.get(i).get(segments.get(i).size()-1).timestamp.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); sortieTemp.area = area; sortieTemp.startGeom = segments.get(i).get(0).lat+" "+segments.get(i).get(0).lon; sorties.add(sortieTemp); } return sorties; } public static void main(String[] args) throws Exception { String filePath = "/Users/leesans/Documents/03_Source/jiagu/test/sorties/src/main/java/org/example/test0319.csv"; // List> segments = readAndProcessFlightData(filePath); // for (int i = 0; i < segments.size(); i++) { // double area = calculateArea(segments.get(i)); // if (segments.get(i).size() < 30 || segments.get(i).size() / area < 1){ // continue; // } // SortieTemp sortieTemp = new SortieTemp(); // sortieTemp.startTime = segments.get(i).get(0).timestamp.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); // sortieTemp.endTime = segments.get(i).get(segments.get(i).size()-1).timestamp.atZone(ZoneId.systemDefault()).toInstant().toEpochMilli(); // sortieTemp.area = area; // saveTrajectoryToImage(segments.get(i), String.format("/Users/leesans/Documents/03_Source/jiagu/test/output/segment-%d.png", i + 1)); // System.out.printf("Segment %d size: %d area: %.2f mu density: %.2f first timestamp: %s end timestamp: %s%n", i + 1, // segments.get(i).size(), area, segments.get(i).size() / area, segments.get(i).get(0).timestamp, // segments.get(i).get(segments.get(i).size()-1).timestamp); // System.out.println(sortieTemp.toJson()); // } List points = getPointsTempFromCsv(filePath); List sorties = pointToSortie(points); for (int i = 0; i < sorties.size(); i++) { System.out.println(sorties.get(i).toJson()); } } public static ProjCoordinate CGS2000ToWGS84(double x, double y) { // 定义坐标转换器 CoordinateTransformFactory ctFactory = new CoordinateTransformFactory(); // 定义源和目标投影 CRSFactory crsFactory = new CRSFactory(); CoordinateReferenceSystem sourceCRS = crsFactory.createFromName("EPSG:4547"); // 原始坐标系 CoordinateReferenceSystem targetCRS = crsFactory.createFromName("EPSG:4326"); // 目标坐标系 // 创建转换器 CoordinateTransform transform = ctFactory.createTransform(sourceCRS, targetCRS); // 执行坐标转换 ProjCoordinate srcCoord = new ProjCoordinate(x, y); ProjCoordinate targetCoord = new ProjCoordinate(); transform.transform(srcCoord, targetCoord); // 4. 输出转换后的正常经纬度坐标 return targetCoord; } }