package com.xiaopeng.pushMessage.utils; import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; 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){ List lonLatDtos = polyline.subList(i + 1, polyline.size()); lonLatDtos.add(0, new LonLatDto(longitude, latitude)); return lonLatDtos; } } polyline.add(0, new LonLatDto(longitude, latitude)); return polyline; } public static void main(String[] args) { JSONArray objects = JSON.parseArray("[{\n" + "\t\t\"latitude\": 30.58549,\n" + "\t\t\"longitude\": 104.054501\n" + "\t}, {\n" + "\t\t\"latitude\": 30.585938,\n" + "\t\t\"longitude\": 104.054501\n" + "\t}, {\n" + "\t\t\"latitude\": 30.586522,\n" + "\t\t\"longitude\": 104.054501\n" + "\t}, {\n" + "\t\t\"latitude\": 30.587026,\n" + "\t\t\"longitude\": 104.054507\n" + "\t}, {\n" + "\t\t\"latitude\": 30.587922,\n" + "\t\t\"longitude\": 104.054518\n" + "\t}, {\n" + "\t\t\"latitude\": 30.588062,\n" + "\t\t\"longitude\": 104.054518\n" + "\t}, {\n" + "\t\t\"latitude\": 30.588292,\n" + "\t\t\"longitude\": 104.054528\n" + "\t}, {\n" + "\t\t\"latitude\": 30.588292,\n" + "\t\t\"longitude\": 104.0544\n" + "\t}, {\n" + "\t\t\"latitude\": 30.588292,\n" + "\t\t\"longitude\": 104.0544\n" + "\t}, {\n" + "\t\t\"latitude\": 30.587949,\n" + "\t\t\"longitude\": 104.054384\n" + "\t}, {\n" + "\t\t\"latitude\": 30.587311,\n" + "\t\t\"longitude\": 104.054389\n" + "\t}, {\n" + "\t\t\"latitude\": 30.587118,\n" + "\t\t\"longitude\": 104.054394\n" + "\t}, {\n" + "\t\t\"latitude\": 30.586704,\n" + "\t\t\"longitude\": 104.054394\n" + "\t}, {\n" + "\t\t\"latitude\": 30.586125,\n" + "\t\t\"longitude\": 104.0544\n" + "\t}, {\n" + "\t\t\"latitude\": 30.585938,\n" + "\t\t\"longitude\": 104.054405\n" + "\t}, {\n" + "\t\t\"latitude\": 30.585476,\n" + "\t\t\"longitude\": 104.054405\n" + "\t}, {\n" + "\t\t\"latitude\": 30.584693,\n" + "\t\t\"longitude\": 104.054405\n" + "\t}, {\n" + "\t\t\"latitude\": 30.584403,\n" + "\t\t\"longitude\": 104.054416\n" + "\t}, {\n" + "\t\t\"latitude\": 30.584312,\n" + "\t\t\"longitude\": 104.054416\n" + "\t}, {\n" + "\t\t\"latitude\": 30.583593,\n" + "\t\t\"longitude\": 104.054416\n" + "\t}, {\n" + "\t\t\"latitude\": 30.583486,\n" + "\t\t\"longitude\": 104.054416\n" + "\t}, {\n" + "\t\t\"latitude\": 30.583116,\n" + "\t\t\"longitude\": 104.054421\n" + "\t}, {\n" + "\t\t\"latitude\": 30.582391,\n" + "\t\t\"longitude\": 104.054426\n" + "\t}, {\n" + "\t\t\"latitude\": 30.582397,\n" + "\t\t\"longitude\": 104.054577\n" + "\t}, {\n" + "\t\t\"latitude\": 30.582397,\n" + "\t\t\"longitude\": 104.054577\n" + "\t}, {\n" + "\t\t\"latitude\": 30.582815,\n" + "\t\t\"longitude\": 104.054561\n" + "\t}, {\n" + "\t\t\"latitude\": 30.58311,\n" + "\t\t\"longitude\": 104.05455\n" + "\t}, {\n" + "\t\t\"latitude\": 30.58311,\n" + "\t\t\"longitude\": 104.05455\n" + "\t}, {\n" + "\t\t\"latitude\": 30.58311,\n" + "\t\t\"longitude\": 104.054899\n" + "\t}, {\n" + "\t\t\"latitude\": 30.583143,\n" + "\t\t\"longitude\": 104.054931\n" + "\t}, {\n" + "\t\t\"latitude\": 30.583148,\n" + "\t\t\"longitude\": 104.054973\n" + "\t}]"); List lonLatDtoList = new ArrayList<>(); for (int i = 0; i < objects.size(); i++) { JSONObject object = objects.getJSONObject(i); LonLatDto lonLatDto = new LonLatDto(object.getDouble("longitude"), object.getDouble("latitude")); lonLatDtoList.add(lonLatDto); } for (int i = 0; i < 20; i++) { List remainingNavigation = getRemainingNavigation(lonLatDtoList, 104.054503, 30.58549); lonLatDtoList = remainingNavigation; } } /** * 轨迹裁剪(线段版本) * @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}; } }