feat(positioning): 添加封闭区域匹配功能
parent
961a87226e
commit
c70864998e
|
|
@ -42,3 +42,4 @@ build/
|
||||||
# Local smoke-test logs and extracted dependency metadata
|
# Local smoke-test logs and extracted dependency metadata
|
||||||
/tmp/
|
/tmp/
|
||||||
/logs/
|
/logs/
|
||||||
|
/start/src/main/resources/templates/
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,114 @@
|
||||||
|
package com.zcloud.personnel.positioning.command.query;
|
||||||
|
|
||||||
|
import com.alibaba.cola.exception.BizException;
|
||||||
|
import com.zcloud.personnel.positioning.persistence.dataobject.ClosedAreaDO;
|
||||||
|
import org.springframework.stereotype.Component;
|
||||||
|
import org.springframework.util.StringUtils;
|
||||||
|
|
||||||
|
import java.math.BigDecimal;
|
||||||
|
import java.math.RoundingMode;
|
||||||
|
import java.util.ArrayList;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
@Component
|
||||||
|
class ClosedAreaMatcher {
|
||||||
|
|
||||||
|
List<AreaPolygon> prepare(List<ClosedAreaDO> areas) {
|
||||||
|
if (areas == null || areas.isEmpty()) {
|
||||||
|
return Collections.emptyList();
|
||||||
|
}
|
||||||
|
List<AreaPolygon> result = new ArrayList<>(areas.size());
|
||||||
|
for (ClosedAreaDO area : areas) {
|
||||||
|
result.add(new AreaPolygon(area, parseLocation(area)));
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
AreaPolygon match(BigDecimal lon, BigDecimal lat, List<AreaPolygon> areas) {
|
||||||
|
if (lon == null || lat == null || areas == null) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (AreaPolygon area : areas) {
|
||||||
|
if (contains(lon, lat, area)) {
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
boolean contains(BigDecimal lon, BigDecimal lat, AreaPolygon area) {
|
||||||
|
if (lon == null || lat == null || area == null || area.points.size() < 3) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
boolean inside = false;
|
||||||
|
double x = lon.doubleValue();
|
||||||
|
double y = lat.doubleValue();
|
||||||
|
for (int i = 0, j = area.points.size() - 1; i < area.points.size(); j = i++) {
|
||||||
|
double xi = area.points.get(i)[0].doubleValue();
|
||||||
|
double yi = area.points.get(i)[1].doubleValue();
|
||||||
|
double xj = area.points.get(j)[0].doubleValue();
|
||||||
|
double yj = area.points.get(j)[1].doubleValue();
|
||||||
|
if ((yi > y) != (yj > y)
|
||||||
|
&& x < (xj - xi) * (y - yi) / (yj - yi) + xi) {
|
||||||
|
inside = !inside;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return inside;
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<BigDecimal[]> parseLocation(ClosedAreaDO area) {
|
||||||
|
List<BigDecimal[]> points = new ArrayList<>();
|
||||||
|
if (area == null || !StringUtils.hasText(area.getLocation())) {
|
||||||
|
throw locationFormatException(area);
|
||||||
|
}
|
||||||
|
for (String coordinate : area.getLocation().split(";")) {
|
||||||
|
if (!StringUtils.hasText(coordinate)) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
String[] values = coordinate.trim().split(",");
|
||||||
|
if (values.length != 2) {
|
||||||
|
throw locationFormatException(area);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
points.add(new BigDecimal[]{
|
||||||
|
new BigDecimal(values[0].trim()),
|
||||||
|
new BigDecimal(values[1].trim())
|
||||||
|
});
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
throw locationFormatException(area);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (points.size() < 3) {
|
||||||
|
throw locationFormatException(area);
|
||||||
|
}
|
||||||
|
return points;
|
||||||
|
}
|
||||||
|
|
||||||
|
private BizException locationFormatException(ClosedAreaDO area) {
|
||||||
|
return new BizException("封闭区域经纬度格式错误,区域id=" + (area == null ? null : area.getId()));
|
||||||
|
}
|
||||||
|
|
||||||
|
static class AreaPolygon {
|
||||||
|
private final ClosedAreaDO area;
|
||||||
|
private final List<BigDecimal[]> points;
|
||||||
|
|
||||||
|
private AreaPolygon(ClosedAreaDO area, List<BigDecimal[]> points) {
|
||||||
|
this.area = area;
|
||||||
|
this.points = points;
|
||||||
|
}
|
||||||
|
|
||||||
|
ClosedAreaDO getArea() {
|
||||||
|
return area;
|
||||||
|
}
|
||||||
|
|
||||||
|
BigDecimal center(int coordinateIndex) {
|
||||||
|
BigDecimal total = BigDecimal.ZERO;
|
||||||
|
for (BigDecimal[] point : points) {
|
||||||
|
total = total.add(point[coordinateIndex]);
|
||||||
|
}
|
||||||
|
return total.divide(BigDecimal.valueOf(points.size()), 12, RoundingMode.HALF_UP)
|
||||||
|
.stripTrailingZeros();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,13 @@
|
||||||
|
package com.zcloud.personnel.positioning.persistence.dataobject;
|
||||||
|
|
||||||
|
import lombok.Data;
|
||||||
|
|
||||||
|
@Data
|
||||||
|
public class ClosedAreaDO {
|
||||||
|
private Long id;
|
||||||
|
private String closedAreaName;
|
||||||
|
private String closedAreaType;
|
||||||
|
private Long jurisdictionalCorpId;
|
||||||
|
private String jurisdictionalCorpName;
|
||||||
|
private String location;
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue