日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

【Java代码】道格拉斯-普克 Douglas-Peucker 抽稀算法(算法流程图解+使用JDK8方法实现+详细注解源码)

發布時間:2024/10/6 java 50 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Java代码】道格拉斯-普克 Douglas-Peucker 抽稀算法(算法流程图解+使用JDK8方法实现+详细注解源码) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

1.算法說明

??道格拉斯-普克算法 Douglas-Peucker Algorithm 簡稱 D-P 算法,亦稱為拉默-道格拉斯-普克算法、迭代適應點算法、分裂與合并算法,是將曲線近似表示為一系列點,并減少點的數量的一種算法。該算法的原始類型分別由烏爾斯·拉默于1972年以及大衛·道格拉斯和托馬斯·普克于1973年提出,并在之后的數十年中由其他學者予以完善。

??D-P 算法是公認的線狀要素化簡經典算法?,F有的線化簡算法中,有相當一部分都是在該算法基礎上進行改進產生的。用來對大量冗余的圖形數據點進行壓縮以提取必要的數據點,它是一個整體算法,具有平移和旋轉不變性,可保留較大彎曲形態上的特征點。且在給定曲線與閾值后,抽樣結果一定。

保留了較大彎曲形態上的特征點:

經典的 D-P 算法描述如下【紅色部分用于輔助理解 可忽略】:

  • 連接當前矢量曲線首尾點a、b,該直線AB為當前矢量曲線的弦;
  • 計算首尾點a、b間所有坐標點到該弦AB的距離,并獲取最大距離d及其的坐標點c;
  • 比較最大距離d與給定的閾值thresholdVal,小于閾值,當前弦AB可作為曲線的近似【首尾間的坐標點將被抽稀】,該段矢量曲線處理完畢。
  • 大于閾值,使用最大距離坐標點c分割當前矢量曲線,分割后的兩段矢量曲線分別使用1~3的流程進行處理。
  • 全部處理完畢后,依次連接各個分割點形成的折線,即可以作為整個曲線的近似。
  • 示意圖如下:

    2.源碼分享

    2.1 公式

    M(x0,y0)M(x_{0},y_{0})M(x0?,y0?) 是平面上的一個點,它到直線 Ax+By+C=0Ax+By+C=0Ax+By+C=0 的距離 d 為:

    d=∣Ax0+By0+C∣A2+B2d= \frac{|Ax_{0}+By_{0}+C|}{ \sqrt{A^{2}+B^{2}}}d=A2+B2?Ax0?+By0?+C?

    2.2 源碼

    一下是優化前的代碼,優化后的代碼請查看《道格拉斯-普克 Douglas-Peucker 抽稀算法近1000倍的速度提升》兩個對象封裝類【為了簡潔注解未使用DOC規范】:

    // 類1 坐標數據封裝 @Data @NoArgsConstructor @AllArgsConstructor public class PointData {// 標記是否刪除(0保留 1刪除)private int isDelete;// 數據點的經度private double longitudeEx;// 數據點的緯度private double latitudeEx; }// 類2 點INDEX及到線距離封裝 @Data @NoArgsConstructor @AllArgsConstructor @Builder public class DistanceData {// 當前點的 INDEX 值private int index;// 當前點到直線的距離值private double distance; }

    算法核心代碼及測試代碼【為了簡潔部分注解未使用DOC規范】:

    public class Douglas {// 矢量曲線的點數據private static List<PointData> points = new ArrayList<>();// 距離閾值(值越大效果越好但丟失細節就越多)private static final double DISTANCE_THRESHOLD = 0.00001;/*** 標記要刪除的點** @param from 矢量曲線的起始點* @param to 矢量曲線的終止點*/private static void markDeletePoints(PointData from, PointData to) {// 計算由起始點和終止點構成直線方程一般式的系數double fromLat = from.getLatitudeEx();double fromLng = from.getLongitudeEx();double toLat = to.getLatitudeEx();double toLng = to.getLongitudeEx();// 求解點到直線距離方程的參數double sqrtPowAddVal = Math.sqrt(Math.pow((fromLat - toLat), 2) + Math.pow((fromLng - toLng), 2));double parameterA = (fromLat - toLat) / sqrtPowAddVal;double parameterB = (toLng - fromLng) / sqrtPowAddVal;double parameterC = (fromLng * toLat - toLng * fromLat) / sqrtPowAddVal;int fromIndex = points.indexOf(from);int toIndex = points.indexOf(to);if (toIndex == fromIndex + 1) {return;}// 起止點之間的點到起止點連線的距離數據集合List<DistanceData> distanceDataList = new ArrayList<>();double powAddRes = Math.pow(parameterA, 2) + Math.pow(parameterB, 2);for (int i = fromIndex + 1; i < toIndex; i++) {double lng = points.get(i).getLongitudeEx();double lat = points.get(i).getLatitudeEx();// 點到直線的距離double distance = Math.abs(parameterA * (lng) + parameterB * (lat) + parameterC) / Math.sqrt(powAddRes);distanceDataList.add(DistanceData.builder().index(i).distance(distance).build());}// 獲取最大距離double distanceMax = 0;Optional<Double> max = distanceDataList.stream().map(DistanceData::getDistance).max(Double::compareTo);if (max.isPresent()) {distanceMax = max.get();}// 分割點PointData splitPoint;// 最大距離與距離閾值進行比較if (distanceMax < DISTANCE_THRESHOLD) {// 小于則標記 points(fromIndex,toIndex) 內的坐標(用于刪除)for (int i = fromIndex + 1; i < toIndex; i++) {points.get(i).setIsDelete(1);}} else {// 大于極差則進行分割double finalDistanceMax = distanceMax;// 存在多個最大點的情況List<DistanceData> distanceMaxIndex = distanceDataList.stream().filter(distanceData -> distanceData.getDistance() == finalDistanceMax).collect(Collectors.toList());int maxIndex = distanceMaxIndex.get(0).getIndex();splitPoint = points.get(maxIndex);// 分割點左側進行遞歸抽稀(標記全部要刪除的點)markDeletePoints(from, splitPoint);// 分割點右側進行遞歸抽稀(標記全部要刪除的點)markDeletePoints(splitPoint, to);}}/*** 進行抽稀:標記要刪除的點并篩選** @param source 矢量曲線點數據* @return 抽稀后的點數據*/public static List<PointData> toDilute(List<PointData> source) {// 抽稀前的點數據points = source;// 進行標記if (!CollectionUtils.isEmpty(points)) {markDeletePoints(points.get(0), points.get(points.size() - 1));}// 篩選掉標記為刪除的點return points.stream().filter(point -> point.getIsDelete() != 1).collect(Collectors.toList());}public static void main(String[] args) {List<PointData> list = new ArrayList<>();PointData data;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",};for (String point : points) {data = new PointData();data.setLongitudeEx(Double.parseDouble(point.split(",")[0]));data.setLatitudeEx(Double.parseDouble(point.split(",")[1]));list.add(data);}List<PointData> gpsDataList = toDilute(list);System.out.println(points.length + "---------------" + gpsDataList.size());System.out.println(gpsDataList.toString());} }

    2.3 結果

    80---------------46 [PointData(isDelete=0, longitudeEx=117.212448, latitudeEx=39.133785), PointData(isDelete=0, longitudeEx=117.212669, latitudeEx=39.133667), PointData(isDelete=0, longitudeEx=117.213203, latitudeEx=39.13327), PointData(isDelete=0, longitudeEx=117.213554, latitudeEx=39.133099), PointData(isDelete=0, longitudeEx=117.213669, latitudeEx=39.13295), PointData(isDelete=0, longitudeEx=117.214088, latitudeEx=39.132126), PointData(isDelete=0, longitudeEx=117.214188, latitudeEx=39.13176), PointData(isDelete=0, longitudeEx=117.214233, latitudeEx=39.131397), PointData(isDelete=0, longitudeEx=117.21418, latitudeEx=39.13055), PointData(isDelete=0, longitudeEx=117.214279, latitudeEx=39.130459), PointData(isDelete=0, longitudeEx=117.214539, latitudeEx=39.130375), PointData(isDelete=0, longitudeEx=117.214874, latitudeEx=39.130188), PointData(isDelete=0, longitudeEx=117.216881, latitudeEx=39.128716), PointData(isDelete=0, longitudeEx=117.217598, latitudeEx=39.127995), PointData(isDelete=0, longitudeEx=117.218338, latitudeEx=39.127178), PointData(isDelete=0, longitudeEx=117.218407, latitudeEx=39.127071), PointData(isDelete=0, longitudeEx=117.219704, latitudeEx=39.125702), PointData(isDelete=0, longitudeEx=117.220284, latitudeEx=39.125114), PointData(isDelete=0, longitudeEx=117.220619, latitudeEx=39.124802), PointData(isDelete=0, longitudeEx=117.222824, latitudeEx=39.122406), PointData(isDelete=0, longitudeEx=117.223663, latitudeEx=39.121544), PointData(isDelete=0, longitudeEx=117.2239, latitudeEx=39.121452), PointData(isDelete=0, longitudeEx=117.225136, latitudeEx=39.122208), PointData(isDelete=0, longitudeEx=117.227806, latitudeEx=39.123779), PointData(isDelete=0, longitudeEx=117.228531, latitudeEx=39.124161), PointData(isDelete=0, longitudeEx=117.228668, latitudeEx=39.124187), PointData(isDelete=0, longitudeEx=117.228897, latitudeEx=39.124325), PointData(isDelete=0, longitudeEx=117.229767, latitudeEx=39.12479), PointData(isDelete=0, longitudeEx=117.230927, latitudeEx=39.12545), PointData(isDelete=0, longitudeEx=117.231659, latitudeEx=39.125908), PointData(isDelete=0, longitudeEx=117.231834, latitudeEx=39.126026), PointData(isDelete=0, longitudeEx=117.232018, latitudeEx=39.126186), PointData(isDelete=0, longitudeEx=117.232185, latitudeEx=39.126362), PointData(isDelete=0, longitudeEx=117.232658, latitudeEx=39.126972), PointData(isDelete=0, longitudeEx=117.233253, latitudeEx=39.127609), PointData(isDelete=0, longitudeEx=117.233368, latitudeEx=39.127689), PointData(isDelete=0, longitudeEx=117.233513, latitudeEx=39.127762), PointData(isDelete=0, longitudeEx=117.233734, latitudeEx=39.127846), PointData(isDelete=0, longitudeEx=117.233994, latitudeEx=39.127888), PointData(isDelete=0, longitudeEx=117.234329, latitudeEx=39.127884), PointData(isDelete=0, longitudeEx=117.234612, latitudeEx=39.127838), PointData(isDelete=0, longitudeEx=117.234955, latitudeEx=39.127754), PointData(isDelete=0, longitudeEx=117.236282, latitudeEx=39.12738), PointData(isDelete=0, longitudeEx=117.237671, latitudeEx=39.126961), PointData(isDelete=0, longitudeEx=117.237953, latitudeEx=39.126949), PointData(isDelete=0, longitudeEx=117.243645, latitudeEx=39.125298)]

    3.優化說明

    • 使用了JKD8的語法
    • 查找最大距離點的方法進行了優化
    與50位技術專家面對面20年技術見證,附贈技術全景圖

    總結

    以上是生活随笔為你收集整理的【Java代码】道格拉斯-普克 Douglas-Peucker 抽稀算法(算法流程图解+使用JDK8方法实现+详细注解源码)的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。