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°。如此继续下去,直到整个点列检测完 为止。所有被保留的节点(含首、末点),顺序地构成了简化后 的新点列。

实现代码:

	/**
	 * 光栏法概化折线
	 * @param caliber 口径
	 * @return
	 */
	public Polyline simplify_LightBar(double caliber) {
		if(caliber<=0)
			return null;
		if(this.points.size()<2) {
			return this;
		}
		//求光栏下边界
		List<Point> points = this.getPoints();
		Point p1 = points.get(0);
		Point p2 = points.get(1);
		Line line = new Line(p1,p2);
		double len = line.getLength();
		double angle1 = Math.toDegrees(Math.atan2(.5*caliber,len));
		double angle2 = line.getVector2D().getAngle();

		//求光栏下边界
		Line down = new Line(angle2-angle1, p1);
		//求光栏上边界
		Line up = new Line(angle1+angle2,p1);
		

//		//计算光栏a1,a2坐标
//		//p1p2直线的法线矢量
//		Vector2D n = line.getN();
//		//光栏垂直平分线的垂线
//		Line l = new Line(n,p2);
//		Point a1 = l.intercourse(down);
//		Point a2 = l.intercourse(up);
		for(int i=2;i<points.size();i++) {
			Point p = points.get(i);
			//如果下一个点在光栏内,则删除上一个点,当前点为新p2
			//如果不在,则保留上一个点,以上一个点为新p1
			if(isInLightBar(up, down, p)) {
				points.get(i-1).setEnable(false);
				p2=p;
				//求当前点与p1的垂线
				Line line2 = new Line(p1,p2);
				Vector2D nn = line.getN();
				Line line3 = new Line(nn,p);
				//建立新的光栏
				double length =  line2.getLength();
				double angle11 = Math.toDegrees(Math.atan(.5*caliber/length));
				double angle22 = line2.getVector2D().getAngle();
				Line newDown = new Line(angle22-angle11, p1);
				Line newUp = new Line(angle11+angle22,p1);
				//求当前点与p1的连线的垂线与新光栏的交点
				Point b1 = line3.intercourse(newDown);
				Point b2 = line3.intercourse(newUp);
				//检查新光栏的交点是否在原光栏内
				//如果在就使用新光栏
				boolean isDownLineInLightBar = isInLightBar(up,down,b1);
				boolean isUpLineInLightBar = isInLightBar(up,down,b2);
				if(isDownLineInLightBar){
					down = newDown;
				}
				if(isUpLineInLightBar){
					up = newUp;
				}

				
			}else {
				points.get(i-1).setEnable(true);
				p1=points.get(i-1);
				p2=points.get(i);
				line = new Line(p1,p2);
				len = line.getLength();
				angle1 = Math.toDegrees(Math.atan(.5*caliber/len));
				angle2 = line.getVector2D().getAngle();
				//求光栏上下边界
				down = new Line(angle2-angle1, p1);
				up = new Line(angle1+angle2,p1);
			}

		}
		
		
//		List<Line> lines = new ArrayList<>();
//		lines.add(line);
//		lines.add(down);
//		lines.add(up);
//		lines.add(l);
//		List<Point> points2 = new ArrayList<>();
//		points2.add(a1);
//		points2.add(a2);
//		System.out.println(isInLightBar(up, down, a2));
//		PaintVector.createAndShowGUI(points2, lines, null);
		
		
		List<Point> selectedPoints = new ArrayList<>();
		Collections.addAll(selectedPoints,  new  Point[this.points.size()]); 
		Collections.copy(selectedPoints, this.points);
		Iterator<Point> iterator = selectedPoints.iterator();
		while (iterator.hasNext()) {
			Point point = (Point) iterator.next();
			if(!point.isEnable()) {
				iterator.remove();
			}
		}
		
		return new Polyline(selectedPoints);
	}
	/**
	 * 判断点是否光栏内
	 * @param up
	 * @param down
	 * @param point
	 * @return
	 */
	private static boolean isInLightBar(Line up,Line down,Point point) {
		Point start = up.getStart();
		Line line = new Line(start,point);
		Vector2D upVector = up.getVector2D();
		Vector2D downVector = down.getVector2D();
		Vector2D target = line.getVector2D();
		//利用矢量的叉积判断即可
		if(target.crossProduct(upVector)>=0 && target.crossProduct(downVector) <=0) {
			return true;
		}
		return false;
	}

运行结果:

原始数据:

光栏法压缩后(阈值5)

因为有两条折线,所以概化了两次

再次使用光栏法压缩(阈值调为10)

再次使用光栏法压缩(阈值调为20)

再次使用光栏法压缩(阈值调为50)

总结

与道格拉斯算法的对比:

道格拉斯普克算法采用递归实现,它需要对整条曲线进行扫描,才能进行压缩,而且采用递归,计算量较大。

光栏法能给定阈值保留曲线特征点、并且他和道克拉斯普克算法最大的不同是,他能实时计算的,且计算量较小,占用的内存也小了。

因此光栏法是一种优秀高效的矢量压缩算法

 

猜你喜欢

转载自blog.csdn.net/weixin_41154636/article/details/90728461