目录

1. 抽稀

2. 原理

3. 具体思路

4. 代码示例


   道格拉斯-普克算法是我们常用的一种轨迹点的抽稀算法,抽稀出来的点可以尽可能的维持原先轨迹点的大体轮廓,剔除一些非必要的点

2. 原理

假设在平面坐标系上有一条由N个坐标点组成的曲线,已设定一个阈值epsilon
(1)首先,将起始点与结束点用直线连接, 再找出到该直线的距离最大,同时又大于阈值epsilon的点并记录下该点的位置(这里暂且称其为最大阈值点),如图所示:

java实现道格拉斯普克算法 道格拉斯佩克算法步骤_Math

(2)接着,以该点为分界点,将整条曲线分割成两段(这里暂且称之为左曲线和右曲线),将这两段曲线想象成独立的曲线然后重复操作(1),找出两边的最大阈值点,如图所示:

java实现道格拉斯普克算法 道格拉斯佩克算法步骤_java实现道格拉斯普克算法_02

(3)最后,重复操作(2)(1)直至再也找不到最大阈值点为止,然后将所有最大阈值点按顺序连接起来便可以得到一条更简化的,更平滑的,与原曲线十分近似的曲线,如图所示:

java实现道格拉斯普克算法 道格拉斯佩克算法步骤_git_03

java实现道格拉斯普克算法 道格拉斯佩克算法步骤_java实现道格拉斯普克算法_04

java实现道格拉斯普克算法 道格拉斯佩克算法步骤_git_05

java实现道格拉斯普克算法 道格拉斯佩克算法步骤_Math_06

3. 具体思路

        对每一条曲线的首末点虚连一条直线,求所有点与直线的距离,并找出最大距离值dmax ,用dmax与限差D相比:若dmax < ,这条曲线上的中间点所有舍去;若dmax ≥,保留dmax 相应的坐标点,并以该点为界,把曲线分为两部分,对这两部分反复使用该方法。控制限差值的大小可以控制抽稀的粒度。

4. 代码示例

public class GpsData {
   private  int index;
    public GpsData(){

    }


    /**
     * 纬度
     */
    private double latitudeEx;

    /**
     * 经度
     */
    private double longitudeEx;

  

    public double getLatitudeEx() {
        return latitudeEx;
    }

    public void setLatitudeEx(double latitudeEx) {
        this.latitudeEx = latitudeEx;
    }

    public double getLongitudeEx() {
        return longitudeEx;
    }

    public void setLongitudeEx(double longitudeEx) {
        this.longitudeEx = longitudeEx;
    }

   
    public int getIndex() {
        return index;
    }

