package com.xiaopeng.pushMessage.utils; import com.xiaopeng.api.dto.operation.LonLatDto; import lombok.extern.slf4j.Slf4j; import org.gavaghan.geodesy.Ellipsoid; import org.gavaghan.geodesy.GeodeticCalculator; import org.gavaghan.geodesy.GeodeticCurve; import org.gavaghan.geodesy.GlobalCoordinates; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.concurrent.*; @Slf4j public class MapMatchingUtil { /** * 获取剩余导航数据 * @param polyline * @param longitude * @param latitude * @return */ public static List getRemainingNavigation(List polyline, Double longitude, Double latitude){ //导航数据只有一个点的情况 if(polyline.size() == 1){ LonLatDto lonLatDto = polyline.get(0); GlobalCoordinates source1 = new GlobalCoordinates(lonLatDto.getLatitude(), lonLatDto.getLongitude()); GlobalCoordinates target1 = new GlobalCoordinates(latitude, longitude); GeodeticCurve geoCurve1 = new GeodeticCalculator().calculateGeodeticCurve(Ellipsoid.WGS84, source1, target1); double distance =geoCurve1.getEllipsoidalDistance(); //判断距离小于5米,则可以返回空数据 if(5 >= distance){ return Collections.emptyList(); } //大于5米的情况,将车子当前位置添加进结果中 lonLatDto = new LonLatDto(longitude, latitude); polyline.add(0, lonLatDto); return polyline; } //多个点位的情况,需要找出当前点位处于那两个点位之间,然后截取掉超出的部分轨迹 return getRecentlyPoint(polyline, longitude, latitude); } /** * 判断点位在轨迹线路中的点位之间 * 返回截取后的剩余轨迹 * @param polyline * @param longitude * @param latitude * @return */ public static List getRecentlyPoint(List polyline, Double longitude, Double latitude){ for (int i = 0; i < polyline.size(); i++) { LonLatDto lonLatDto1 = polyline.get(i); GlobalCoordinates source1 = new GlobalCoordinates(lonLatDto1.getLatitude(), lonLatDto1.getLongitude()); GlobalCoordinates target1 = new GlobalCoordinates(latitude, longitude); GeodeticCurve geoCurve1 = new GeodeticCalculator().calculateGeodeticCurve(Ellipsoid.WGS84, source1, target1); double distance1 =geoCurve1.getEllipsoidalDistance(); LonLatDto lonLatDto2 = polyline.get(i + 1); GlobalCoordinates source2 = new GlobalCoordinates(lonLatDto2.getLatitude(), lonLatDto2.getLongitude()); GlobalCoordinates target2 = new GlobalCoordinates(latitude, longitude); GeodeticCurve geoCurve2 = new GeodeticCalculator().calculateGeodeticCurve(Ellipsoid.WGS84, source2, target2); double distance2 =geoCurve2.getEllipsoidalDistance(); GlobalCoordinates source3 = new GlobalCoordinates(lonLatDto1.getLatitude(), lonLatDto1.getLongitude()); GlobalCoordinates target3 = new GlobalCoordinates(lonLatDto2.getLatitude(), lonLatDto2.getLongitude()); GeodeticCurve geoCurve3 = new GeodeticCalculator().calculateGeodeticCurve(Ellipsoid.WGS84, source3, target3); double distance3 =geoCurve3.getEllipsoidalDistance(); //判断当前位置是否处于两个点位之间 if((distance1 + distance2) == distance3 || (distance1 + distance2) <= distance3 + 5){ //取距离最近的点位数据 if(distance1 < distance2){ return polyline.subList(i, polyline.size() - 1); } return polyline.subList(i + 1, polyline.size() - 1); } } return Collections.emptyList(); } /** * 轨迹裁剪(线段版本) * @param polyline * @param currentPoint * @return */ public static List clipBySegmentDistance(List polyline,LonLatDto currentPoint) { if (polyline == null || polyline.size() < 2) { return Collections.emptyList(); } double minDistance = Double.MAX_VALUE; int minSegmentIndex = 0; int increaseCount = 0; final int threshold = 3; final double tolerance = 1.0; for (int i = 0; i < polyline.size() - 1; i++) { double d = pointToSegmentDistance( currentPoint, polyline.get(i), polyline.get(i + 1) ); if (d < minDistance) { minDistance = d; minSegmentIndex = i; increaseCount = 0; } else if (d > minDistance + tolerance) { increaseCount++; } if (increaseCount >= threshold) { break; } } // 从最优线段的后一个点开始截取 return new ArrayList<>( polyline.subList(minSegmentIndex + 1, polyline.size()) ); } /** * 点到线段距离 * @param p * @param a * @param b * @return */ public static double pointToSegmentDistance( LonLatDto p, LonLatDto a, LonLatDto b) { double refLat = p.getLatitude(); double[] pXY = toXY(p.getLatitude(), p.getLongitude(), refLat); double[] aXY = toXY(a.getLatitude(), a.getLongitude(), refLat); double[] bXY = toXY(b.getLatitude(), b.getLongitude(), refLat); double x = pXY[0]; double y = pXY[1]; double x1 = aXY[0]; double y1 = aXY[1]; double x2 = bXY[0]; double y2 = bXY[1]; double dx = x2 - x1; double dy = y2 - y1; if (dx == 0 && dy == 0) { return Math.hypot(x - x1, y - y1); } double t = ((x - x1) * dx + (y - y1) * dy) / (dx * dx + dy * dy); if (t < 0) { return Math.hypot(x - x1, y - y1); } else if (t > 1) { return Math.hypot(x - x2, y - y2); } double projX = x1 + t * dx; double projY = y1 + t * dy; return Math.hypot(x - projX, y - projY); } /** * 经纬度转平面坐标(局部近似) * @param lat * @param lon * @param refLat * @return */ private static double[] toXY(double lat, double lon, double refLat) { double R = 6378137; // 地球半径 double x = Math.toRadians(lon) * R * Math.cos(Math.toRadians(refLat)); double y = Math.toRadians(lat) * R; return new double[]{x, y}; } }