diff --git a/.gitignore b/.gitignore index d5a5837..399d75c 100644 --- a/.gitignore +++ b/.gitignore @@ -42,3 +42,4 @@ build/ # Local smoke-test logs and extracted dependency metadata /tmp/ /logs/ +/start/src/main/resources/templates/ diff --git a/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/ClosedAreaMatcher.java b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/ClosedAreaMatcher.java new file mode 100644 index 0000000..a1b470a --- /dev/null +++ b/web-app/src/main/java/com/zcloud/personnel/positioning/command/query/ClosedAreaMatcher.java @@ -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 prepare(List areas) { + if (areas == null || areas.isEmpty()) { + return Collections.emptyList(); + } + List 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 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 parseLocation(ClosedAreaDO area) { + List 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 points; + + private AreaPolygon(ClosedAreaDO area, List 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(); + } + } +} diff --git a/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/dataobject/ClosedAreaDO.java b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/dataobject/ClosedAreaDO.java new file mode 100644 index 0000000..ca5a552 --- /dev/null +++ b/web-infrastructure/src/main/java/com/zcloud/personnel/positioning/persistence/dataobject/ClosedAreaDO.java @@ -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; +}