道格拉斯·普克算法 JAVA实现
作用:
假如现在有个需求,后端计算出来一条路线规划,路线是由无数个坐标点组成,但是其中大部分连起来都是直线,那直线中有用的点也就起始点和终点,中间的全部点都是可以省略的。
原理:
对一条路径的起点和终点做一条直线,计算其余全部点到直线的距离,并找出最大距离,若最大距离比阈值小,则去掉全部中间点,若距离比阈值大,则保留当前点,以当前点为界,分成左右两条路径,重复前面的过程,直到所有的距离都小于阈值,就完事了,所以阈值越大,点越稀少,阈值越小,路径越接近真实的。
1、坐标点实体
package com.pet.utils;
import lombok.AllArgsConstructor;
import lombok.Data;
@Data
@AllArgsConstructor
class Coordinate {
private double x;
private double y;
}
2、抽稀
阈值应大于等于1,阈值越大,路径越简单,阈值越小,路径越全。
package com.pet.utils;
import java.util.ArrayList;
import java.util.List;
public class DouglasUtil {
public static void main(String[] args) {
List<Coordinate> points = new ArrayList<>();
points.add(new Coordinate(1, 2));
points.add(new Coordinate(2, 2));
points.add(new Coordinate(3, 4));
points.add(new Coordinate(4, -5));
points.add(new Coordinate(5, 3));
points.add(new Coordinate(6, 3));
points.add(new Coordinate(7, 5));
points.add(new Coordinate(8, 2));
points.add(new Coordinate(9, 0));
points.add(new Coordinate(10, 9));
points.add(new Coordinate(11, 5));
for (Coordinate p : douglas(points, 1)) {
System.out.print("(" + p.getX() + "," + p.getY() + ") ");
}
}
private static List<Coordinate> douglas(List<Coordinate> points, int threshold) {
List<Coordinate> result = new ArrayList<>();
// 找到最大阈值点
double maxH = 0;
int index = 0;
int end = points.size();
for (int i = 1; i < end - 1; i++) {
// 计算点到起点和终点组成线段的高
double h = getDistance(points.get(i), points.get(0), points.get(end - 1));
if (h > maxH) {
maxH = h;
index = i;
}
}
// 如果存在最大阈值点,就进行递归遍历出所有最大阈值点
return getCoordinates(points, threshold, maxH, index, end, result);
}
private static List<Coordinate> getCoordinates(List<Coordinate> points, int epsilon, double maxH, int index, int end, List<Coordinate> result) {
if (maxH > epsilon) {
List<Coordinate> leftPoints = new ArrayList<>();
List<Coordinate> rightPoints = new ArrayList<>();
// 分成两半 继续找比阈值大的
for (int i = 0; i < end; i++) {
if (i < index) {
leftPoints.add(points.get(i));
} else {
rightPoints.add(points.get(i));
}
}
List<Coordinate> leftResult = douglas(leftPoints, epsilon);
List<Coordinate> rightResult = douglas(rightPoints, epsilon);
rightResult.remove(0);
leftResult.addAll(rightResult);
result = leftResult;
} else {
result.add(points.get(0));
result.add(points.get(end - 1));
}
return result;
}
/**
* 计算点到直线的距离
*/
private static double getDistance(Coordinate p, Coordinate s, Coordinate e) {
double AB = distance(s, e);
double CB = distance(p, s);
double CA = distance(p, e);
// 三角形面积
double S = helen(CB, CA, AB);
// 三角形面积 = AB(底) * 高 / 2
// 所以 高 = 2 * 三角形面积 / AB(底)
return 2 * S / AB;
}
/**
* 计算两点之间的距离
*/
private static double distance(Coordinate p1, Coordinate p2) {
double x1 = p1.getX();
double y1 = p1.getY();
double x2 = p2.getX();
double y2 = p2.getY();
return Math.sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2));
}
/**
* 三角形面积
*/
private static double helen(double CB, double CA, double AB) {
double p = (CB + CA + AB) / 2;
return Math.sqrt(p * (p - CB) * (p - CA) * (p - AB));
}
}