道格拉斯压缩算法
import java.util.List;public interface Track { /** * 轨迹压缩 * @param coordinate 原始轨迹 Array<{latitude,longitude,UTC}> * @return douglasResult 压缩后的轨迹 */ List<double[]> compress(List<double[]> coordinate);}import lombok.Data;import lombok.Setter;import java.util.ArrayList;import java.util.Arrays;import java.util.Comparator;import java.util.List;public class Douglas implements Track{ private int dMax; private int dMaxLimit; /** * @param dMax 允许最大距离误差 * @param dMaxLimit 允许最大距离误差上限,超过该上限,认为异常点 */ public Douglas(int dMax,int dMaxLimit){ this.dMax = dMax; this.dMaxLimit = dMaxLimit; } /** * @param coordinate 原始轨迹 Array<{latitude,longitude}> * @return douglasResult 压缩后的轨迹 */ @Override public List<double[]> compress(List<double[]> coordinate) { //抽稀点数量需要大于2 if (coordinate == null || coordinate.size() <= 2) { return null; } List<double[]> coordinate2 = new ArrayList<>(); for (int i = 0; i < coordinate.size(); i++) { double[] point = Arrays.copyOf(coordinate.get(i),4) ; point[3] = i; coordinate2.add(point); } List<double[]> result = new ArrayList<>(); compressLine(coordinate2, result, 0, coordinate2.size() - 1, dMax,dMaxLimit); result.add(coordinate2.get(0)); result.add(coordinate2.get(coordinate.size() - 1)); result.sort(new Comparator<double[]>() { @Override public int compare(double[] u1, double[] u2) { if (u1[3] > u2[3]) { return 1; } else if (u1[3] < u2[3]) { return -1; } // 相等 return 0; } }); return result; } /** * 计算点pX到点pA和pB所确定的直线的距离 */ private double distToSegment(double[] start, double[] end, double[] center) { double a = Math.abs(calculationDistance(start, end)); double b = Math.abs(calculationDistance(start, center)); double c = Math.abs(calculationDistance(end, center)); double p = (a + b + c) / 2.0; double s = Math.sqrt(Math.abs(p * (p - a) * (p - b) * (p - c))); return s * 2.0 / a; } /** * 递归方式压缩轨迹 */ private void compressLine(List<double[]> coordinate, List<double[]> result, int start, int end, int dMax, int dMaxLimit) { if (start < end) { double maxDist = 0; int currentIndex = 0; double[] startPoint = coordinate.get(start); double[] endPoint = coordinate.get(end); for (int i = start + 1; i < end; i++) { double currentDist = distToSegment(startPoint, endPoint, coordinate.get(i)); if (currentDist > maxDist) { maxDist = currentDist; currentIndex = i; } } if (maxDist >= dMax && maxDist <= dMaxLimit) { //将当前点加入到过滤数组中 result.add(coordinate.get(currentIndex)); //将原来的线段以当前点为中心拆成两段,分别进行递归处理 compressLine(coordinate, result, start, currentIndex, dMax,dMaxLimit); compressLine(coordinate, result, currentIndex, end, dMax,dMaxLimit); } } } /** * 计算两点距离 */ private double calculationDistance(double[] point1, double[] point2) { double lat1 = point1[0]; double lat2 = point2[0]; double lng1 = point1[1]; double lng2 = point2[1]; double radLat1 = lat1 * Math.PI / 180.0; double radLat2 = lat2 * Math.PI / 180.0; double a = radLat1 - radLat2; double b = (lng1 * Math.PI / 180.0) - (lng2 * Math.PI / 180.0); double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) + Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2))); return s * 6370996.81; }}