diff --git a/pav-server/src/main/java/com/palnet/comn/utils/AirAreaUtils.java b/pav-server/src/main/java/com/palnet/comn/utils/AirAreaUtils.java new file mode 100644 index 00000000..e9fa5196 --- /dev/null +++ b/pav-server/src/main/java/com/palnet/comn/utils/AirAreaUtils.java @@ -0,0 +1,374 @@ +package com.palnet.comn.utils; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonValue; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.locationtech.jts.geom.Coordinate; +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.geom.GeometryFactory; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.stream.Collectors; + +// TODO areaUtils, airspaceUtils merge +// TODO airspaceUtils의 검증 관련 로직은 해당 표시에서 적용. +@Slf4j +public class AirAreaUtils { + + private final String RESOURCE_BASE_PATH = "air/airspace/"; + private final String COMMON_PATH = "common/"; + private final String CUSTOM_PATH = "custom/"; + private final String SUFFIX_FILE_NAME = "Area.json"; + + private Map> airAreaMap; + private GeometryFactory geometryFactory; + + private AirAreaUtils() { + log.info("===== AirAreaUtils init ====="); + this.init(); + } + + public static AirAreaUtils getInstance() { + return LazyHolder.INSTANCE; + } + + private static class LazyHolder { + private static final AirAreaUtils INSTANCE = new AirAreaUtils(); + } + + // TODO 초기화 + private void init() { + geometryFactory = new GeometryFactory(); + // TODO 데이터 로드 + } + + /** + * air area와 중복 여부(고도 무시) + * + * @param targetfeatureInfo + * @return + */ + public boolean isDuplicatedAirAreaIgnoreElev(FeatureInfo targetfeatureInfo) { + return isDuplicatedAirAreaIgnoreElev(targetfeatureInfo, AirAreaType.ALL); + } + + /** + * air area와 중복 여부(고도 무시) + * + * @param targetFeatureInfo + * @param airAreaType + * @return + */ + public boolean isDuplicatedAirAreaIgnoreElev(FeatureInfo targetFeatureInfo, AirAreaType airAreaType) { + if (targetFeatureInfo == null) return false; + if (airAreaMap == null || airAreaMap.isEmpty()) { + log.warn("airAreaMap is empty"); + return false; + } + if (airAreaType == null) airAreaType = AirAreaType.ALL; + + Geometry targetGeometry = targetFeatureInfo.getGeometry(); + if (targetGeometry == null) return false; + + List duplicatedAirAreaFeatureInfos = getDuplicatedAirAreaIgnoreElev(targetFeatureInfo, airAreaType); + + return !duplicatedAirAreaFeatureInfos.isEmpty(); + } + + /** + * air area와 중복 여부(고도 포함) + * + * @param featureInfo + * @return + */ + public boolean isDuplicatedAirArea(FeatureInfo featureInfo) { + return isDuplicatedAirArea(featureInfo, AirAreaType.ALL); + } + + /** + * air area와 중복 여부(고도 포함) + * + * @param targetFeatureInfo + * @param airAreaType + * @return + */ + public boolean isDuplicatedAirArea(FeatureInfo targetFeatureInfo, AirAreaType airAreaType) { + if (targetFeatureInfo == null) return false; + if (airAreaMap == null || airAreaMap.isEmpty()) { + log.warn("airAreaMap is empty"); + return false; + } + if (airAreaType == null) airAreaType = AirAreaType.ALL; + + List duplicatedAirAreaFeatureInfos = getDuplicatedAirArea(targetFeatureInfo, airAreaType); + return !duplicatedAirAreaFeatureInfos.isEmpty(); + } + + /** + * 중복되는 air area FeatureInfo 반환(고도 무시) + * + * @param targetFeatureInfo + * @return + */ + public List getDuplicatedAirAreaIgnoreElev(FeatureInfo targetFeatureInfo) { + return getDuplicatedAirAreaIgnoreElev(targetFeatureInfo, AirAreaType.ALL); + } + + /** + * 중복되는 air area FeatureInfo 반환(고도 무시) + * + * @param targetFeatureInfo + * @param airAreaType + * @return + */ + public List getDuplicatedAirAreaIgnoreElev(FeatureInfo targetFeatureInfo, AirAreaType airAreaType) { + Geometry targetGeometry = targetFeatureInfo.getGeometry(); + if (targetGeometry == null) return new ArrayList<>(); + + List airAreaFeatureInfos = null; + if (AirAreaType.ALL == airAreaType) { + airAreaFeatureInfos = airAreaMap.values().stream() + .flatMap(List::stream) + .collect(Collectors.toList()); + } else { + airAreaFeatureInfos = airAreaMap.get(airAreaType.getCode()); + } + + return airAreaFeatureInfos.stream() + .filter(airAreaFeatureInfo -> { + Geometry airAreaGeometry = airAreaFeatureInfo.getGeometry(); + if (airAreaGeometry == null) return false; + return airAreaGeometry.intersects(targetGeometry); + }) + .collect(Collectors.toList()); + } + + /** + * 중복되는 air area FeatureInfo 반환(고도 포함) + * + * @param targetFeatureInfo + * @return + */ + public List getDuplicatedAirArea(FeatureInfo targetFeatureInfo) { + return getDuplicatedAirArea(targetFeatureInfo, AirAreaType.ALL); + } + + /** + * 중복되는 air area FeatureInfo 반환(고도 포함) + * + * @param targetFeatureInfo + * @param airAreaType + * @return + */ + public List getDuplicatedAirArea(FeatureInfo targetFeatureInfo, AirAreaType airAreaType) { + List duplicatedAirAreaFeatureInfosIgnoreElev = getDuplicatedAirAreaIgnoreElev(targetFeatureInfo, airAreaType); + final double targetHighElev = targetFeatureInfo.getHighElev() != null ? targetFeatureInfo.getHighElev() : 0D; + return duplicatedAirAreaFeatureInfosIgnoreElev.stream().filter(airAreaFeatureInfo -> { + final double airAreaHighElev = airAreaFeatureInfo.getHighElev() != null ? airAreaFeatureInfo.getHighElev() : 0D; + return targetHighElev <= airAreaHighElev; + }).collect(Collectors.toList()); + } + + /** + * FeatureInfo 생성 + * + * @param coordinates + * @return + */ + public FeatureInfo craeteFeatureInfo(List coordinates) { + return craeteFeatureInfo(coordinates, GeometryType.POLYGON, null); + } + + /** + * FeatureInfo 생성 + * + * @param coordinates + * @param geometryType + * @param highElev + * @return + */ + public FeatureInfo craeteFeatureInfo(List coordinates, GeometryType geometryType, Double highElev) { + return craeteFeatureInfo(coordinates, geometryType, null, null, null, highElev); + } + + /** + * FeatureInfo 생성 + * + * @param coordinates + * @param geometryType + * @param name + * @param description + * @param type + * @param highElev + * @return + */ + public FeatureInfo craeteFeatureInfo(List coordinates, GeometryType geometryType, String name, String description, String type, Double highElev) { + return craeteFeatureInfo(coordinates, geometryType, name, description, type, null, highElev, true); + } + + /** + * FeatureInfo 생성 + * + * @param coordinates + * @param geometryType + * @param name + * @param description + * @param type + * @param lowElev + * @param highElev + * @param isUse + * @return + */ + public FeatureInfo craeteFeatureInfo(List coordinates, GeometryType geometryType, String name, String description, String type, Double lowElev, Double highElev, boolean isUse) { + Geometry geometry = createGeometryByCoordinate(coordinates, geometryType); + return FeatureInfo.builder() + .name(name) + .description(description) + .type(type) + .lowElev(lowElev) + .highElev(highElev) + .isUse(isUse) + .geometry(geometry) + .build(); + } + + /** + * Geometry 생성 + * 좌표 목록과 GeometryType을 이용하여 Geometry 생성 + * + * @param target + * @param type + * @return + */ + public Geometry createGeometryByCoordinate(List target, GeometryType type) { + if (target == null || target.isEmpty()) return null; + + if (type == null) { + if (target.size() == 1) { + type = GeometryType.POINT; + } else if (target.size() == 2) { + type = GeometryType.LINESTRING; + } else { + type = GeometryType.POLYGON; + } + } + + Geometry geometry = null; + if (GeometryType.POLYGON == type) { + if (target.get(0) != target.get(target.size() - 1)) { + target.add(target.get(0)); + } + geometry = this.geometryFactory.createPolygon(target.toArray(new Coordinate[0])); + } else if (GeometryType.LINESTRING == type) { + geometry = this.geometryFactory.createLineString(target.toArray(new Coordinate[0])); + } else if (GeometryType.POINT == type) { + geometry = this.geometryFactory.createPoint(target.get(0)); + } + return geometry; + } + + + @Data + @NoArgsConstructor + @AllArgsConstructor + @Builder + public static class FeatureInfo { + private String name; + private String description; + private String type; + private Double lowElev; + private Double highElev; + private boolean isUse; + private Geometry geometry; + } + + /** + * AirAreaType - 모두, 김포 + */ + public enum AirAreaType { + ALL("all"), + GIMPO("gimpo"); + + private final String code; + + @JsonValue + public String getCode() { + return code; + } + + AirAreaType(String code) { + this.code = code; + } + + @JsonCreator + public static AirAreaType fromCode(String code) { + for (AirAreaType type : AirAreaType.values()) { + if (type.code.equals(code)) { + return type; + } + } + log.warn("Unknown AirAreaType code: {}", code); + return null; + } + } + + /** + * GeometryType - Point, LineString, Polygon + */ + public enum GeometryType { + POINT("Point"), + LINESTRING("LineString"), + POLYGON("Polygon"); +// MULTIPOINT("MultiPoint"), +// MULTILINESTRING("MultiLineString"), +// MULTIPOLYGON("MultiPolygon"), +// GEOMETRYCOLLECTION("GeometryCollection"); + + private final String code; + + @JsonValue + public String getCode() { + return code; + } + + GeometryType(String code) { + this.code = code; + } + + @JsonCreator + public static GeometryType fromCode(String code) { + for (GeometryType type : GeometryType.values()) { + if (type.code.equals(code)) { + return type; + } + } + log.warn("Unknown GeometryType code: {}", code); + return null; + } + } + + // TODO 가장 가까운 거리 반환 + + + + public static void main(String[] args) { + AirAreaUtils utils = AirAreaUtils.getInstance(); + log.info("utils: {}", utils); + AirAreaUtils utils2 = AirAreaUtils.getInstance(); + log.info("utils2: {}", utils2); + log.info("utils == utils2: {}", utils == utils2); + + List list = List.of(1, 2, 3, 4, 5); + boolean b = list.stream().anyMatch(i -> i == 3); + log.info("b:: {}", b); + List list2 = new ArrayList<>(); + boolean b2 = list2.stream().anyMatch(i -> i == 3); + log.info("b2:: {}", b2); + } +} diff --git a/pav-server/src/main/java/com/palnet/comn/utils/AirspaceUtils.java b/pav-server/src/main/java/com/palnet/comn/utils/AirspaceUtils.java index cb94329b..abb30bf8 100644 --- a/pav-server/src/main/java/com/palnet/comn/utils/AirspaceUtils.java +++ b/pav-server/src/main/java/com/palnet/comn/utils/AirspaceUtils.java @@ -56,7 +56,7 @@ public class AirspaceUtils { private static final AirspaceUtils INSTANCE = new AirspaceUtils(); } - // 공영 중복 검사 - 고도x + // 공역 중복 검사 - 고도x public boolean isDuplicatedAirspace(FeatureInfo target) { Geometry targetGeometry = target.getGeometry(); @@ -70,7 +70,7 @@ public class AirspaceUtils { }); } - // 공영 중복 검사 - 고도x + // 공역 중복 검사 - 고도x public boolean isDuplicatedAirspace(FeatureInfo target, AirspaceType airspaceType) { Geometry targetGeometry = target.getGeometry();