lkd9125(이경도)
7 months ago
13 changed files with 869 additions and 10 deletions
@ -0,0 +1,284 @@ |
|||||||
|
package kr.co.palnet.kac.api.util; |
||||||
|
|
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.geotools.geojson.feature.FeatureJSON; |
||||||
|
import org.geotools.geojson.geom.GeometryJSON; |
||||||
|
import org.json.simple.JSONArray; |
||||||
|
import org.json.simple.JSONObject; |
||||||
|
import org.json.simple.parser.JSONParser; |
||||||
|
import org.locationtech.jts.geom.Coordinate; |
||||||
|
import org.locationtech.jts.geom.Geometry; |
||||||
|
import org.locationtech.jts.geom.GeometryFactory; |
||||||
|
import org.opengis.feature.simple.SimpleFeature; |
||||||
|
import org.springframework.core.io.Resource; |
||||||
|
import org.springframework.core.io.support.PathMatchingResourcePatternResolver; |
||||||
|
import org.springframework.core.io.support.ResourcePatternResolver; |
||||||
|
|
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.IOException; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.math.BigDecimal; |
||||||
|
import java.nio.charset.StandardCharsets; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.List; |
||||||
|
import java.util.stream.Collectors; |
||||||
|
|
||||||
|
@Slf4j |
||||||
|
public class AirspaceUtils { |
||||||
|
|
||||||
|
private final String CLASS_PATH = "air/elev2d"; |
||||||
|
private final GeometryFactory geometryFactory = new GeometryFactory(); |
||||||
|
private List<FeatureInfo> airspaces; |
||||||
|
|
||||||
|
|
||||||
|
private AirspaceUtils() { |
||||||
|
// 초기화
|
||||||
|
log.info("===== AirspaceUtils init ====="); |
||||||
|
this.loadResourceAirspace(); |
||||||
|
} |
||||||
|
|
||||||
|
public static AirspaceUtils getInstance() { |
||||||
|
return LazyHolder.INSTANCE; |
||||||
|
} |
||||||
|
|
||||||
|
private static class LazyHolder { |
||||||
|
private static final AirspaceUtils INSTANCE = new AirspaceUtils(); |
||||||
|
} |
||||||
|
|
||||||
|
// 공영 중복 검사 - 고도x
|
||||||
|
public boolean isDuplicatedAirspace(FeatureInfo target) { |
||||||
|
Geometry targetGeometry = target.getGeometry(); |
||||||
|
return this.airspaces.stream().anyMatch(featureInfo -> { |
||||||
|
Geometry featureGeometry = featureInfo.getGeometry(); |
||||||
|
return featureGeometry.intersects(targetGeometry); |
||||||
|
}); |
||||||
|
} |
||||||
|
|
||||||
|
// 공역 중복 검사
|
||||||
|
public boolean isDuplicatedAirspaceElev(FeatureInfo target) { |
||||||
|
if (this.airspaces == null || this.airspaces.isEmpty()) return true; |
||||||
|
Integer targetHighElev = target.getHighElev(); |
||||||
|
if (targetHighElev == null) targetHighElev = 0; |
||||||
|
Geometry targetGeometry = target.getGeometry(); |
||||||
|
|
||||||
|
for (FeatureInfo airspace : this.airspaces) { |
||||||
|
Integer airspaceHighElev = airspace.getHighElev(); |
||||||
|
Geometry airspaceGeometry = airspace.getGeometry(); |
||||||
|
|
||||||
|
// 임시로 0~최대고도 기준으로 검증
|
||||||
|
if (airspaceHighElev <= targetHighElev) { |
||||||
|
if (airspaceGeometry.intersects(targetGeometry)) { |
||||||
|
return true; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return false; |
||||||
|
} |
||||||
|
|
||||||
|
/* |
||||||
|
LAANC 공역 검사 |
||||||
|
- 비행구역이 공역(금지구역)에 접근할 경우만 검사 - 통과 true, 실패 false |
||||||
|
- 겹치지 않을 경우는 실패로 처리 - false |
||||||
|
*/ |
||||||
|
public boolean isValidLaancAirspace(FeatureInfo target) { |
||||||
|
if (this.airspaces == null || this.airspaces.isEmpty()) return true; |
||||||
|
final int targetHighElev = target.getHighElev() != null ? target.getHighElev() : 0; |
||||||
|
Geometry targetGeometry = target.getGeometry(); |
||||||
|
|
||||||
|
boolean isDuplicated = this.airspaces.stream().anyMatch(featureInfo -> { |
||||||
|
Geometry featureGeometry = featureInfo.getGeometry(); |
||||||
|
return featureGeometry.intersects(targetGeometry); |
||||||
|
}); |
||||||
|
// 공역(금지구역)과 겹치지 않을 경우는 실패로 처리
|
||||||
|
if (!isDuplicated) return false; |
||||||
|
|
||||||
|
// 겹칠 경우 공역과 비교
|
||||||
|
for (FeatureInfo featureInfo : this.airspaces) { |
||||||
|
Geometry airspaceGeometry = featureInfo.getGeometry(); |
||||||
|
int airspaceHighElev = featureInfo.getHighElev() != null ? featureInfo.getHighElev() : 0; |
||||||
|
// 0~최대고도 기준으로 검증
|
||||||
|
if (airspaceHighElev == 0 || airspaceHighElev < targetHighElev) { |
||||||
|
if (airspaceGeometry.intersects(targetGeometry)) { |
||||||
|
return false; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
// get allowable elevation
|
||||||
|
public Integer getAllowElevation(Geometry target) { |
||||||
|
Integer allowElevation = null; |
||||||
|
if (this.airspaces == null || this.airspaces.isEmpty()) return null; |
||||||
|
// target과 겹치는 airspace 조회
|
||||||
|
List<FeatureInfo> duplicationAirspace = this.airspaces.stream().filter(featureInfo -> { |
||||||
|
Geometry featureGeometry = featureInfo.getGeometry(); |
||||||
|
return featureGeometry.intersects(target); |
||||||
|
}).collect(Collectors.toList()); |
||||||
|
// 중복된 airspace가 없을 경우 기본 허용고도 반환 150m(관제권x)
|
||||||
|
if (duplicationAirspace.isEmpty()) return 150; |
||||||
|
allowElevation = duplicationAirspace.stream().map(FeatureInfo::getHighElev).min(Integer::compareTo).orElse(150); |
||||||
|
return allowElevation; |
||||||
|
} |
||||||
|
|
||||||
|
// convert coordiate to geometry
|
||||||
|
public Geometry createGeometryByCoordinate(List<Coordinate> target) { |
||||||
|
return this.createGeometryByCoordinate(target, "Polygon"); |
||||||
|
} |
||||||
|
|
||||||
|
// convert coordiate to geometry
|
||||||
|
public Geometry createGeometryByCoordinate(List<Coordinate> target, String type) { |
||||||
|
Geometry geometry = null; |
||||||
|
if ("Polygon".equals(type)) { |
||||||
|
if (target == null || target.isEmpty()) return null; |
||||||
|
log.info(">>> {}", target.get(0) != target.get(target.size() - 1)); |
||||||
|
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 ("LineString".equals(type)) { |
||||||
|
geometry = this.geometryFactory.createLineString(target.toArray(new Coordinate[0])); |
||||||
|
} else if ("Point".equals(type)) { |
||||||
|
geometry = this.geometryFactory.createPoint(target.get(0)); |
||||||
|
} |
||||||
|
return geometry; |
||||||
|
} |
||||||
|
|
||||||
|
// get geometry
|
||||||
|
private List<Geometry> getAirspaceGeometry() { |
||||||
|
return this.airspaces.stream().map(FeatureInfo::getGeometry).collect(Collectors.toList()); |
||||||
|
} |
||||||
|
|
||||||
|
// 파일에서 공역 데이터 가져와서 geometry로 변환 - 초기화.
|
||||||
|
private void loadResourceAirspace() { |
||||||
|
|
||||||
|
List<FeatureInfo> featureInfos = new ArrayList<>(); |
||||||
|
ResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); |
||||||
|
Resource[] resources = null; |
||||||
|
try { |
||||||
|
resources = resolver.getResources("classpath:" + CLASS_PATH + "/*-elev.json"); |
||||||
|
} catch (IOException e) { |
||||||
|
log.warn("airspaces load error : {}", e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
if (resources == null) { |
||||||
|
log.info("airspace resources is null"); |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
for (Resource resource : resources) { |
||||||
|
try (InputStream is = resource.getInputStream()) { |
||||||
|
BufferedReader reader = new BufferedReader(new InputStreamReader(is, StandardCharsets.UTF_8)); |
||||||
|
JSONParser jsonParser = new JSONParser(); |
||||||
|
JSONObject jsonObject = (JSONObject) jsonParser.parse(reader); |
||||||
|
List<FeatureInfo> fis = this.convertGeoJsonToGeometry(jsonObject); |
||||||
|
featureInfos.addAll(fis); |
||||||
|
} catch (Exception e) { |
||||||
|
log.warn("airspace resource read error : {}", e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
log.info(">>> featureInfos size : {}", featureInfos.size()); |
||||||
|
|
||||||
|
this.airspaces = featureInfos; |
||||||
|
} |
||||||
|
|
||||||
|
private List<FeatureInfo> convertGeoJsonToGeometry(JSONObject jsonObject) { |
||||||
|
List<FeatureInfo> featureInfos = new ArrayList<>(); |
||||||
|
String type = (String) jsonObject.get("type"); |
||||||
|
if ("FeatureCollection".equals(type)) { |
||||||
|
List<JSONObject> features = (List<JSONObject>) jsonObject.get("features"); |
||||||
|
// log.debug(">>> features size : {}", features.size());
|
||||||
|
for (JSONObject feature : features) { |
||||||
|
JSONObject geometryObject = (JSONObject) feature.get("geometry"); |
||||||
|
String geometryType = String.valueOf(geometryObject.get("type")); |
||||||
|
|
||||||
|
List<JSONObject> coordinatesObject = (List<JSONObject>) geometryObject.get("coordinates"); |
||||||
|
if ("Polygon".equals(geometryType)) { |
||||||
|
List<JSONArray> innerObject = (List<JSONArray>) coordinatesObject.get(0); |
||||||
|
JSONArray firstCoords = innerObject.get(0); |
||||||
|
JSONArray lastCoords = innerObject.get(innerObject.size() - 1); |
||||||
|
BigDecimal ff = new BigDecimal(String.valueOf(firstCoords.get(0))); |
||||||
|
BigDecimal fl = new BigDecimal(String.valueOf(firstCoords.get(1))); |
||||||
|
BigDecimal lf = new BigDecimal(String.valueOf(lastCoords.get(0))); |
||||||
|
BigDecimal ll = new BigDecimal(String.valueOf(lastCoords.get(1))); |
||||||
|
if (!ff.equals(lf) || !fl.equals(ll)) { |
||||||
|
JSONObject propertiesObject = (JSONObject) feature.get("properties"); |
||||||
|
// String nameObject = String.valueOf(propertiesObject.get("name"));
|
||||||
|
// String descriptionObject = String.valueOf(propertiesObject.get("description"));
|
||||||
|
// log.info("coords first and last coords not eqauls : name/descriion = {}/{}", nameObject, descriptionObject);
|
||||||
|
innerObject.add(firstCoords); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
try { |
||||||
|
FeatureJSON featureJSON = new FeatureJSON(); |
||||||
|
SimpleFeature simpleFeature = null; |
||||||
|
simpleFeature = featureJSON.readFeature(feature.toJSONString()); |
||||||
|
Boolean use = Boolean.valueOf(String.valueOf(simpleFeature.getAttribute("use"))); |
||||||
|
if (use) { |
||||||
|
String name = String.valueOf(simpleFeature.getAttribute("name")); |
||||||
|
String description = String.valueOf(simpleFeature.getAttribute("description")); |
||||||
|
Integer lowElev = Integer.parseInt(String.valueOf(simpleFeature.getAttribute("lowElev"))); |
||||||
|
Integer highElev = Integer.parseInt(String.valueOf(simpleFeature.getAttribute("highElev"))); |
||||||
|
Geometry geometry = (Geometry) simpleFeature.getDefaultGeometry(); |
||||||
|
// log.debug(">>> name, description, use, lowElev, highElev : {}, {}, {}, {}, {}", name, description, use, lowElev, highElev);
|
||||||
|
FeatureInfo info = new FeatureInfo(name, description, lowElev, highElev, geometry); |
||||||
|
featureInfos.add(info); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
log.error("geometry json read error : {}", e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
} else if ("Feature".equals(type)) { |
||||||
|
FeatureJSON featureJSON = new FeatureJSON(); |
||||||
|
try { |
||||||
|
SimpleFeature simpleFeature = featureJSON.readFeature(jsonObject.toJSONString()); |
||||||
|
Boolean use = Boolean.valueOf(String.valueOf(simpleFeature.getAttribute("use"))); |
||||||
|
if (use) { |
||||||
|
String name = String.valueOf(simpleFeature.getAttribute("name")); |
||||||
|
String description = String.valueOf(simpleFeature.getAttribute("description")); |
||||||
|
Integer lowElev = Integer.parseInt(String.valueOf(simpleFeature.getAttribute("lowElev"))); |
||||||
|
Integer highElev = Integer.parseInt(String.valueOf(simpleFeature.getAttribute("highElev"))); |
||||||
|
Geometry geometry = (Geometry) simpleFeature.getDefaultGeometry(); |
||||||
|
FeatureInfo info = new FeatureInfo(name, description, lowElev, highElev, geometry); |
||||||
|
featureInfos.add(info); |
||||||
|
} |
||||||
|
} catch (IOException e) { |
||||||
|
log.error("geometry json read error : {}", e.getMessage()); |
||||||
|
} |
||||||
|
|
||||||
|
} else { |
||||||
|
GeometryJSON geoJson = new GeometryJSON(); |
||||||
|
try { |
||||||
|
Geometry geometry = geoJson.read(jsonObject.toJSONString()); |
||||||
|
FeatureInfo info = new FeatureInfo(null, null, null, null, geometry); |
||||||
|
} catch (IOException e) { |
||||||
|
log.error("geometry json read error : {}", e.getMessage()); |
||||||
|
} |
||||||
|
} |
||||||
|
return featureInfos; |
||||||
|
} |
||||||
|
|
||||||
|
public int getSize() { |
||||||
|
if(this.airspaces == null) return 0; |
||||||
|
return this.airspaces.size(); |
||||||
|
} |
||||||
|
|
||||||
|
@Data |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
public static class FeatureInfo { |
||||||
|
private String name; |
||||||
|
private String description; |
||||||
|
private Integer lowElev; |
||||||
|
private Integer highElev; |
||||||
|
private Geometry geometry; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
} |
@ -0,0 +1,344 @@ |
|||||||
|
package kr.co.palnet.kac.api.util; |
||||||
|
|
||||||
|
import kr.co.palnet.kac.api.util.model.FlightPlanAreaCoordModel; |
||||||
|
import lombok.extern.slf4j.Slf4j; |
||||||
|
import org.json.simple.JSONArray; |
||||||
|
import org.json.simple.JSONObject; |
||||||
|
import org.json.simple.parser.JSONParser; |
||||||
|
import org.locationtech.jts.geom.*; |
||||||
|
import org.locationtech.jts.operation.buffer.BufferOp; |
||||||
|
import org.locationtech.jts.operation.buffer.BufferParameters; |
||||||
|
import org.locationtech.jts.util.GeometricShapeFactory; |
||||||
|
import org.locationtech.proj4j.BasicCoordinateTransform; |
||||||
|
import org.locationtech.proj4j.CRSFactory; |
||||||
|
import org.locationtech.proj4j.CoordinateReferenceSystem; |
||||||
|
import org.locationtech.proj4j.ProjCoordinate; |
||||||
|
import org.springframework.cache.annotation.Cacheable; |
||||||
|
import org.springframework.core.io.ClassPathResource; |
||||||
|
import org.springframework.stereotype.Component; |
||||||
|
|
||||||
|
import java.io.BufferedReader; |
||||||
|
import java.io.InputStream; |
||||||
|
import java.io.InputStreamReader; |
||||||
|
import java.util.ArrayList; |
||||||
|
import java.util.Arrays; |
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Component |
||||||
|
@Slf4j |
||||||
|
public class AreaUtils { |
||||||
|
|
||||||
|
private List<Polygon> polygons = new ArrayList<>(); |
||||||
|
private List<Point> points = new ArrayList<>(); |
||||||
|
|
||||||
|
private GeometryFactory geometryFactory = new GeometryFactory(); |
||||||
|
|
||||||
|
public AreaUtils() throws Exception { |
||||||
|
this.init(); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO 좌표계 변환 |
||||||
|
* |
||||||
|
* @param coordList |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
@Cacheable(value = "coordTransform") |
||||||
|
public List<Coordinate> transform(List<Coordinate> coordList, String from, String to) { |
||||||
|
CRSFactory factory = new CRSFactory(); |
||||||
|
|
||||||
|
CoordinateReferenceSystem srcCRS = factory.createFromName(from); |
||||||
|
CoordinateReferenceSystem tgtCRS = factory.createFromName(to); |
||||||
|
|
||||||
|
BasicCoordinateTransform transform = new BasicCoordinateTransform(srcCRS, tgtCRS); |
||||||
|
|
||||||
|
List<Coordinate> result = new ArrayList<>(); |
||||||
|
|
||||||
|
for(Coordinate coord : coordList) { |
||||||
|
ProjCoordinate projCoordinate = new ProjCoordinate(coord.getX(), coord.getY()); |
||||||
|
ProjCoordinate projTrasform = transform.transform(projCoordinate, new ProjCoordinate()); |
||||||
|
|
||||||
|
Coordinate target = new Coordinate(projTrasform.x, projTrasform.y); |
||||||
|
|
||||||
|
result.add(target); |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO 비행 구역 생성시 장애물 영역 포함 체크 |
||||||
|
* |
||||||
|
* @param targetCoordList - 비행구역 좌표 리스트 |
||||||
|
* @return boolean - true(비정상), false(정상) |
||||||
|
*/ |
||||||
|
public boolean overlaps(List<Coordinate> targetCoordList) { |
||||||
|
boolean result = false; |
||||||
|
targetCoordList.add(targetCoordList.get(0)); |
||||||
|
Polygon targetPolygon = geometryFactory.createPolygon(targetCoordList.toArray(new Coordinate[] {})); |
||||||
|
|
||||||
|
/* 공역 데이터 */ |
||||||
|
if(polygons.size() > 0) { |
||||||
|
for(Polygon polygon : polygons) { |
||||||
|
Geometry overlabsGeometry = geometryFactory.createGeometry(polygon); |
||||||
|
Geometry targetGeometry = geometryFactory.createGeometry(targetPolygon); |
||||||
|
|
||||||
|
boolean over = targetPolygon.overlaps(polygon); |
||||||
|
|
||||||
|
if(over) { |
||||||
|
result = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO 구역 포함 여부 |
||||||
|
* |
||||||
|
* @param targetCoordList - 생성 구역 |
||||||
|
* @param effectiveCoordList - 유효한 비행 구역 |
||||||
|
* @return boolean - true(비정상), false(정상) |
||||||
|
*/ |
||||||
|
public boolean overlaps(List<Coordinate> targetCoordList, List<Coordinate> effectiveCoordList) { |
||||||
|
targetCoordList.add(targetCoordList.get(0)); |
||||||
|
Polygon targetPolygon = geometryFactory.createPolygon(targetCoordList.toArray(new Coordinate[] {})); |
||||||
|
|
||||||
|
effectiveCoordList.add(effectiveCoordList.get(0)); |
||||||
|
Polygon effectivePolygon = geometryFactory.createPolygon(effectiveCoordList.toArray(new Coordinate[] {})); |
||||||
|
|
||||||
|
return targetPolygon.overlaps(effectivePolygon); |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO 드론 관제시 정상 비행 체크 (공역) |
||||||
|
* |
||||||
|
* @param targetCoordinate - 드론 좌표 정보 |
||||||
|
* @return boolean - true(비정상), false(정상) |
||||||
|
*/ |
||||||
|
public boolean contains(Coordinate targetCoordinate) { |
||||||
|
boolean result = true; |
||||||
|
|
||||||
|
Point target = geometryFactory.createPoint(targetCoordinate); |
||||||
|
|
||||||
|
for(Polygon polygon : polygons) { |
||||||
|
boolean contains = polygon.contains(target); |
||||||
|
|
||||||
|
if(contains) { |
||||||
|
result = true; |
||||||
|
break; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO 드론 관제시 정상 비행 체크 (비행 구역) |
||||||
|
* |
||||||
|
* @param areaCoordList - 비행 구역 좌표 리스트 |
||||||
|
* @param targetCoordinate - 드론 좌표 정보 |
||||||
|
* @return boolean - true(비정상), false(정상) |
||||||
|
*/ |
||||||
|
public boolean contains(List<Coordinate> areaCoordList, Coordinate targetCoordinate) { |
||||||
|
boolean result = true; |
||||||
|
areaCoordList.add(areaCoordList.get(0)); |
||||||
|
if(targetCoordinate != null) { |
||||||
|
Polygon plan = geometryFactory.createPolygon(areaCoordList.toArray(new Coordinate[]{})); // 비행 구역
|
||||||
|
Point target = geometryFactory.createPoint(targetCoordinate); |
||||||
|
|
||||||
|
result = plan.contains(target); |
||||||
|
// target.contains(plan);
|
||||||
|
} |
||||||
|
|
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO 비행 구역 - Buffer 영역 생성 |
||||||
|
* |
||||||
|
* @param coordList - 비행 구역 기초 좌표 리스트 |
||||||
|
* @param bufferZone - 반경 |
||||||
|
* |
||||||
|
*/ |
||||||
|
public List<Coordinate> buffer(List<Coordinate> coordList, double bufferZone) { |
||||||
|
List<Coordinate> bufferList = new ArrayList<>(); |
||||||
|
|
||||||
|
LineString line = geometryFactory.createLineString(coordList.toArray(new Coordinate[] {})); |
||||||
|
Geometry geometry = geometryFactory.createGeometry(line); |
||||||
|
|
||||||
|
// buffer
|
||||||
|
int nSegments = 10; |
||||||
|
int cap = BufferParameters.CAP_SQUARE; |
||||||
|
int join = BufferParameters.JOIN_ROUND; |
||||||
|
|
||||||
|
BufferParameters bufferParam = new BufferParameters(nSegments, cap, join, join); |
||||||
|
BufferOp ops = new BufferOp(geometry, bufferParam); |
||||||
|
|
||||||
|
// Geometry bufTrans = ops.getResultGeometry((bufferZone/177763.63662580872)*2);
|
||||||
|
Geometry bufTrans = ops.getResultGeometry(bufferZone); |
||||||
|
|
||||||
|
Coordinate[] coords = bufTrans.getCoordinates(); |
||||||
|
|
||||||
|
bufferList.addAll(Arrays.asList(coords)); |
||||||
|
|
||||||
|
return bufferList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO 타원 생성 |
||||||
|
* |
||||||
|
* @param CircleCoord |
||||||
|
* @param BufferZone |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public List<Coordinate> createCircle(Coordinate CircleCoord, double BufferZone) { |
||||||
|
GeometricShapeFactory shapeFactory = new GeometricShapeFactory(); |
||||||
|
|
||||||
|
double lng = CircleCoord.x; |
||||||
|
double lat = CircleCoord.y; |
||||||
|
double diameterInMeters = BufferZone; |
||||||
|
|
||||||
|
shapeFactory.setCentre(new Coordinate(lng , lat)); |
||||||
|
shapeFactory.setHeight((diameterInMeters * 2) / 111320d); |
||||||
|
shapeFactory.setWidth((diameterInMeters * 2) / (40075000 * Math.cos(Math.toRadians(lat)) / 360)); |
||||||
|
shapeFactory.setNumPoints(64); |
||||||
|
|
||||||
|
final Polygon circle = shapeFactory.createCircle(); |
||||||
|
circle.setSRID(4326); |
||||||
|
|
||||||
|
Geometry geometry = geometryFactory.createGeometry(circle); |
||||||
|
Coordinate[] coords = geometry.getCoordinates(); |
||||||
|
List<Coordinate> coordList = new ArrayList<>(); |
||||||
|
coordList.addAll(Arrays.asList(coords)); |
||||||
|
|
||||||
|
return coordList; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO service model -> jts model mapping |
||||||
|
* |
||||||
|
* @param coordList |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public List<Coordinate> convertCoordinates(List<FlightPlanAreaCoordModel> coordList) { |
||||||
|
List<Coordinate> coordinates = new ArrayList<>(); |
||||||
|
|
||||||
|
for(FlightPlanAreaCoordModel coord : coordList) { |
||||||
|
Coordinate coordinate = new Coordinate(coord.getLon(), coord.getLat()); |
||||||
|
|
||||||
|
coordinates.add(coordinate); |
||||||
|
} |
||||||
|
|
||||||
|
return coordinates; |
||||||
|
} |
||||||
|
|
||||||
|
/** |
||||||
|
* TODO jts model -> service model mapiing |
||||||
|
* |
||||||
|
* @param bufferList |
||||||
|
* @return |
||||||
|
*/ |
||||||
|
public List<FlightPlanAreaCoordModel> convertModel(List<Coordinate> bufferList) { |
||||||
|
List<FlightPlanAreaCoordModel> bufferCoordList = new ArrayList<>(); |
||||||
|
|
||||||
|
for(Coordinate buffer : bufferList) { |
||||||
|
FlightPlanAreaCoordModel bufferCoord = new FlightPlanAreaCoordModel(); |
||||||
|
|
||||||
|
bufferCoord.setLat(buffer.getY()); |
||||||
|
bufferCoord.setLon(buffer.getX()); |
||||||
|
|
||||||
|
bufferCoordList.add(bufferCoord); |
||||||
|
} |
||||||
|
|
||||||
|
return bufferCoordList; |
||||||
|
} |
||||||
|
|
||||||
|
// TODO 공역 데이터 생성
|
||||||
|
public void init() throws Exception { |
||||||
|
// 1. file read
|
||||||
|
ClassPathResource resource = new ClassPathResource("air/airgeo.json"); |
||||||
|
InputStream jsonInputStream = resource.getInputStream(); |
||||||
|
InputStreamReader inputStreamReader = new InputStreamReader(jsonInputStream, "UTF-8"); |
||||||
|
BufferedReader reader = new BufferedReader(inputStreamReader); |
||||||
|
|
||||||
|
// 2. json parsing
|
||||||
|
JSONParser jsonParser = new JSONParser(); |
||||||
|
JSONObject jsonObject = (JSONObject) jsonParser.parse(reader); |
||||||
|
|
||||||
|
List<JSONObject> features = (List<JSONObject>) jsonObject.get("features"); |
||||||
|
|
||||||
|
for(int i=0; i<features.size(); i++) { |
||||||
|
JSONObject geometry = (JSONObject) features.get(i).get("geometry"); |
||||||
|
|
||||||
|
List<JSONArray> coordinates = (List<JSONArray>) geometry.get("coordinates"); |
||||||
|
String type = (String) geometry.get("type"); |
||||||
|
|
||||||
|
if("Polygon".equals(type)) { |
||||||
|
for(JSONArray coordList : coordinates) { |
||||||
|
List<Coordinate> polygonPaths = new ArrayList<>(); |
||||||
|
|
||||||
|
for(Object coord : coordList) { |
||||||
|
Object y = ((JSONArray) coord).get(0); |
||||||
|
Object x = ((JSONArray) coord).get(1); |
||||||
|
|
||||||
|
Double lon = y instanceof Double ? (Double) y : Double.valueOf((Long) y); |
||||||
|
Double lat = x instanceof Double ? (Double) x : Double.valueOf((Long) x); |
||||||
|
|
||||||
|
Coordinate areaCoord = new Coordinate(lon, lat); |
||||||
|
|
||||||
|
polygonPaths.add(areaCoord); |
||||||
|
} |
||||||
|
|
||||||
|
Polygon polygon = geometryFactory.createPolygon(polygonPaths.toArray(new Coordinate[] {})); |
||||||
|
|
||||||
|
polygons.add(polygon); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
if("LineString".equals(type)) { |
||||||
|
List<Coordinate> lineStringPaths = new ArrayList<>(); |
||||||
|
for(JSONArray coordList : coordinates) { |
||||||
|
|
||||||
|
Object y = coordList.get(0); |
||||||
|
Object x = coordList.get(1); |
||||||
|
|
||||||
|
Double lon = y instanceof Double ? (Double) y : Double.valueOf((Long) y); |
||||||
|
Double lat = x instanceof Double ? (Double) x : Double.valueOf((Long) x); |
||||||
|
|
||||||
|
Coordinate areaCoord = new Coordinate(lon, lat); |
||||||
|
|
||||||
|
lineStringPaths.add(areaCoord); |
||||||
|
} |
||||||
|
|
||||||
|
Polygon polygon = geometryFactory.createPolygon(lineStringPaths.toArray(new Coordinate[] {})); |
||||||
|
|
||||||
|
polygons.add(polygon); |
||||||
|
} |
||||||
|
|
||||||
|
if("Point".equals(type)) { |
||||||
|
Object y = coordinates.get(0); |
||||||
|
Object x = coordinates.get(1); |
||||||
|
|
||||||
|
Double lon = y instanceof Double ? (Double) y : Double.valueOf((Long) y); |
||||||
|
Double lat = x instanceof Double ? (Double) x : Double.valueOf((Long) x); |
||||||
|
|
||||||
|
Coordinate areaCoord = new Coordinate(lon, lat); |
||||||
|
|
||||||
|
Point point = geometryFactory.createPoint(areaCoord); |
||||||
|
points.add(point); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
public Geometry coordinateToGeometry(List<Coordinate> target) { |
||||||
|
if (target == null || target.isEmpty()) return null; |
||||||
|
log.info(">>> {}", target.get(0) != target.get(target.size() - 1)); |
||||||
|
if (target.get(0) != target.get(target.size() - 1)) { |
||||||
|
target.add(target.get(0)); |
||||||
|
} |
||||||
|
return geometryFactory.createPolygon(target.toArray(new Coordinate[]{})); |
||||||
|
} |
||||||
|
|
||||||
|
} |
@ -0,0 +1,22 @@ |
|||||||
|
package kr.co.palnet.kac.api.util.model; |
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.time.LocalDateTime; |
||||||
|
|
||||||
|
@Data |
||||||
|
public class FlightPlanAreaCoordModel { |
||||||
|
private Integer planAreaCoordSno; |
||||||
|
private Integer planAreaSno; |
||||||
|
@Schema(description = "x 좌표", example = "37.5208864") |
||||||
|
private double lat; |
||||||
|
@Schema(description = "y 좌표", example = "126.6071164") |
||||||
|
private double lon; |
||||||
|
@Schema(hidden = true) |
||||||
|
private String createUserId; |
||||||
|
@Schema(hidden = true) |
||||||
|
private LocalDateTime createDt; |
||||||
|
@Schema(hidden = true) |
||||||
|
private String docState = "R"; |
||||||
|
} |
@ -0,0 +1,11 @@ |
|||||||
|
package kr.co.palnet.kac.api.v1.flight.laanc.model.valid; |
||||||
|
|
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Data |
||||||
|
public class LaancAllowableElevationRS { |
||||||
|
|
||||||
|
private List<Integer> allowableElevation; |
||||||
|
} |
@ -0,0 +1,21 @@ |
|||||||
|
package kr.co.palnet.kac.api.v1.flight.laanc.model.valid; |
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Data |
||||||
|
public class LaancAreaByAirspaceModel { |
||||||
|
|
||||||
|
@Schema(description = "구역종류", example = "CIRCLE") |
||||||
|
private String areaType; |
||||||
|
|
||||||
|
@Schema(description = "버퍼존", example = "100") |
||||||
|
private Integer bufferZone; |
||||||
|
|
||||||
|
@Schema(description = "고도", example = "110") |
||||||
|
private String fltElev; |
||||||
|
|
||||||
|
private List<LaancAreaCoordModel> coordList; |
||||||
|
} |
@ -0,0 +1,19 @@ |
|||||||
|
package kr.co.palnet.kac.api.v1.flight.laanc.model.valid; |
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
import java.util.List; |
||||||
|
|
||||||
|
@Data |
||||||
|
public class LaancAreaByElevModel { |
||||||
|
|
||||||
|
@Schema(description = "구역종류", example = "CIRCLE") |
||||||
|
private String areaType; |
||||||
|
|
||||||
|
@Schema(description = "버퍼존", example = "100") |
||||||
|
private Integer bufferZone; |
||||||
|
|
||||||
|
private List<LaancAreaCoordModel> coordList; |
||||||
|
|
||||||
|
} |
@ -0,0 +1,14 @@ |
|||||||
|
package kr.co.palnet.kac.api.v1.flight.laanc.model.valid; |
||||||
|
|
||||||
|
import io.swagger.v3.oas.annotations.media.Schema; |
||||||
|
import lombok.Data; |
||||||
|
|
||||||
|
@Data |
||||||
|
public class LaancAreaCoordModel { |
||||||
|
|
||||||
|
@Schema(description = "위도", example = "127.33") |
||||||
|
private double lat; |
||||||
|
|
||||||
|
@Schema(description = "경도", example = "37.99") |
||||||
|
private double lon; |
||||||
|
} |
@ -0,0 +1,15 @@ |
|||||||
|
package kr.co.palnet.kac.api.v1.flight.laanc.model.valid; |
||||||
|
|
||||||
|
import lombok.AllArgsConstructor; |
||||||
|
import lombok.Builder; |
||||||
|
import lombok.Data; |
||||||
|
import lombok.NoArgsConstructor; |
||||||
|
|
||||||
|
@Data |
||||||
|
@NoArgsConstructor |
||||||
|
@AllArgsConstructor |
||||||
|
@Builder |
||||||
|
public class LaancDuplicatedAirspaceRs { |
||||||
|
private boolean isDuplicated; |
||||||
|
private LaancAreaByAirspaceModel rq; |
||||||
|
} |
Loading…
Reference in new issue