    public void setIndex(int index) {
        this.index = index;
    }
}
public class Douglas extends JFrame {

/**
 * 存储采样点数据链表
 */
public static List<GpsData > points = new ArrayList<GpsData >();
/**
 * 控制数据经度压缩的极差
 */
private static final double D = 0.0001;

/**
 * 对矢量曲线进行压缩
 *
 * @param from 曲线的起始点
 * @param to   曲线的终止点
 */
public static void compress(GpsData from, GpsData to) {
    /**
     * 压缩算法的开关
     */
    boolean switchvalue = false;
    /**
     * 由起始点和终止点构成直线方程一般式的系数
     */
    double fromLat = Double.valueOf(from.getLatitudeEx());
    double fromLng = Double.valueOf(from.getLongitudeEx());
    double toLat = Double.valueOf(to.getLatitudeEx());
    double toLng = Double.valueOf(to.getLongitudeEx());
    double A = (fromLat - toLat)
            / Math.sqrt(Math.pow((fromLat - toLat), 2)+ Math.pow((fromLng - toLng), 2));


    /**
     * 由起始点和终止点构成直线方程一般式的系数
     */
    double B = (toLng - fromLng)
            / Math.sqrt(Math.pow((fromLat - toLat), 2)
            + Math.pow((fromLng - toLng), 2));
    /**
     * 由起始点和终止点构成直线方程一般式的系数
     */
    double C = (fromLng * toLat - toLng * fromLat)
            / Math.sqrt(Math.pow((fromLat - toLat), 2)
            + Math.pow((fromLng - toLng), 2));

    double d = 0;
    double dmax = 0;
    int m = points.indexOf(from);
    int n = points.indexOf(to);
    if (n == m + 1){ return;}

    GpsData middle = null;
    List<Double> distance = new ArrayList<>();
    for (int i = m + 1; i < n; i++) {
        double blng = Double.valueOf(points.get(i).getLongitudeEx());
        double blat = Double.valueOf(points.get(i).getLatitudeEx());
        d = Math.abs(A * (blng) + B * (blat) + C) / Math.sqrt(Math.pow(A, 2) + Math.pow(B, 2));
        distance.add(d);
    }
    dmax = distance.get(0);
    for (int j = 1; j < distance.size(); j++) {
        if (distance.get(j) > dmax){
            dmax = distance.get(j);
        }

    }
    if (dmax > D) {
        switchvalue = true;


    } else{
        switchvalue = false;
    }

    if (!switchvalue) {
        //删除Points(m,n)内的坐标
        for (int i = m + 1; i < n; i++) {
            points.get(i).setIndex(-1);
        }
    } else {
        for (int i = m + 1; i < n; i++) {
            double blng = Double.valueOf(points.get(i).getLongitudeEx());
            double blat = Double.valueOf(points.get(i).getLatitudeEx());
            if ((Math.abs(A * (blng) + B
                    * (blat) + C)
                    / Math.sqrt(Math.pow(A, 2) + Math.pow(B, 2)) == dmax)){
                middle = points.get(i);
            }

        }
        compress(from, middle);
        compress(middle, to);
    }
}

public static List<GpsData > douglasData(List<GpsData> source) {
    points = source;
    System.out.println("压缩前:");

    for (int i = 0; i < points.size(); i++) {
        GpsData p = points.get(i);
        System.out.print("[" + p.getLongitudeEx() + "," + p.getLatitudeEx() + "],");
    }
    if(!CollectionUtils.isEmpty(points)){
        compress(points.get(0), points.get(points.size() - 1));
    }
    System.out.println("\n压缩后:");
    List<GpsData > list = new ArrayList<>();
    for (int i = 0; i < points.size(); i++) {
        GpsData p = points.get(i);
        if (p.getIndex()>-1) {
            list.add(p);
            System.out.println("[" + p.getLongitudeEx() + "," + p.getLatitudeEx() + "],");
        }
    }
    return list;
}


public static void main(String[] args) {
    List<GpsData> list = new ArrayList<>();
    GpsData data=null;

   String[] $points = new String []{"117.212448,39.133785", "117.212669,39.133667", "117.213165,39.133297", "117.213203,39.13327", "117.213554,39.133099", "117.213669,39.13295", "117.213921,39.132462", "117.214088,39.132126", "117.214142,39.131962", "117.214188,39.13176", "117.214233,39.131397", "117.21418,39.13055", "117.214279,39.130459", "117.214539,39.130375", "117.214874,39.130188", "117.216881,39.128716", "117.217598,39.127995", "117.217972,39.12759", "117.218338,39.127178", "117.218407,39.127071", "117.218567,39.126911", "117.219704,39.125702", "117.219795,39.12561", "117.220284,39.125114", "117.220619,39.124802", "117.221046,39.124348", "117.221138,39.124245", "117.221268,39.124092", "117.222321,39.122955", "117.222824,39.122406", "117.222916,39.122311", "117.223663,39.121544", "117.2239,39.121452", "117.224113,39.12159", "117.224251,39.121677", "117.225136,39.122208", "117.225281,39.122292", "117.225319,39.122311", "117.226273,39.122875", "117.226685,39.123127", "117.227371,39.12352", "117.227806,39.123779", "117.228477,39.124134", "117.228531,39.124161", "117.228531,39.124161", "117.228668,39.124187", "117.228897,39.124325", "117.229767,39.12479", "117.230927,39.12545", "117.231186,39.12561", "117.231659,39.125908", "117.231834,39.126026", "117.232018,39.126186", "117.232185,39.126362", "117.232353,39.126583", "117.232658,39.126972", "117.232658,39.126972", "117.233124,39.12748", "117.233253,39.127609", "117.233368,39.127689", "117.233513,39.127762", "117.233665,39.127823", "117.233734,39.127846", "117.233833,39.127865", "117.233994,39.127888", "117.234138,39.127892", "117.234329,39.127884", "117.234612,39.127838", "117.234955,39.127754", "117.235252,39.12767", "117.236282,39.12738", "117.237137,39.127129", "117.237671,39.126961", "117.237953,39.126949", "117.238213,39.126865", "117.238472,39.126793", "117.2397,39.126434", "117.242233,39.125698", "117.243538,39.12532", "117.243645,39.125298",};
    System.out.println("抽稀前---------------"+$points.length);
    for(int i=0;i<$points.length;i++){

        data= new GpsData();
        data.setLongitudeEx(Double.valueOf($points[i].split(",")[0]));
        data.setLatitudeEx(Double.valueOf($points[i].split(",")[1]));
        list.add(data);
    }
  List<GpsData> gpsDataList=  douglasData(list);
    System.out.println("---------------"+gpsDataList.size());
    System.out.println(gpsDataList.toString());
}
}