You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

382 lines
15 KiB

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<List<PointTemp>> readAndProcessFlightData(String filePath) throws IOException {
List<PointTemp> 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<List<PointTemp>> segments = new ArrayList<>();
List<PointTemp> 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<List<PointTemp>> readAndProcessFlightData(List<PointTemp> points) {
points = removeStationaryPoints(points,0.5);
List<List<PointTemp>> segments = new ArrayList<>();
List<PointTemp> 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<PointTemp> 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<PointTemp> 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<PointTemp> removeStationaryPoints(List<PointTemp> points, double threshold) {
List<PointTemp> 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<PointTemp> 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<PointTemp> getPointsTempFromCsv(String path) throws Exception {
List<PointTemp> 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<SortieTemp> pointToSortie(List<PointTemp> points){
List<SortieTemp> sorties = new ArrayList<>();
List<List<PointTemp>> 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<List<PointTemp>> 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<PointTemp> points = getPointsTempFromCsv(filePath);
List<SortieTemp> 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;
}
}