|
|
|
@ -10,6 +10,9 @@ import java.util.List;
|
|
|
|
|
import java.util.stream.Collectors; |
|
|
|
|
|
|
|
|
|
import com.palnet.biz.api.bas.laanc.model.*; |
|
|
|
|
import com.palnet.biz.api.comn.sunriseset.model.ComnSunrisesetCoordRq; |
|
|
|
|
import com.palnet.biz.api.comn.sunriseset.model.ComnSunrisesetRs; |
|
|
|
|
import com.palnet.biz.jpa.entity.type.FltMethod; |
|
|
|
|
import org.locationtech.jts.geom.Coordinate; |
|
|
|
|
import org.locationtech.jts.geom.Geometry; |
|
|
|
|
import org.springframework.stereotype.Service; |
|
|
|
@ -87,6 +90,8 @@ public class BasLaancService {
|
|
|
|
|
private final ComnSmsService comnSmsService; |
|
|
|
|
private final JwtTokenUtil jwtTokenUtil; |
|
|
|
|
private final AreaUtils areaUtils; |
|
|
|
|
private final ComRiseSetQueryRepository comRiseSetQueryRepository; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// LAANC 검증
|
|
|
|
|
public BasLaancValidatedRs validationLaanc(BasLaancPlanRq rq) { |
|
|
|
@ -94,63 +99,66 @@ public class BasLaancService {
|
|
|
|
|
|
|
|
|
|
BasLaancValidatedRs rs = new BasLaancValidatedRs(); |
|
|
|
|
|
|
|
|
|
// 조종사 자격 확인 - 무게가 2kg 초과이거나 상업적일 경우에만 진행
|
|
|
|
|
// 상업 여부 - 상업(true)
|
|
|
|
|
boolean isCommercial = FltType.COMMERCIAL == rq.getFltType(); |
|
|
|
|
rs.setCommercial(isCommercial); |
|
|
|
|
|
|
|
|
|
// 2kg 초과 기체신고번호
|
|
|
|
|
List<String> idntfNumList = rq.getArcrftList().stream().filter(arcrft -> arcrft.getArcrftWghtCd() != ArcrftWghtCd.W250G_LOE && arcrft.getArcrftWghtCd() != ArcrftWghtCd.W250G_W2KG).map(BasLaancArcrftModel::getIdntfNum).collect(Collectors.toList()); |
|
|
|
|
|
|
|
|
|
// 신고 여부 - 비상업적이고 기체중량 2kg이하일 경우 - false, 상업적이거나 기체중량 2kg초과일 경우 - true
|
|
|
|
|
boolean isReport = !idntfNumList.isEmpty() || isCommercial; |
|
|
|
|
rs.setReport(isReport); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 고도 150m 이하 - true
|
|
|
|
|
boolean isElev = rq.getAreaList().stream().anyMatch(area -> area.getFltElev() != null && Integer.parseInt(area.getFltElev()) <= 150); |
|
|
|
|
rs.setElev(isElev); |
|
|
|
|
|
|
|
|
|
// 기체중량 25kg 이하
|
|
|
|
|
boolean isArcrftWeight = rq.getArcrftList().stream().anyMatch(arcrft -> arcrft.getArcrftWghtCd() != ArcrftWghtCd.W25KG_GO && arcrft.getArcrftWghtCd() != ArcrftWghtCd.W25KG_GO_TEST); |
|
|
|
|
rs.setArcrftWeight(isArcrftWeight); |
|
|
|
|
|
|
|
|
|
// TODO start - 조종사 자격 및 기체보험 확인
|
|
|
|
|
if (isReport) { |
|
|
|
|
List<PilotValidRq> pilotValidRqList = idntfNumList.stream().map(idntfNum -> { |
|
|
|
|
// TS 자격 판별 - 조종사 자격증명, 기체 보험
|
|
|
|
|
List<String> idntfNumList = rq.getArcrftList().stream().filter(arcrft -> arcrft.getIdntfNum() != null && !arcrft.getIdntfNum().isBlank()).map(BasLaancArcrftModel::getIdntfNum).collect(Collectors.toList()); |
|
|
|
|
Integer cstmrSno = jwtTokenUtil.getCstmrSnoByToken(); |
|
|
|
|
AnctCstmrModel cstmrInfo = ptyCstmrQueryRepository.findByCstmrSno(cstmrSno); |
|
|
|
|
String userCi = cstmrInfo.getIpinCi(); |
|
|
|
|
List<PilotValidRq> pilotValidRqList = new ArrayList<>(); |
|
|
|
|
if (!idntfNumList.isEmpty()) { |
|
|
|
|
pilotValidRqList = idntfNumList.stream().map(idntfNum -> { |
|
|
|
|
// TODO 기체보험 확인, 조종사 자격 확인
|
|
|
|
|
return PilotValidRq.builder() |
|
|
|
|
.pilotci("조종사CI") |
|
|
|
|
.pilotci(userCi) |
|
|
|
|
.declarationnum(idntfNum) |
|
|
|
|
.build(); |
|
|
|
|
}).collect(Collectors.toList()); |
|
|
|
|
} else { |
|
|
|
|
pilotValidRqList.add(PilotValidRq.builder() |
|
|
|
|
.pilotci(userCi) |
|
|
|
|
.build()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
List<PilotValidRs> pilotValidRsList = tsService.getAccountValidate(pilotValidRqList); |
|
|
|
|
if (pilotValidRsList.isEmpty()) { |
|
|
|
|
rs.setPilotQlfc(false); |
|
|
|
|
rs.setArcrftInsurance(false); |
|
|
|
|
} else { |
|
|
|
|
rs.setPilotValidRsList(pilotValidRsList); |
|
|
|
|
rs.setPilotQlfc(pilotValidRsList.stream().allMatch(pilotValidRs -> "Y".equals(pilotValidRs.getPilotcredentialyn()))); |
|
|
|
|
rs.setArcrftInsurance(pilotValidRsList.stream().allMatch(pilotValidRs -> "Y".equals(pilotValidRs.getArcrftinsuranceyn()))); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// TODO end - 조종사 자격 및 기체보험 확인
|
|
|
|
|
|
|
|
|
|
// 비행유형 판별 - 상업 - true
|
|
|
|
|
boolean isCommercial = FltType.COMMERCIAL == rq.getFltType(); |
|
|
|
|
rs.setCommercial(isCommercial); |
|
|
|
|
|
|
|
|
|
// 관제권 여부 판별
|
|
|
|
|
BasLaancValidatedRs validationPlanDbRs = this.validationPlanAirspace(rq); |
|
|
|
|
boolean isEvaluatedTargetArea = validationPlanDbRs.isEvaluatedTargetArea(); |
|
|
|
|
rs.setEvaluatedTargetArea(isEvaluatedTargetArea); |
|
|
|
|
|
|
|
|
|
/* 비행구역 및 기체 중복여부 확인 안하기로 함. |
|
|
|
|
// 비행구역 중복여부, 기체 중복여부
|
|
|
|
|
BasLaancValidatedRs validationPlanAirspaceRs = this.validationPlanAreaAndArcrft(rq); |
|
|
|
|
rs.setPlanAreaDuplicatd(validationPlanAirspaceRs.isPlanAreaDuplicatd()); |
|
|
|
|
rs.setArcrftDuplicated(validationPlanAirspaceRs.isArcrftDuplicated()); |
|
|
|
|
*/ |
|
|
|
|
// 고도여부 - 관제권내 설정고도 이하, 권제권밖 150m 이하 true
|
|
|
|
|
if (isEvaluatedTargetArea) { |
|
|
|
|
// 관제권 내부 - 설정고도 이하
|
|
|
|
|
rs.setElev(validationPlanDbRs.isElev()); |
|
|
|
|
} else { |
|
|
|
|
// 관제권 외부 - 150m 이하
|
|
|
|
|
boolean isElev = rq.getAreaList().stream().allMatch(area -> area.getFltElev() != null && Integer.parseInt(area.getFltElev()) <= 150); |
|
|
|
|
rs.setElev(isElev); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 기체중량 판별 - 25kg 이하 true
|
|
|
|
|
boolean isArcrftWeight = rq.getArcrftList().stream().allMatch(arcrft -> arcrft.getArcrftWghtCd() != ArcrftWghtCd.W25KG_GO && arcrft.getArcrftWghtCd() != ArcrftWghtCd.W25KG_GO_TEST); |
|
|
|
|
rs.setArcrftWeight(isArcrftWeight); |
|
|
|
|
|
|
|
|
|
// 판단구역 - 공역과 겹칠 경우, 비행가능여부
|
|
|
|
|
BasLaancValidatedRs validationPlanDbRs = this.validationPlanAirspace(rq); |
|
|
|
|
rs.setEvaluatedTargetArea(validationPlanDbRs.isEvaluatedTargetArea()); |
|
|
|
|
rs.setFlightArea(validationPlanDbRs.isFlightArea()); |
|
|
|
|
// 특별비행여부 판별 - 야간/주간 - 야간 true
|
|
|
|
|
boolean isSpcialFlight = this.validationPlanSpecialFlight(rq); |
|
|
|
|
rs.setSpacialFlight(isSpcialFlight); |
|
|
|
|
|
|
|
|
|
// 비행방식 - 군집비행 false, 그외 true
|
|
|
|
|
boolean isFltMethod = rq.getAreaList().stream().allMatch(area -> FltMethod.CLUSTER_FLIGHT != area.getFltMethod()); |
|
|
|
|
rs.setFltMethod(isFltMethod); |
|
|
|
|
|
|
|
|
|
return rs; |
|
|
|
|
|
|
|
|
@ -369,113 +377,13 @@ public class BasLaancService {
|
|
|
|
|
return rs; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// LAANC 검증
|
|
|
|
|
/* |
|
|
|
|
private BasLaancValidatedRs validationPlanAreaAndArcrft(BasLaancPlanRq rq) { |
|
|
|
|
|
|
|
|
|
// 초기화
|
|
|
|
|
BasLaancValidatedRs rs = BasLaancValidatedRs.builder() |
|
|
|
|
.isPlanAreaDuplicatd(false) // 비행구역 중복여부
|
|
|
|
|
.isArcrftDuplicated(false) // 기체 중복여부
|
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 비행계획서
|
|
|
|
|
List<FltPlanBas> fltPlanBasList = fltPlanBasRepository.findBySchFltStDtLessThanEqualAndSchFltEndDtGreaterThanEqualAndAprvlYnAndDelYn(rq.getSchFltEndDt(), rq.getSchFltStDt(), "Y", "N"); |
|
|
|
|
if (fltPlanBasList != null && !fltPlanBasList.isEmpty()) { |
|
|
|
|
|
|
|
|
|
if (rq.getPlanSno() != null) { |
|
|
|
|
// 동일한 비행계획서는 검증에서 제외 처리
|
|
|
|
|
fltPlanBasList = fltPlanBasList.stream().filter(fltPlanBas -> !rq.getPlanSno().equals(fltPlanBas.getPlanSno())).collect(Collectors.toList()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 비행계획서 planSno 모음
|
|
|
|
|
List<Integer> planSnoList = fltPlanBasList.stream().map(FltPlanBas::getPlanSno).collect(Collectors.toList()); |
|
|
|
|
|
|
|
|
|
// 지역
|
|
|
|
|
// List<FltPlanArea> fltPlanAreaList = fltPlanAreaRepository.findByPlanSnoIn(planSnoList);
|
|
|
|
|
|
|
|
|
|
for (FltPlanBas fltPlanBas : fltPlanBasList) { |
|
|
|
|
// 1. 구역조회
|
|
|
|
|
List<FltPlanArea> fltPlanAreaList = fltPlanAreaRepository.findByPlanSnoOrderByPlanAreaSnoAsc(fltPlanBas.getPlanSno()); |
|
|
|
|
|
|
|
|
|
// 2. 좌표 조회 -> 영역 포함 여부 확인
|
|
|
|
|
for (FltPlanArea fltPlanArea : fltPlanAreaList) { |
|
|
|
|
String effectiveFltElev = fltPlanArea.getFltElev(); |
|
|
|
|
List<FltPlanAreaCoord> fltPlanAreaCoordList = fltPlanAreaCoordRepository.findByPlanAreaSnoOrderByPlanAreaCoordSnoAsc(fltPlanArea.getPlanAreaSno()); |
|
|
|
|
if (fltPlanAreaCoordList == null || fltPlanAreaCoordList.isEmpty()) continue; |
|
|
|
|
|
|
|
|
|
// 2-1 영역 좌표
|
|
|
|
|
List<Coordinate> effectiveCoordList = fltPlanAreaCoordList.stream().map(fltPlanAreaCoord -> new Coordinate(fltPlanAreaCoord.getLon(), fltPlanAreaCoord.getLat())).collect(Collectors.toList()); |
|
|
|
|
List<Coordinate> effectiveCoordBufferList = new ArrayList<>(); |
|
|
|
|
// Query에서 조회한 좌표로 버퍼좌표 생성
|
|
|
|
|
if ("LINE".equals(fltPlanArea.getAreaType())) { |
|
|
|
|
List<Coordinate> trans = areaUtils.transform(effectiveCoordList, "EPSG:4326", "EPSG:5181"); |
|
|
|
|
List<Coordinate> bufferList = areaUtils.buffer(trans, fltPlanArea.getBufferZone()); |
|
|
|
|
effectiveCoordBufferList = areaUtils.transform(bufferList, "EPSG:5181", "EPSG:4326"); |
|
|
|
|
} |
|
|
|
|
if ("POLYGON".equals(fltPlanArea.getAreaType())) { |
|
|
|
|
effectiveCoordBufferList.addAll(effectiveCoordList); |
|
|
|
|
} |
|
|
|
|
if ("CIRCLE".equals(fltPlanArea.getAreaType())) { |
|
|
|
|
effectiveCoordBufferList = areaUtils.createCircle(effectiveCoordList.get(0), fltPlanArea.getBufferZone()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
for (BasLaancAreaModel basLaancAreaModel : rq.getAreaList()) { |
|
|
|
|
String targetFltElev = basLaancAreaModel.getFltElev(); |
|
|
|
|
// TODO 추후 특정 고도 범위 확인
|
|
|
|
|
boolean isEqualsFltElev = effectiveFltElev.equals(targetFltElev); |
|
|
|
|
// rq로 들어온 좌표로 버퍼좌표 생성
|
|
|
|
|
List<Coordinate> targetCoords = basLaancAreaModel.getCoordList().stream().map(coord -> new Coordinate(coord.getLon(), coord.getLat())).collect(Collectors.toList()); |
|
|
|
|
List<Coordinate> targetBufferCoords = new ArrayList<>(); |
|
|
|
|
if ("LINE".equals(basLaancAreaModel.getAreaType())) { |
|
|
|
|
List<Coordinate> trans = areaUtils.transform(targetCoords, "EPSG:4326", "EPSG:5181"); |
|
|
|
|
List<Coordinate> bufferList = areaUtils.buffer(trans, fltPlanArea.getBufferZone()); |
|
|
|
|
targetBufferCoords = areaUtils.transform(bufferList, "EPSG:5181", "EPSG:4326"); |
|
|
|
|
} else if ("POLYGON".equals(basLaancAreaModel.getAreaType())) { |
|
|
|
|
targetBufferCoords.addAll(targetCoords); |
|
|
|
|
} else if ("CIRCLE".equals(basLaancAreaModel.getAreaType())) { |
|
|
|
|
targetBufferCoords = areaUtils.createCircle(targetCoords.get(0), fltPlanArea.getBufferZone()); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 검증
|
|
|
|
|
Geometry targetGeometry = areaUtils.coordinateToGeometry(targetBufferCoords); |
|
|
|
|
Geometry effectiveGeometry = areaUtils.coordinateToGeometry(effectiveCoordBufferList); |
|
|
|
|
if (targetGeometry.intersects(effectiveGeometry) && isEqualsFltElev) { |
|
|
|
|
rs.setPlanAreaDuplicatd(true); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 기체 중복 여부 확인
|
|
|
|
|
List<BasLaancArcrftModel> arcrftList = rq.getArcrftList(); |
|
|
|
|
if (arcrftList != null && !arcrftList.isEmpty()) { |
|
|
|
|
List<FltPlanArcrft> fltPlanArcrftList = fltPlanArcrftRepository.findByPlanSnoIn(planSnoList); |
|
|
|
|
if (fltPlanArcrftList != null && !fltPlanArcrftList.isEmpty()) { |
|
|
|
|
boolean isDuplicatedArcrft = arcrftList.stream().anyMatch(arcrft -> |
|
|
|
|
fltPlanArcrftList.stream().anyMatch(fltPlanArcrft -> |
|
|
|
|
arcrft.getIdntfNum().equals(fltPlanArcrft.getIdntfNum()) |
|
|
|
|
) |
|
|
|
|
); |
|
|
|
|
if (isDuplicatedArcrft) { |
|
|
|
|
rs.setArcrftDuplicated(true); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return rs; |
|
|
|
|
} |
|
|
|
|
*/ |
|
|
|
|
|
|
|
|
|
private BasLaancValidatedRs validationPlanAirspace(BasLaancPlanRq rq) { |
|
|
|
|
|
|
|
|
|
// 초기화
|
|
|
|
|
BasLaancValidatedRs rs = BasLaancValidatedRs.builder() |
|
|
|
|
.isEvaluatedTargetArea(false) // 판단구역 - 공역과 겹칠 경우
|
|
|
|
|
.isFlightArea(false) // 비행가능여부
|
|
|
|
|
.isElev(false) // 비행가능여부
|
|
|
|
|
.build(); |
|
|
|
|
|
|
|
|
|
// 공역 중복 확인
|
|
|
|
@ -507,17 +415,9 @@ public class BasLaancService {
|
|
|
|
|
boolean duplicatedAirspace = airspaceUtils.isDuplicatedAirspace(featureInfo); |
|
|
|
|
rs.setEvaluatedTargetArea(duplicatedAirspace); |
|
|
|
|
|
|
|
|
|
// 비행 가능 지역 판단
|
|
|
|
|
// if (duplicatedAirspace) {
|
|
|
|
|
// boolean validLaancAirspace = airspaceUtils.isValidLaancAirspace(featureInfo);
|
|
|
|
|
// rs.setFlightAreaYn(validLaancAirspace ? "Y" : "N");
|
|
|
|
|
// } else {
|
|
|
|
|
// rs.setFlightAreaYn("N");
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// 비행 가능 지역 판단
|
|
|
|
|
boolean validLaancAirspace = airspaceUtils.isValidLaancAirspace(featureInfo); |
|
|
|
|
rs.setFlightArea(validLaancAirspace); |
|
|
|
|
rs.setElev(validLaancAirspace); |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|
return rs; |
|
|
|
@ -571,6 +471,29 @@ public class BasLaancService {
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private boolean validationPlanSpecialFlight(BasLaancPlanRq rq) { |
|
|
|
|
|
|
|
|
|
String[] stringStDt = InstantUtils.toDatetimeString(rq.getSchFltStDt()).split(" "); |
|
|
|
|
String[] stringEndDt = InstantUtils.toDatetimeString(rq.getSchFltEndDt()).split(" "); |
|
|
|
|
|
|
|
|
|
ComnSunrisesetCoordRq comnSunrisesetCoordRq = new ComnSunrisesetCoordRq(stringStDt[0].replace("-", ""), stringEndDt[0].replace("-", ""), null, null); |
|
|
|
|
|
|
|
|
|
ComnSunrisesetRs comnSunrisesetRs = comRiseSetQueryRepository.findBySearchCoordDateTransform(comnSunrisesetCoordRq); |
|
|
|
|
|
|
|
|
|
LocalTime sunUp = this.convertStringToTime(comnSunrisesetRs.getCivilm(), "HHmmss"); |
|
|
|
|
LocalTime sunDown = this.convertStringToTime(comnSunrisesetRs.getCivile(), "HHmmss"); |
|
|
|
|
|
|
|
|
|
LocalTime stringStTm = this.convertStringToTime(stringStDt[1].replace(":", ""), "HHmmss"); |
|
|
|
|
LocalTime stringEndTm = this.convertStringToTime(stringEndDt[1].replace(":", ""), "HHmmss"); |
|
|
|
|
|
|
|
|
|
boolean stTmValid = this.isBetweenSunriseAndSunset(sunUp, sunDown, stringStTm); |
|
|
|
|
boolean endTmValid = this.isBetweenSunriseAndSunset(sunUp, sunDown, stringEndTm); |
|
|
|
|
|
|
|
|
|
if (!stTmValid || !endTmValid) return false; |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/** |
|
|
|
|
* String to LocalTime - Instant는 시간정보만 담을 수 없음.. |
|
|
|
|
* |
|
|
|
|