2021-09-09 23:24:28 +08:00
|
|
|
|
package ink.wgink.util.point;
|
|
|
|
|
|
|
|
|
|
import java.math.BigDecimal;
|
|
|
|
|
import java.util.List;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* When you feel like quitting. Think about why you started
|
|
|
|
|
* 当你想要放弃的时候,想想当初你为何开始
|
|
|
|
|
*
|
|
|
|
|
* @ClassName: PointUtil
|
|
|
|
|
* @Description: 点工具类
|
|
|
|
|
* @Author: WangGeng
|
|
|
|
|
* @Date: 2019-08-09 22:43
|
|
|
|
|
* @Version: 1.0
|
|
|
|
|
**/
|
|
|
|
|
public class PointUtil {
|
|
|
|
|
|
|
|
|
|
private static final double EARTH_RADIUS = 637_1393.00D;
|
|
|
|
|
private static final double RADIAN = Math.PI / 180.00D;
|
|
|
|
|
private static final double HALF = 0.5D;
|
|
|
|
|
|
2022-11-15 20:55:56 +08:00
|
|
|
|
public static final double MIN_LAT = -90;
|
|
|
|
|
public static final double MAX_LAT = 90;
|
|
|
|
|
public static final double MIN_LNG = -180;
|
|
|
|
|
public static final double MAX_LNG = 180;
|
|
|
|
|
|
2021-09-09 23:24:28 +08:00
|
|
|
|
/**
|
|
|
|
|
* 点在多边形内
|
|
|
|
|
*
|
|
|
|
|
* @param point
|
|
|
|
|
* @param points
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static boolean isPointInPoly(Point point, List<Point> points) {
|
|
|
|
|
int N = points.size();
|
|
|
|
|
//如果点位于多边形的顶点或边上,也算做点在多边形内,直接返回true
|
|
|
|
|
boolean boundOrVertex = true;
|
|
|
|
|
//cross points count of x
|
|
|
|
|
int intersectCount = 0;
|
|
|
|
|
//浮点类型计算时候与0比较时候的容差
|
|
|
|
|
double precision = 2e-10;
|
|
|
|
|
//neighbour bound vertices
|
|
|
|
|
Point p1, p2;
|
|
|
|
|
//当前点
|
|
|
|
|
Point p = point;
|
|
|
|
|
//left vertex
|
|
|
|
|
p1 = points.get(0);
|
|
|
|
|
//check all rays
|
|
|
|
|
for (int i = 1; i <= N; ++i) {
|
|
|
|
|
if (p.equals(p1)) {
|
|
|
|
|
//p is an vertex
|
|
|
|
|
return boundOrVertex;
|
|
|
|
|
}
|
|
|
|
|
//right vertex
|
|
|
|
|
p2 = points.get(i % N);
|
|
|
|
|
//ray is outside of our interests
|
|
|
|
|
if (p.getX() < Math.min(p1.getX(), p2.getX()) || p.getX() > Math.max(p1.getX(), p2.getX())) {
|
|
|
|
|
p1 = p2;
|
|
|
|
|
continue;//next ray left point
|
|
|
|
|
}
|
|
|
|
|
//ray is crossing over by the algorithm (common part of)
|
|
|
|
|
if (p.getX() > Math.min(p1.getX(), p2.getX()) && p.getX() < Math.max(p1.getX(), p2.getX())) {
|
|
|
|
|
//x is before of ray
|
|
|
|
|
if (p.getY() <= Math.max(p1.getY(), p2.getY())) {
|
|
|
|
|
//overlies on a horizontal ray
|
|
|
|
|
if (p1.getX() == p2.getX() && p.getY() >= Math.min(p1.getY(), p2.getY())) {
|
|
|
|
|
return boundOrVertex;
|
|
|
|
|
}
|
|
|
|
|
//ray is vertical
|
|
|
|
|
if (p1.getY() == p2.getY()) {
|
|
|
|
|
//overlies on a vertical ray
|
|
|
|
|
if (p1.getY() == p.getY()) {
|
|
|
|
|
return boundOrVertex;
|
|
|
|
|
} else {
|
|
|
|
|
//before ray
|
|
|
|
|
++intersectCount;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//cross point on the left side
|
|
|
|
|
//cross point of y
|
|
|
|
|
double xinters = (p.getX() - p1.getX()) * (p2.getY() - p1.getY()) / (p2.getX() - p1.getX()) + p1.getY();
|
|
|
|
|
//overlies on a ray
|
|
|
|
|
if (Math.abs(p.getY() - xinters) < precision) {
|
|
|
|
|
return boundOrVertex;
|
|
|
|
|
}
|
|
|
|
|
//before ray
|
|
|
|
|
if (p.getY() < xinters) {
|
|
|
|
|
++intersectCount;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
//special case when ray is crossing through the vertex
|
|
|
|
|
//p crossing over p2
|
|
|
|
|
if (p.getX() == p2.getX() && p.getY() <= p2.getY()) {
|
|
|
|
|
//next vertex
|
|
|
|
|
Point p3 = points.get((i + 1) % N);
|
|
|
|
|
//p.x lies between p1.x & p3.x
|
|
|
|
|
if (p.getX() >= Math.min(p1.getX(), p3.getX()) && p.getX() <= Math.max(p1.getX(), p3.getX())) {
|
|
|
|
|
++intersectCount;
|
|
|
|
|
} else {
|
|
|
|
|
intersectCount += 2;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
//next ray left point
|
|
|
|
|
p1 = p2;
|
|
|
|
|
}
|
|
|
|
|
if (intersectCount % 2 == 0) {
|
|
|
|
|
//偶数在多边形外
|
|
|
|
|
return false;
|
|
|
|
|
} else {
|
|
|
|
|
//奇数在多边形内
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 判断是否在圆形内
|
|
|
|
|
* <p>
|
|
|
|
|
* 判断点与圆心之间的距离和圆半径的关系
|
|
|
|
|
*
|
|
|
|
|
* @param p
|
|
|
|
|
* @param c
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static int distencePC(Point p, Circle c) {
|
|
|
|
|
int s;
|
|
|
|
|
double d2 = Math.hypot((p.getX() - c.getPoint().getX()), (p.getY() - c.getPoint().getY()));
|
|
|
|
|
double r = c.getR();
|
|
|
|
|
if (d2 > r) {
|
|
|
|
|
// 圆外
|
|
|
|
|
s = -1;
|
|
|
|
|
} else if (d2 < r) {
|
|
|
|
|
// 圆内
|
|
|
|
|
s = 1;
|
|
|
|
|
} else {
|
|
|
|
|
// 圆上
|
|
|
|
|
s = 0;
|
|
|
|
|
}
|
|
|
|
|
return s;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 计算距离,Point的x为纬度,y为经度
|
|
|
|
|
*
|
|
|
|
|
* @param point1
|
|
|
|
|
* @param point2
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static double getDistance(Point point1, Point point2) {
|
|
|
|
|
double lat1 = point1.getX();
|
|
|
|
|
double lon1 = point1.getY();
|
|
|
|
|
double lat2 = point2.getX();
|
|
|
|
|
double lon2 = point2.getY();
|
|
|
|
|
double x, y, a, b, distance;
|
|
|
|
|
|
|
|
|
|
lat1 *= RADIAN;
|
|
|
|
|
lat2 *= RADIAN;
|
|
|
|
|
x = lat1 - lat2;
|
|
|
|
|
y = lon1 - lon2;
|
|
|
|
|
y *= RADIAN;
|
|
|
|
|
a = Math.sin(x * HALF);
|
|
|
|
|
b = Math.sin(y * HALF);
|
|
|
|
|
distance = EARTH_RADIUS * Math.asin(Math.sqrt(a * a + Math.cos(lat1) * Math.cos(lat2) * b * b)) / HALF;
|
|
|
|
|
return new BigDecimal(distance).setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 计算面积
|
|
|
|
|
*
|
|
|
|
|
* @param points
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static double getSqua(List<Point> points) {
|
|
|
|
|
double area = 0;
|
|
|
|
|
int size = points.size();
|
|
|
|
|
if (size > 2) {
|
|
|
|
|
double LowX = 0.0;
|
|
|
|
|
double LowY = 0.0;
|
|
|
|
|
double MiddleX = 0.0;
|
|
|
|
|
double MiddleY = 0.0;
|
|
|
|
|
double HighX = 0.0;
|
|
|
|
|
double HighY = 0.0;
|
|
|
|
|
|
|
|
|
|
double AM = 0.0;
|
|
|
|
|
double BM = 0.0;
|
|
|
|
|
double CM = 0.0;
|
|
|
|
|
|
|
|
|
|
double AL = 0.0;
|
|
|
|
|
double BL = 0.0;
|
|
|
|
|
double CL = 0.0;
|
|
|
|
|
|
|
|
|
|
double AH = 0.0;
|
|
|
|
|
double BH = 0.0;
|
|
|
|
|
double CH = 0.0;
|
|
|
|
|
|
|
|
|
|
double CoefficientL = 0.0;
|
|
|
|
|
double CoefficientH = 0.0;
|
|
|
|
|
|
|
|
|
|
double ALtangent = 0.0;
|
|
|
|
|
double BLtangent = 0.0;
|
|
|
|
|
double CLtangent = 0.0;
|
|
|
|
|
|
|
|
|
|
double AHtangent = 0.0;
|
|
|
|
|
double BHtangent = 0.0;
|
|
|
|
|
double CHtangent = 0.0;
|
|
|
|
|
|
|
|
|
|
double ANormalLine = 0.0;
|
|
|
|
|
double BNormalLine = 0.0;
|
|
|
|
|
double CNormalLine = 0.0;
|
|
|
|
|
|
|
|
|
|
double OrientationValue = 0.0;
|
|
|
|
|
|
|
|
|
|
double AngleCos = 0.0;
|
|
|
|
|
|
|
|
|
|
double Sum1 = 0.0;
|
|
|
|
|
double Sum2 = 0.0;
|
|
|
|
|
double Count2 = 0;
|
|
|
|
|
double Count1 = 0;
|
|
|
|
|
|
|
|
|
|
double Sum = 0.0;
|
|
|
|
|
double Radius = 6378000;
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < size; i++) {
|
|
|
|
|
if (i == 0) {
|
|
|
|
|
LowX = points.get(size - 1).getX() * Math.PI / 180;
|
|
|
|
|
LowY = points.get(size - 1).getY() * Math.PI / 180;
|
|
|
|
|
MiddleX = points.get(0).getX() * Math.PI / 180;
|
|
|
|
|
MiddleY = points.get(0).getY() * Math.PI / 180;
|
|
|
|
|
HighX = points.get(1).getX() * Math.PI / 180;
|
|
|
|
|
HighY = points.get(1).getY() * Math.PI / 180;
|
|
|
|
|
} else if (i == size - 1) {
|
|
|
|
|
LowX = points.get(size - 2).getX() * Math.PI / 180;
|
|
|
|
|
LowY = points.get(size - 2).getY() * Math.PI / 180;
|
|
|
|
|
MiddleX = points.get(size - 1).getX() * Math.PI / 180;
|
|
|
|
|
MiddleY = points.get(size - 1).getY() * Math.PI / 180;
|
|
|
|
|
HighX = points.get(0).getX() * Math.PI / 180;
|
|
|
|
|
HighY = points.get(0).getY() * Math.PI / 180;
|
|
|
|
|
} else {
|
|
|
|
|
LowX = points.get(i - 1).getX() * Math.PI / 180;
|
|
|
|
|
LowY = points.get(i - 1).getY() * Math.PI / 180;
|
|
|
|
|
MiddleX = points.get(i).getX() * Math.PI / 180;
|
|
|
|
|
MiddleY = points.get(i).getY() * Math.PI / 180;
|
|
|
|
|
HighX = points.get(i + 1).getX() * Math.PI / 180;
|
|
|
|
|
HighY = points.get(i + 1).getY() * Math.PI / 180;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
AM = Math.cos(MiddleY) * Math.cos(MiddleX);
|
|
|
|
|
BM = Math.cos(MiddleY) * Math.sin(MiddleX);
|
|
|
|
|
CM = Math.sin(MiddleY);
|
|
|
|
|
AL = Math.cos(LowY) * Math.cos(LowX);
|
|
|
|
|
BL = Math.cos(LowY) * Math.sin(LowX);
|
|
|
|
|
CL = Math.sin(LowY);
|
|
|
|
|
AH = Math.cos(HighY) * Math.cos(HighX);
|
|
|
|
|
BH = Math.cos(HighY) * Math.sin(HighX);
|
|
|
|
|
CH = Math.sin(HighY);
|
|
|
|
|
|
|
|
|
|
CoefficientL = (AM * AM + BM * BM + CM * CM) / (AM * AL + BM * BL + CM * CL);
|
|
|
|
|
CoefficientH = (AM * AM + BM * BM + CM * CM) / (AM * AH + BM * BH + CM * CH);
|
|
|
|
|
|
|
|
|
|
ALtangent = CoefficientL * AL - AM;
|
|
|
|
|
BLtangent = CoefficientL * BL - BM;
|
|
|
|
|
CLtangent = CoefficientL * CL - CM;
|
|
|
|
|
AHtangent = CoefficientH * AH - AM;
|
|
|
|
|
BHtangent = CoefficientH * BH - BM;
|
|
|
|
|
CHtangent = CoefficientH * CH - CM;
|
|
|
|
|
|
|
|
|
|
AngleCos = (AHtangent * ALtangent + BHtangent * BLtangent + CHtangent * CLtangent) / (
|
|
|
|
|
Math.sqrt(AHtangent * AHtangent + BHtangent * BHtangent + CHtangent * CHtangent)
|
|
|
|
|
* Math.sqrt(ALtangent * ALtangent + BLtangent * BLtangent
|
|
|
|
|
+ CLtangent * CLtangent));
|
|
|
|
|
|
|
|
|
|
AngleCos = Math.acos(AngleCos);
|
|
|
|
|
|
|
|
|
|
ANormalLine = BHtangent * CLtangent - CHtangent * BLtangent;
|
|
|
|
|
BNormalLine = 0 - (AHtangent * CLtangent - CHtangent * ALtangent);
|
|
|
|
|
CNormalLine = AHtangent * BLtangent - BHtangent * ALtangent;
|
|
|
|
|
|
|
|
|
|
if (AM != 0) {
|
|
|
|
|
OrientationValue = ANormalLine / AM;
|
|
|
|
|
} else if (BM != 0) {
|
|
|
|
|
OrientationValue = BNormalLine / BM;
|
|
|
|
|
} else {
|
|
|
|
|
OrientationValue = CNormalLine / CM;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (OrientationValue > 0) {
|
|
|
|
|
Sum1 += AngleCos;
|
|
|
|
|
Count1++;
|
|
|
|
|
|
|
|
|
|
} else {
|
|
|
|
|
Sum2 += AngleCos;
|
|
|
|
|
Count2++;
|
|
|
|
|
//Sum +=2*Math.PI-AngleCos;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
if (Sum1 > Sum2) {
|
|
|
|
|
Sum = Sum1 + (2 * Math.PI * Count2 - Sum2);
|
|
|
|
|
} else {
|
|
|
|
|
Sum = (2 * Math.PI * Count1 - Sum1) + Sum2;
|
|
|
|
|
}
|
|
|
|
|
//平方米
|
|
|
|
|
area = (Sum - (size - 2) * Math.PI) * Radius * Radius;
|
|
|
|
|
}
|
|
|
|
|
return Math.abs(area);
|
|
|
|
|
}
|
|
|
|
|
|
2022-11-15 20:55:56 +08:00
|
|
|
|
/**
|
|
|
|
|
* 计算中心点
|
|
|
|
|
*
|
|
|
|
|
* @param mPoints
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static Point getCenterPoint(List<Point> mPoints) {
|
|
|
|
|
double latitude = (getMinLatitude(mPoints) + getMaxLatitude(mPoints)) / 2;
|
|
|
|
|
double longitude = (getMinLongitude(mPoints) + getMaxLongitude(mPoints)) / 2;
|
|
|
|
|
return new Point(latitude, longitude);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 获取不规则多边形重心点
|
|
|
|
|
*
|
|
|
|
|
* @param mPoints
|
|
|
|
|
* @return
|
|
|
|
|
*/
|
|
|
|
|
public static Point getCenterOfGravityPoint(List<Point> mPoints) {
|
|
|
|
|
double area = 0.0;//多边形面积
|
|
|
|
|
double Gx = 0.0, Gy = 0.0;// 重心的x、y
|
|
|
|
|
for (int i = 1; i <= mPoints.size(); i++) {
|
|
|
|
|
double iLat = mPoints.get(i % mPoints.size()).getX();
|
|
|
|
|
double iLng = mPoints.get(i % mPoints.size()).getY();
|
|
|
|
|
double nextLat = mPoints.get(i - 1).getX();
|
|
|
|
|
double nextLng = mPoints.get(i - 1).getY();
|
|
|
|
|
double temp = (iLat * nextLng - iLng * nextLat) / 2.0;
|
|
|
|
|
area += temp;
|
|
|
|
|
Gx += temp * (iLat + nextLat) / 3.0;
|
|
|
|
|
Gy += temp * (iLng + nextLng) / 3.0;
|
|
|
|
|
}
|
|
|
|
|
Gx = Gx / area;
|
|
|
|
|
Gy = Gy / area;
|
|
|
|
|
return new Point(Gx, Gy);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 经度最小值
|
|
|
|
|
public static double getMinLongitude(List<Point> mPoints) {
|
|
|
|
|
double minLongitude = MAX_LNG;
|
|
|
|
|
if (mPoints.size() > 0) {
|
|
|
|
|
minLongitude = mPoints.get(0).getY();
|
|
|
|
|
for (Point latlng : mPoints) {
|
|
|
|
|
// 经度最小值
|
|
|
|
|
if (latlng.getY() < minLongitude)
|
|
|
|
|
minLongitude = latlng.getY();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return minLongitude;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 经度最大值
|
|
|
|
|
public static double getMaxLongitude(List<Point> mPoints) {
|
|
|
|
|
double maxLongitude = MIN_LNG;
|
|
|
|
|
if (mPoints.size() > 0) {
|
|
|
|
|
maxLongitude = mPoints.get(0).getY();
|
|
|
|
|
for (Point latlng : mPoints) {
|
|
|
|
|
// 经度最大值
|
|
|
|
|
if (latlng.getY() > maxLongitude)
|
|
|
|
|
maxLongitude = latlng.getY();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return maxLongitude;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 纬度最小值
|
|
|
|
|
public static double getMinLatitude(List<Point> mPoints) {
|
|
|
|
|
double minLatitude = MAX_LAT;
|
|
|
|
|
if (mPoints.size() > 0) {
|
|
|
|
|
minLatitude = mPoints.get(0).getX();
|
|
|
|
|
for (Point latlng : mPoints) {
|
|
|
|
|
// 纬度最小值
|
|
|
|
|
if (latlng.getX() < minLatitude)
|
|
|
|
|
minLatitude = latlng.getX();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return minLatitude;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 纬度最大值
|
|
|
|
|
public static double getMaxLatitude(List<Point> mPoints) {
|
|
|
|
|
double maxLatitude = MIN_LAT;
|
|
|
|
|
if (mPoints.size() > 0) {
|
|
|
|
|
maxLatitude = mPoints.get(0).getX();
|
|
|
|
|
for (Point latlng : mPoints) {
|
|
|
|
|
// 纬度最大值
|
|
|
|
|
if (latlng.getX() > maxLatitude)
|
|
|
|
|
maxLatitude = latlng.getX();
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return maxLatitude;
|
|
|
|
|
}
|
|
|
|
|
|
2021-09-09 23:24:28 +08:00
|
|
|
|
}
|