GIS算法基礎(十)矢量壓縮算法-光欄法


前言:

遠程倉庫地址: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)

 

 

總結

與道格拉斯算法的對比:

道格拉斯普克算法采用遞歸實現,它需要對整條曲線進行掃描,才能進行壓縮,而且采用遞歸,計算量較大。

光欄法能給定閾值保留曲線特征點、並且他和道克拉斯普克算法最大的不同是,他能實時計算的,且計算量較小,占用的內存也小了。

因此光欄法是一種優秀高效的矢量壓縮算法

 

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM