前言:
遠程倉庫地址:https://github.com/XiaoZhong233/GIS_ALG
光欄法是一種矢量數據的壓縮算法。光欄法的基本思路是對每一條曲線上的所有點, 逐點定義一個扇形區域。若曲線的下一節點在扇形外, 則保留當前節點; 若曲線的下一節點在扇形內, 則舍去當前節點。
說明:
光欄法與道格拉斯算法都是矢量壓縮算法,但是光欄法能很好的保存線的形狀,而道格拉斯普克算法是概化算法,他的作用主要是把凹凸不平的折線變得平直,因此算“概化”算法
算法步驟:
1、輸入光欄的口徑d
這個口徑也就是每次掃描的扇形區域,若曲線的下一節點在扇形外, 則保留當前節點; 若曲線的下一節點在扇形內, 則舍去當前節點。
2、讀取坐標
1、2兩點坐標,記入p1,p2
3、建立光欄
連接p1和p2點,過 p2點作一條垂直於p1p2 的直線,在該垂線上取 兩點a1和a2,使a1p2= a2p2=d/2,此時a1和 a2為“光欄”邊界點, p1與a1、p1與a2的連線 為以p1為頂點的扇形的 兩條邊,這就定義了一 個扇形(這個扇形的口朝 向曲線的前進方向,邊 長是任意的)。通過p1並在扇形內的所有直線都具有這種性質, 即p1p2上各點到這些直線的垂距都不大於d/2。
若p3點在扇形內,則舍去p2點。然后連接p1和p3,過p3作 p1p1的垂線,該垂線與前面定義的扇形邊交於c1和c2。在垂線 上找到b1和b2點,使p3b1=p3b2=d/2,若b1或b2點落在原扇 形外面,則用c1或c2取代。此時用p1b1和p1c2定義一個新的扇 形,這當然是口徑(b1c2)縮小了的“光欄”。
4、檢查下一節點
若該點在新扇形內,則重復第(2)步;直 到發現有一個節點在最新定義的扇形外為止。
當發現在扇形外的節點,如p4,此時保留p3點,以p3作為 新起點,重復1°~3°。如此繼續下去,直到整個點列檢測完 為止。所有被保留的節點(含首、末點),順序地構成了簡化后 的新點列。
實現代碼:
1 /** 2 * 光欄法壓縮折線 3 * @param caliber 口徑 4 * @return 5 */ 6 public Polyline simplify_LightBar(double caliber) { 7 if(caliber<=0) 8 return null; 9 if(this.points.size()<2) { 10 return this; 11 } 12 //求光欄下邊界 13 List<Point> points = this.getPoints(); 14 Point p1 = points.get(0); 15 Point p2 = points.get(1); 16 Line line = new Line(p1,p2); 17 double len = line.getLength(); 18 double angle1 = Math.toDegrees(Math.atan2(.5*caliber,len)); 19 double angle2 = line.getVector2D().getAngle(); 20 21 //求光欄下邊界 22 Line down = new Line(angle2-angle1, p1); 23 //求光欄上邊界 24 Line up = new Line(angle1+angle2,p1); 25 26 27 // //計算光欄a1,a2坐標 28 // //p1p2直線的法線矢量 29 // Vector2D n = line.getN(); 30 // //光欄垂直平分線的垂線 31 // Line l = new Line(n,p2); 32 // Point a1 = l.intercourse(down); 33 // Point a2 = l.intercourse(up); 34 for(int i=2;i<points.size();i++) { 35 Point p = points.get(i); 36 //如果下一個點在光欄內,則刪除上一個點,當前點為新p2 37 //如果不在,則保留上一個點,以上一個點為新p1 38 if(isInLightBar(up, down, p)) { 39 points.get(i-1).setEnable(false); 40 p2=p; 41 //求當前點與p1的垂線 42 Line line2 = new Line(p1,p2); 43 Vector2D nn = line.getN(); 44 Line line3 = new Line(nn,p); 45 //建立新的光欄 46 double length = line2.getLength(); 47 double angle11 = Math.toDegrees(Math.atan(.5*caliber/length)); 48 double angle22 = line2.getVector2D().getAngle(); 49 Line newDown = new Line(angle22-angle11, p1); 50 Line newUp = new Line(angle11+angle22,p1); 51 //求當前點與p1的連線的垂線與新光欄的交點 52 Point b1 = line3.intercourse(newDown); 53 Point b2 = line3.intercourse(newUp); 54 //檢查新光欄的交點是否在原光欄內 55 //如果在就使用新光欄,不在就構建另一個光欄 56 if(isInLightBar(up, down, b1) && isInLightBar(up, down, b2)) { 57 down = newDown; 58 up = newUp; 59 }else { 60 //只有b1在光欄內 61 if(isInLightBar(up, down, b1)) { 62 down = new Line(p1,b1); 63 } 64 if(isInLightBar(up, down, b2)) { 65 up = new Line(p1,b2); 66 } 67 } 68 69 }else { 70 points.get(i-1).setEnable(true); 71 p1=points.get(i-1); 72 p2=points.get(i); 73 line = new Line(p1,p2); 74 len = line.getLength(); 75 angle1 = Math.toDegrees(Math.atan(.5*caliber/len)); 76 angle2 = line.getVector2D().getAngle(); 77 //求光欄上下邊界 78 down = new Line(angle2-angle1, p1); 79 up = new Line(angle1+angle2,p1); 80 } 81 82 83 84 } 85 86 List<Point> selectedPoints = new ArrayList<>(); 87 Collections.addAll(selectedPoints, new Point[this.points.size()]); 88 Collections.copy(selectedPoints, this.points); 89 Iterator<Point> iterator = selectedPoints.iterator(); 90 while (iterator.hasNext()) { 91 Point point = (Point) iterator.next(); 92 if(!point.isEnable()) { 93 iterator.remove(); 94 } 95 } 96 97 return new Polyline(selectedPoints); 98 } 99 /** 100 * 判斷點是否光欄內 101 * @param up 102 * @param down 103 * @param point 104 * @return 105 */ 106 private static boolean isInLightBar(Line up,Line down,Point point) { 107 Point start = up.getStart(); 108 Line line = new Line(start,point); 109 Vector2D upVector = up.getVector2D(); 110 Vector2D downVector = down.getVector2D(); 111 Vector2D target = line.getVector2D(); 112 //利用矢量的叉積判斷即可 113 if(target.crossProduct(upVector)>=0 && target.crossProduct(downVector) <=0) { 114 return true; 115 } 116 return false; 117 }
運行結果:
原始數據:
光欄法壓縮后(閾值5)
因為有兩條折線,所以概化了兩次
再次使用光欄法壓縮(閾值調為10)
再次使用光欄法壓縮(閾值調為20)
再次使用光欄法壓縮(閾值調為50)
總結
與道格拉斯算法的對比:
道格拉斯普克算法采用遞歸實現,它需要對整條曲線進行掃描,才能進行壓縮,而且采用遞歸,計算量較大。
光欄法能給定閾值保留曲線特征點、並且他和道克拉斯普克算法最大的不同是,他能實時計算的,且計算量較小,占用的內存也小了。
因此光欄法是一種優秀高效的矢量壓縮算法