package com.xiaopeng.map.utils;
|
|
import lombok.extern.slf4j.Slf4j;
|
import org.locationtech.jts.geom.Coordinate;
|
import org.locationtech.jts.geom.GeometryFactory;
|
import org.locationtech.jts.geom.LinearRing;
|
import org.locationtech.jts.geom.Polygon;
|
import org.springframework.stereotype.Component;
|
|
@Component
|
@Slf4j
|
public class PolygonOverlapService {
|
|
/**
|
* 检查两个多边形是否重叠
|
* @param polygon1Points 多边形1的坐标点 "lng1_lat1,lng2_lat2,..."
|
* @param polygon2Points 多边形2的坐标点 "lng1_lat1,lng2_lat2,..."
|
* @return 是否重叠
|
*/
|
public boolean checkPolygonOverlap(String polygon1Points, String polygon2Points) {
|
try {
|
Polygon polygon1 = createPolygonFromPoints(polygon1Points);
|
Polygon polygon2 = createPolygonFromPoints(polygon2Points);
|
|
// 使用 JTS 库判断多边形是否相交
|
return polygon1.intersects(polygon2);
|
} catch (Exception e) {
|
log.error("多边形重叠检测失败", e);
|
return false; // 发生错误时默认不重叠,可根据业务需要调整
|
}
|
}
|
|
/**
|
* 从坐标点字符串创建多边形
|
* @param pointsStr 坐标点字符串 "lng1_lat1,lng2_lat2,lng3_lat3,..."
|
* @return JTS Polygon 对象
|
*/
|
private Polygon createPolygonFromPoints(String pointsStr) {
|
if (!pointsStr.contains(",")) {
|
throw new IllegalArgumentException("无效的坐标点格式");
|
}
|
|
String[] pointPairs = pointsStr.split(",");
|
Coordinate[] coordinates = new Coordinate[pointPairs.length + 1]; // +1 是为了闭合多边形
|
|
for (int i = 0; i < pointPairs.length; i++) {
|
String[] lngLat = pointPairs[i].trim().split("_");
|
if (lngLat.length != 2) {
|
throw new IllegalArgumentException("坐标点格式错误: " + pointPairs[i]);
|
}
|
|
double lng = Double.parseDouble(lngLat[0]);
|
double lat = Double.parseDouble(lngLat[1]);
|
coordinates[i] = new Coordinate(lng, lat);
|
}
|
|
// 闭合多边形,最后一个点与第一个点相同
|
coordinates[coordinates.length - 1] = coordinates[0];
|
|
GeometryFactory geometryFactory = new GeometryFactory();
|
LinearRing linearRing = geometryFactory.createLinearRing(coordinates);
|
return geometryFactory.createPolygon(linearRing);
|
}
|
|
|
/**
|
* 判断一个坐标点位是否在一个坐标区域内
|
* @param longitude 经度
|
* @param latitude 纬度
|
* @param fencePoints 围栏点位字符串,格式为 "lng1_lat1,lng2_lat2,..."
|
* @return 是否在区域内 true在 false不在
|
*/
|
public boolean isPointInPolygon(double longitude, double latitude, String fencePoints) {
|
if (fencePoints == null || fencePoints.trim().isEmpty()) {
|
return false;
|
}
|
|
// 解析围栏点位
|
String[] points = fencePoints.split(",");
|
if (points.length < 3) {
|
return false;
|
}
|
|
int n = points.length;
|
boolean inside = false;
|
|
double x = longitude, y = latitude;
|
double[] xs = new double[n];
|
double[] ys = new double[n];
|
|
for (int i = 0; i < n; i++) {
|
String[] coords = points[i].split("_");
|
if (coords.length != 2) {
|
continue;
|
}
|
try {
|
xs[i] = Double.parseDouble(coords[0]);
|
ys[i] = Double.parseDouble(coords[1]);
|
} catch (NumberFormatException e) {
|
return false;
|
}
|
}
|
|
// 使用射线法判断点是否在多边形内
|
for (int i = 0, j = n - 1; i < n; j = i++) {
|
if (((ys[i] > y) != (ys[j] > y)) &&
|
(x < (xs[j] - xs[i]) * (y - ys[i]) / (ys[j] - ys[i]) + xs[i])) {
|
inside = !inside;
|
}
|
}
|
|
return inside;
|
}
|
|
|
/**
|
* 判断一个坐标区域(多边形A)是否完全在另一个坐标区域(多边形B)内
|
* 原理:如果多边形A的所有顶点都在多边形B内,则认为A在B内
|
* @param innerFencePoints 内部围栏点位字符串(待判断是否在外的围栏),格式为 "lng1_lat1,lng2_lat2,..."
|
* @param outerFencePoints 外部围栏点位字符串(参考围栏),格式为 "lng1_lat1,lng2_lat2,..."
|
* @return 是否在内 true在内 false不在
|
*/
|
|
public boolean isPolygonInPolygon(String innerFencePoints, String outerFencePoints) {
|
if (innerFencePoints == null || innerFencePoints.trim().isEmpty()
|
|| outerFencePoints == null || outerFencePoints.trim().isEmpty()) {
|
return false;
|
}
|
|
// 解析内部围栏点位
|
String[] innerPoints = innerFencePoints.split(",");
|
if (innerPoints.length < 3) {
|
return false;
|
}
|
|
// 检查内部围栏的每一个顶点是否都在外部围栏内
|
for (String pointStr : innerPoints) {
|
String[] coords = pointStr.split("_");
|
if (coords.length != 2) {
|
continue;
|
}
|
try {
|
double lon = Double.parseDouble(coords[0]);
|
double lat = Double.parseDouble(coords[1]);
|
|
// 如果有一个顶点不在外部围栏内,则整个区域不在内部
|
if (!isPointInPolygon(lon, lat, outerFencePoints)) {
|
return false;
|
}
|
} catch (NumberFormatException e) {
|
return false;
|
}
|
}
|
|
return true;
|
}
|
|
|
/**
|
* 根据围栏点位字符串计算中心点坐标
|
* @param fencePoints 围栏点位字符串,格式为 "lng1_lat1,lng2_lat2,..."
|
* @return double[0] 经度, double[1] 纬度
|
*/
|
public double[] calculateCenterPoint(String fencePoints) {
|
if (fencePoints == null || fencePoints.trim().isEmpty()) {
|
return new double[]{0.0, 0.0};
|
}
|
|
String[] points = fencePoints.split(",");
|
if (points.length == 0) {
|
return new double[]{0.0, 0.0};
|
}
|
|
double sumLon = 0.0;
|
double sumLat = 0.0;
|
int count = 0;
|
|
for (String point : points) {
|
String[] coords = point.split("_");
|
if (coords.length == 2) {
|
try {
|
double lon = Double.parseDouble(coords[0]);
|
double lat = Double.parseDouble(coords[1]);
|
sumLon += lon;
|
sumLat += lat;
|
count++;
|
} catch (NumberFormatException e) {
|
// 忽略解析错误的点
|
}
|
}
|
}
|
|
if (count == 0) {
|
return new double[]{0.0, 0.0};
|
}
|
|
double centerLon = sumLon / count;
|
double centerLat = sumLat / count;
|
|
return new double[]{centerLon, centerLat};
|
}
|
|
|
|
|
}
|