道格拉斯-普克 Douglas-Peuker(DP算法)

道格拉斯-普克抽稀算法,是用来对大量冗余的图形数据点进行压缩以提取必要的数据点。
该算法实现抽稀的过程是:
1)对曲线的首末点虚连一条直线,求曲线上所有点与直线的距离,并找出最大距离值dmax,用dmax与事先给定的阈值D相比: 
2)若dmax<D,则将这条曲线上的中间点全部舍去;则该直线段作为曲线的近似,该段曲线处理完毕。 
  若dmax≥D,保留dmax对应的坐标点,并以该点为界,把曲线分为两部分,对这两部分重复使用该方法,即重复1),2)步,直到所有dmax均<D,即完成对曲线的抽稀。 

显然,本算法的抽稀精度也与阈值相关,阈值越大,简化程度越大,点减少的越多,反之,化简程度越低,点保留的越多,形状也越趋于原曲线。 

Point.java

package com.mapbar.jts;
/**  

 * Class Point.java 

 * Description 

 * Company mapbar 

 * author Chenll E-mail: Chenll@mapbar.com

 * Version 1.0 

 * Date 2012-6-28 下午05:51:09

 */
public class Point {
	/**
	 * 点的X坐标
	 */
	private double x = 0;

	/**
	 * 点的Y坐标
	 */
	private double y = 0;

	/**
	 * 点所属的曲线的索引
	 */
	private int index = 0;

	public double getX() {
		return x;
	}

	public void setX(double x) {
		this.x = x;
	}

	public double getY() {
		return y;
	}

	public void setY(double y) {
		this.y = y;
	}

	public int getIndex() {
		return index;
	}

	public void setIndex(int index) {
		this.index = index;
	}

	/**
	 * 点数据的构造方法
	 * 
	 * @param x
	 *            点的X坐标
	 * @param y
	 *            点的Y坐标
	 * @param index点所属的曲线的索引
	 */
	public Point(double x, double y, int index) {
		this.x = x;
		this.y = y;
		this.index = index;
	}
}

Douglas.java

package com.mapbar.jts;

import java.util.ArrayList;
import java.util.List;

import com.vividsolutions.jts.geom.Coordinate;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.io.ParseException;
import com.vividsolutions.jts.io.WKTReader;

/**
 * 
 * Class Douglas.java
 * 
 * Description
 * 
 * Company mapbar
 * 
 * author Chenll E-mail: Chenll@mapbar.com
 * 
 * Version 1.0
 * 
 * Date 2012-6-28 下午02:53:58
 */
public class Douglas {

	/**
	 * 存储采样点数据的链表
	 */
	public List<Point> points = new ArrayList<Point>();

	/**
	 * 控制数据压缩精度的极差
	 */
	private static final double D = 1;

	private WKTReader reader;

	/**
	 * 构造Geometry
	 * 
	 * @param str
	 * @return
	 */
	public Geometry buildGeo(String str) {
		try {
			if (reader == null) {
				reader = new WKTReader();
			}
			return reader.read(str);
		} catch (ParseException e) {
			throw new RuntimeException("buildGeometry Error", e);
		}
	}

	/**
	 * 读取采样点
	 */
	public void readPoint() {
		Geometry g = buildGeo("LINESTRING (1 4,2 3,4 2,6 6,7 7,8 6,9 5,10 10)");
		Coordinate[] coords = g.getCoordinates();
		for (int i = 0; i < coords.length; i++) {
			Point p = new Point(coords[i].x, coords[i].y, i);
			points.add(p);
		}
	}

	/**
	 * 对矢量曲线进行压缩
	 * 
	 * @param from
	 *            曲线的起始点
	 * @param to
	 *            曲线的终止点
	 */
	public void compress(Point from, Point to) {

		/**
		 * 压缩算法的开关量
		 */
		boolean switchvalue = false;

		/**
		 * 由起始点和终止点构成的直线方程一般式的系数
		 */
		System.out.println(from.getY());
		System.out.println(to.getY());
		double A = (from.getY() - to.getY())
				/ Math.sqrt(Math.pow((from.getY() - to.getY()), 2)
						+ Math.pow((from.getX() - to.getX()), 2));

		/**
		 * 由起始点和终止点构成的直线方程一般式的系数
		 */
		double B = (to.getX() - from.getX())
				/ Math.sqrt(Math.pow((from.getY() - to.getY()), 2)
						+ Math.pow((from.getX() - to.getX()), 2));

		/**
		 * 由起始点和终止点构成的直线方程一般式的系数
		 */
		double C = (from.getX() * to.getY() - to.getX() * from.getY())
				/ Math.sqrt(Math.pow((from.getY() - to.getY()), 2)
						+ Math.pow((from.getX() - to.getX()), 2));

		double d = 0;
		double dmax = 0;
		int m = points.indexOf(from);
		int n = points.indexOf(to);
		if (n == m + 1)
			return;
		Point middle = null;
		List<Double> distance = new ArrayList<Double>();
		for (int i = m + 1; i < n; i++) {
			d = Math.abs(A * (points.get(i).getX()) + B
					* (points.get(i).getY()) + 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++) {
				if ((Math.abs(A * (points.get(i).getX()) + B
						* (points.get(i).getY()) + C)
						/ Math.sqrt(Math.pow(A, 2) + Math.pow(B, 2)) == dmax))
					middle = points.get(i);
			}
			compress(from, middle);
			compress(middle, to);
		}
	}

	public static void main(String[] args) {
		Douglas d = new Douglas();
		d.readPoint();
		d.compress(d.points.get(0), d.points.get(d.points.size() - 1));
		for (int i = 0; i < d.points.size(); i++) {
			Point p = d.points.get(i);
			if (p.getIndex() > -1) {
				System.out.print(p.getX() + " " + p.getY() + ",");
			}
		}
	}
}


输出结果:1.0 4.0,4.0 2.0,7.0 7.0,9.0 5.0,10.0 10.0,其中2 3,6 6,8 6两个坐标被抽稀掉了。

示意图:


  • 2
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 15
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 15
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值