多边形转为周边l距离多边形
0:多边形坐标
1:边按各边垂直方向向外平移l距离
public class PolygonVectorTranslationDemo {
private final static double IGNORE_VALUE = 0.1E-6;
//得到周边l单位距离的点集合
public List<Point> pointsProcess(List<Point> points,double l){
clockwise(points); //未知顺时针、逆时针方向,处理为顺时针
List<Point> vectors = pointToClockwiseVector(points); //点转为顺时针方向向量
List<Point> outBisector = outBisectorVector(vectors); //得到凸角向外角平分线的向量
return convex_concaveProcess(points,vectors,outBisector,l); //凸凹角判断处理
}
private List<Point> convex_concaveProcess(List<Point> ps,List<Point> vs,List<Point> obs,double l){
List<Point> ds = new ArrayList();
for(int i=0;i<ps.size();i++){
//得到角平分线与两边向量夹角的cos值,a*b = |a|*|b|cosα
//double cosα = multiplication(vs.get(i),bs.get(i))/(vectorNorm(vs.get(i))*vectorNorm(bs.get(i)));
double cosα = Math.abs(getCosAngle(vs.get(i),obs.get(i)));
double sinβ = Math.sqrt(1-cosα*cosα);
//凸凹角判断,顺时针的点,当左右两点一起顺时针则为凸
if(isClockwise(ps,i)){
//凸边形处理
ds.addAll(convexProcess(sinβ,ps.get(i),obs.get(i),l));
}else{
//凹边形处理
ds.add(concaveProcess(sinβ,ps.get(i),vs.get(i),obs.get(i),l));
}
}
return ds;
}
//凸角处理
private List<Point> convexProcess(double cosα,Point p,Point v,double l){
return calculateConvexPoint(cosα, p, v, l);
}
//凹角处理
private Point concaveProcess(double sinα,Point p,Point v,Point vb,double l){
Point vb1 = new Point();
double x;
double y;
vb = vectorReverse(vb);
if(Math.abs(vb.getY())>IGNORE_VALUE){
double temp = Math.sqrt((l*l/sinα/sinα)/(1+(vb.getX()*vb.getX()/vb.getY()/vb.getY())));
y = temp + p.getY();
x = (y-p.getY())*vb.getX()/vb.getY()+p.getX();
vb1.setX(x-p.getX());
vb1.setY(y-p.getY());
if(getCosAngle(vb1,v)>0){
y = p.getY()-temp;
x = (y-p.getY())*vb.getX()/vb.getY()+p.getX();
};
vb1.setX(x);
vb1.setY(y);
}else{
x = p.getX()+l/sinα;
y = p.getY();
vb1.setX(x-p.getX());
vb1.setY(y-p.getY());
if(getCosAngle(vb1,v)>0){
x = p.getX()-l/sinα;
y = p.getY();
};
vb1.setX(x);
vb1.setY(y);
}
return vb1;
}
//cosα,顶角,角平分线向量,到顶点距离
//返回符合的点,顺时针
private List<Point> calculateConvexPoint(double cosα,Point p,Point v,double l){
List<Point> points = new ArrayList();
//a*b = |a|*|b|cosα两边平方的,未知点位(X,Y)
//最终的到X*Vx+Y*Vy = 已知数(knownDatas),(X-Px)*(X-Px) +(Y-Py)*(Y-Py) = l*l
double knownDatas = l*vectorNorm(v)*cosα+p.getX()*v.getX()+p.getY()*v.getY();
//double knownDatas = l*cosα+p.getX()*v.getX()+p.getY()*v.getY();
double a = 1+(v.getX()*v.getX())/(v.getY()*v.getY());
double b = -2*(p.getX()+(v.getX()/v.getY())*(knownDatas/v.getY()-p.getY()));
double c = p.getX()*p.getX()+(knownDatas/v.getY()-p.getY())*(knownDatas/v.getY()-p.getY())-l*l;
double x1 = (-b+Math.sqrt(b*b-4*a*c))/(2*a);
double y1 = (knownDatas-x1*v.getX())/v.getY();
double x2 = (-b-Math.sqrt(b*b-4*a*c))/(2*a);
double y2 = (knownDatas-x2*v.getX())/v.getY();
points.add(new Point(zeroProcess(x1),zeroProcess(y1)));
points.add(p);
points.add(new Point(zeroProcess(x2),zeroProcess(y2)));
isClockwise(points,false);
points.remove(1);
return points;
}
//点顺时针判断处理
private void clockwise(List<Point> points){
if(!isClockwise(points)){
Collections.reverse(points);
}
}
//得到凸角向外的角平分线向量
private List<Point> outBisectorVector(List<Point> vs){
List<Point> ds = new ArrayList();
for(int i=0;i<vs.size();i++){
ds.add(add(unit(vs.get(i)), vectorReverse(unit(vs.get((i+1==vs.size()?0:i+1))))));
}
return ds;
}
//点转为各顶点向量(三点测试,用一个角测试)
private List<Point> pointToVector(List<Point> points){
List<Point> ps = new ArrayList();
for (int i=0;i<points.size();i++){
ps.add(minus(points.get(i-1<0?points.size()-1:i-1),points.get(i)));
ps.add(minus(points.get(i+1==points.size()?0:i+1),points.get(i)));
}
return ps;
}
private List<Point> pointToClockwiseVector(List<Point> ps){
List<Point> ds = new ArrayList();
for (int i=0;i<ps.size();i++)
ds.add(minus(ps.get(i),ps.get(i-1<0?ps.size()-1:i-1)));
return ds;
}
//true 则所有点为顺时针
private boolean isClockwise(List<Point> pList){
//获取最值,最值相邻3点判断顺逆时针
int index =0 ;
int size = pList.size();
double maxlng=pList.get(0).getX(); //设最大
for (int i=1;i<size;i++ ){
if(maxlng<=pList.get(i).getY()){
maxlng = pList.get(i).getY();
index = i;
}
}
return isClockwise(pList,index);
}
//flag为true时,则list改变为顺时针排序,flag为false则list改变为逆时针排序
private void isClockwise(List<Point> ps,boolean flag){
if(isClockwise(ps,1)!=flag){
Collections.reverse(ps);
}
}
//传入List,需要判断中间点为位置i
private boolean isClockwise(List<Point> ps,int index){
return isClockwise(ps.get(index- 1 < 0 ? ps.size() - 1 :index-1),
ps.get(index),
ps.get(index+ 1 == ps.size() ? 0 : index+ 1));
}
//三点顺逆时针判断 true 则所有点为顺时针 (x2-x1)*(y3-y1)-(y2-y1)*(x3-x1),在顺时针的基础上,三点逆时针则为凹
private boolean isClockwise(Point p1,Point p2,Point p3){
return (p2.getX() - p1.getX()) * (p3.getY() - p1.getY())
- (p2.getY() - p1.getY()) * (p3.getX() - p1.getX())<0?true:false;
}
//得到cosα
private double getCosAngle(Point v1,Point v2){
return multiplication(v1,v2)/(vectorNorm(v1)*vectorNorm(v2));
}
//向量的模
private double vectorNorm(Point v){
return Math.sqrt(v.getX()*v.getX()+v.getY()*v.getY());
}
//-
private Point minus(Point p1, Point p2){
return new Point(p1.getX() - p2.getX(), p1.getY() - p2.getY());
}
//+
private Point add(Point p1, Point p2){
return new Point(p1.getX() + p2.getX(), p1.getY() + p2.getY());
}
//* 向量数量积
private double multiplication(Point p1, Point p2){
return p1.getX()*p2.getX() + p1.getY()*p2.getY();
}
//向量转单位向量
private Point unit(Point v){
return new Point(v.getX()/vectorNorm(v),v.getY()/vectorNorm(v));
}
//向量取反
private Point vectorReverse(Point v){
return new Point(0-v.getX(),0-v.getY());
}
//数据接近零处理
private double zeroProcess(double a){
return Math.abs(a)<IGNORE_VALUE?0:a;
}
}
判断点是否在区域范围内
点的射线与多边形奇数焦点则在内,偶数焦点则在外
/**
* 判断点是否在多边形内,可以应用于复杂凹多边形
* @return 如果在边上返回0,在内部返回1,在外部返回-1
*/
public static int isInPolygon(Pointlocation point, List<Pointlocation> lists){
int sidesCount = lists.size();
Pointlocation p1,p2;
if(sidesCount < 3)
return -1;
//采用水平左射线计算交点个数
int intersectionCount = 0;
for (int i=0;i<sidesCount;i++){
int j = (i + 1)%sidesCount;//i,j代表边的两个端点
p1 = lists.get(i);
p2 = lists.get(j);
//待测点在区域当前两点右侧
if(p1.getLng()>point.getLng()&&p2.getLng()>point.getLng())
continue;
//待测点纬度平行线不可能与当前两点相交
if((point.getLat()-p1.getLat())*(point.getLat()-p2.getLat()) > 0)
continue;
//得到纬度与point纬度相同的点 (x-x1)/(x2-x1) = (y-y1)/(y2-y1)
double lng = (point.getLat()-p1.getLat())*(p2.getLng()-p1.getLng())/(p2.getLat()-p1.getLat())+p1.getLng();
//必存在焦点
if(Math.abs(lng-p1.getLng())<IGNORE_VALUE)
intersectionCount++;
//该点在线段外则去掉
if(!(lng> Math.min(p1.getLng(),p2.getLng())&&lng<Math.max(p1.getLng(),p2.getLng())))
continue;
//存在交点
if(lng<point.getLng())
intersectionCount++;
}
if(intersectionCount % 2 == 1)
return 1;
else
return -1;
}
最后再判断点到各顶点距离是否为l,可判断在凸角处的拱形区域