Browse Source

laanc validate 재구성(적용x)

pull/16/head
지대한 10 months ago
parent
commit
3215b07e8a
  1. 81
      pav-server/src/main/java/com/palnet/biz/api/bas/laanc/model/BasLaancValidatedRs.java
  2. 231
      pav-server/src/main/java/com/palnet/biz/api/bas/laanc/service/BasLaancService.java
  3. 2
      pav-server/src/main/java/com/palnet/comn/utils/AirspaceUtils.java

81
pav-server/src/main/java/com/palnet/biz/api/bas/laanc/model/BasLaancValidatedRs.java

@ -1,14 +1,10 @@
package com.palnet.biz.api.bas.laanc.model;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.palnet.biz.api.external.model.PilotValidRs;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;
import java.util.List;
/**
* packageName : com.palnet.biz.api.bas.laanc.model
* fileName : BasLaancValidatedRs
@ -26,66 +22,39 @@ import java.util.List;
@AllArgsConstructor
public class BasLaancValidatedRs {
// // TS 연동
// private String corpRegYn; // 사업자 여부
// private List<PilotValidRs> pilotValidRsList; // 자격여부
private boolean isPilotQlfc; // 자격여부
private boolean isArcrftInsurance; // 항공기보험여부
// 비행유형 여부
private boolean isCommercial; // 사업자 - true, 비사업자 - false
// 관제권
private boolean isEvaluatedTargetArea; // 평가대상지역여부 - 공역과 겹칠때만 true
private boolean isFlightArea; // 비행가능여부 - 비행가능 true
// 고도
private boolean isElev; // 고도여부 - 관제권내 설정고도 이하, 권제권밖 150m 이하 true
// 기체중량
private boolean isArcrftWeight; // 항공기중량여부 - 25kg 이하 true
// 비행방식
private boolean isFltMethod; // 비행방식 - 군집비행 false, 그외 true
// 비행시간
private boolean isSpacialFlight; // 특별비행여부 - true (야간비행)
// 자격여부
private boolean isPilotQlfc; // 자격여부
// 기체보험여부
private boolean isArcrftInsurance; // 기체보험여부
private boolean isElev; // 고도여부 - 150m 이하 true
private boolean isReport; // 신고 대상 - 비상업적이고 기체중량 2kg미만일 경우 - false / 그외 true
// 추가....
private boolean isCommercial; // 비행유형 사업자 - true / 비사업자 - false
private boolean isSpacialFlight; // 특별비행여부 - true
// TS 연동
// private String corpRegYn; // 사업자 여부
private List<PilotValidRs> pilotValidRsList; // 자격여부
// 활용안함.
// private boolean isArcrftDuplicated; // 기체 중복여부
// private boolean isPlanAreaDuplicatd; // 비행계획서비행구역 중복여부
// 최종여부
public boolean isValid() {
if (isReport) {
return isPilotQlfc
&& isArcrftInsurance
// && !isArcrftDuplicated // 기체 중복여부
// && !isPlanAreaDuplicatd // 비행구역 중복여부
&& isFlightArea // 비행가능여부
&& isCheckingLance();
}
return isFlightArea
// && !isPlanAreaDuplicatd
// && !isArcrftDuplicated
&& isCheckingLance();
return !isNonAprove() && !isNonAproveFlight();
}
public boolean isFlight() {
return !isCheckingLance() || isValid();
// 미승인
public boolean isNonAprove() {
return isFltMethod && !isSpacialFlight && isElev && isPilotQlfc && isArcrftInsurance;
}
// LAANC 승인 대상 여부
@JsonIgnore
public boolean isCheckingLance() {
return isEvaluatedTargetArea // 관제구역여부
/*
관제권 고도 150m초과에 대해서 승인 대상이나 담당자와의 협의가 필요하여 미승인 처리함
|| !isElev // 150m 초과 -
*/
|| !isArcrftWeight; // 25kg 초과
// 비행가능여부(미승인-날수있음)
public boolean isNonAproveFlight() {
return !isCommercial && !isEvaluatedTargetArea && isElev && isArcrftWeight;
}
// 특별승인 대상 여부
@JsonIgnore
public boolean isTargetSpacialFlight() {
return false;
}
}

231
pav-server/src/main/java/com/palnet/biz/api/bas/laanc/service/BasLaancService.java

@ -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())));
}
List<PilotValidRs> pilotValidRsList = tsService.getAccountValidate(pilotValidRqList);
if (pilotValidRsList.isEmpty()) {
rs.setPilotQlfc(false);
rs.setArcrftInsurance(false);
} else {
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는 시간정보만 담을 없음..
*

2
pav-server/src/main/java/com/palnet/comn/utils/AirspaceUtils.java

@ -100,7 +100,7 @@ public class AirspaceUtils {
for (FeatureInfo featureInfo : this.airspaces) {
Geometry airspaceGeometry = featureInfo.getGeometry();
int airspaceHighElev = featureInfo.getHighElev() != null ? featureInfo.getHighElev() : 0;
// 임시로 0~최대고도 기준으로 검증
// 0~최대고도 기준으로 검증
if (airspaceHighElev == 0 || airspaceHighElev < targetHighElev) {
if (airspaceGeometry.intersects(targetGeometry)) {
return false;

Loading…
Cancel
Save