Browse Source

허용고도 api, ts연동 api

pull/12/head
지대한 11 months ago
parent
commit
1fb70081dd
  1. 53
      pav-server/src/main/java/com/palnet/biz/api/bas/laanc/controller/BasLaancController.java
  2. 7
      pav-server/src/main/java/com/palnet/biz/api/bas/laanc/model/BasLaancPlanRq.java
  3. 30
      pav-server/src/main/java/com/palnet/biz/api/bas/laanc/model/BasLaancTsRq.java
  4. 29
      pav-server/src/main/java/com/palnet/biz/api/bas/laanc/model/BasLaancTsRs.java
  5. 32
      pav-server/src/main/java/com/palnet/biz/api/bas/laanc/model/BasLaancValidatedRs.java
  6. 113
      pav-server/src/main/java/com/palnet/biz/api/bas/laanc/service/BasLaancService.java
  7. 16
      pav-server/src/main/java/com/palnet/comn/utils/AirspaceUtils.java
  8. 4
      pav-server/src/main/resources/air/elev2d/gimpo-airport-2d-elev.json

53
pav-server/src/main/java/com/palnet/biz/api/bas/laanc/controller/BasLaancController.java

@ -1,8 +1,6 @@
package com.palnet.biz.api.bas.laanc.controller;
import com.palnet.biz.api.bas.laanc.model.BasLaancLastRs;
import com.palnet.biz.api.bas.laanc.model.BasLaancPlanRq;
import com.palnet.biz.api.bas.laanc.model.BasLaancValidatedRs;
import com.palnet.biz.api.bas.laanc.model.*;
import com.palnet.biz.api.bas.laanc.service.BasLaancService;
import com.palnet.biz.api.comn.response.BasicResponse;
import com.palnet.biz.api.comn.response.ErrorResponse;
@ -21,6 +19,7 @@ import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
@ -96,4 +95,52 @@ public class BasLaancController {
return ResponseEntity.ok().body(new SuccessResponse<>(rs));
}
// 허용고도 조회
@PostMapping(value = "/valid/elev")
@ApiOperation(value = "비행구역 허용고도 조회")
@Tag(name = "LAANC", description = "LAANC 관련 API")
public ResponseEntity<? extends BasicResponse> getAllowableElevation(@RequestBody List<BasLaancAreaModel> rq) {
List<Integer> rs = null;
try {
rs = basLaancService.getAllowableElevation(rq);
} catch (CustomException e) {
Map<String, Object> resultMap = new HashMap<String, Object>();
log.error("IGNORE : ", e);
resultMap.put("result", false);
resultMap.put("errorCode", e.getErrorCode());
resultMap.put("errorMessage", e.getMessage());
return ResponseEntity.ok().body(new SuccessResponse<Map>(resultMap));
} catch (Exception e) {
log.error("IGNORE : ", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("Server Error", "-1"));
}
return ResponseEntity.ok().body(new SuccessResponse<>(rs));
}
// TS 연동
@PostMapping(value = "/valid/ts/pilot")
@ApiOperation(value = "TS 연동 - 비행자격 조회(조종사 자격증명, 기체보험 여부)")
@Tag(name = "LAANC", description = "LAANC 관련 API")
public ResponseEntity<? extends BasicResponse> getTsValid(@RequestBody List<String> idntfNumList) {
BasLaancTsRs rs = null;
try {
rs = basLaancService.getTsValid(idntfNumList);
} catch (CustomException e) {
Map<String, Object> resultMap = new HashMap<String, Object>();
log.error("IGNORE : ", e);
resultMap.put("result", false);
resultMap.put("errorCode", e.getErrorCode());
resultMap.put("errorMessage", e.getMessage());
return ResponseEntity.ok().body(new SuccessResponse<Map>(resultMap));
} catch (Exception e) {
log.error("IGNORE : ", e);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR)
.body(new ErrorResponse("Server Error", "-1"));
}
return ResponseEntity.ok().body(new SuccessResponse<>(rs));
}
}

7
pav-server/src/main/java/com/palnet/biz/api/bas/laanc/model/BasLaancPlanRq.java

@ -1,6 +1,7 @@
package com.palnet.biz.api.bas.laanc.model;
import com.palnet.biz.api.acnt.cstmr.model.AnctCstmrTermsModel;
import com.palnet.biz.api.external.model.PilotValidRs;
import com.palnet.biz.jpa.entity.type.FltPurpose;
import com.palnet.biz.jpa.entity.type.FltType;
import lombok.Data;
@ -26,8 +27,10 @@ public class BasLaancPlanRq {
// private List<BasFlightPlanPilotModel> pilotList;
private List<BasLaancArcrftModel> arcrftList;
// laanc 정보
private BasLaancValidatedRs validatedRs;
// 약관동의
private List<AnctCstmrTermsModel> terms;
// laanc 정보
// private BasLaancValidatedRs validatedRs;
private List<PilotValidRs> pilotValidRsList; // 자격여부
}

30
pav-server/src/main/java/com/palnet/biz/api/bas/laanc/model/BasLaancTsRq.java

@ -0,0 +1,30 @@
package com.palnet.biz.api.bas.laanc.model;
import io.swagger.annotations.ApiParam;
import io.swagger.v3.oas.annotations.media.Schema;
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 : BasLaancTsRq
* author : dhji
* date : 2023-10-25(025)
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2023-10-25(025) dhji 최초 생성
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BasLaancTsRq {
@ApiParam(name = "기체신고번호", collectionFormat = ",")
private List<String> IdntfNumList;
}

29
pav-server/src/main/java/com/palnet/biz/api/bas/laanc/model/BasLaancTsRs.java

@ -0,0 +1,29 @@
package com.palnet.biz.api.bas.laanc.model;
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 : BasLaancTsRs
* author : dhji
* date : 2023-10-25(025)
* description :
* ===========================================================
* DATE AUTHOR NOTE
* -----------------------------------------------------------
* 2023-10-25(025) dhji 최초 생성
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class BasLaancTsRs {
private boolean isValid;
private List<PilotValidRs> pilotValidRsList;
}

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

@ -10,8 +10,8 @@ import lombok.NoArgsConstructor;
import java.util.List;
/**
* packageName : com.palnet.biz.api.bas.flight.model
* fileName : BasFlightPlanErrorRS
* packageName : com.palnet.biz.api.bas.laanc.model
* fileName : BasLaancValidatedRs
* author : dhji
* date : 2023-09-19(019)
* description :
@ -26,6 +26,7 @@ import java.util.List;
@AllArgsConstructor
public class BasLaancValidatedRs {
private boolean isPilotQlfc; // 자격여부
private boolean isArcrftInsurance; // 항공기보험여부
@ -33,11 +34,17 @@ public class BasLaancValidatedRs {
private boolean isFlightArea; // 비행가능여부 - 비행가능 true
private boolean isArcrftWeight; // 항공기중량여부 - 25kg 이하 true
private boolean isElev; // 고도여부 - 150m 이하 true
private boolean isReport; // 신고 대상 - 상업적이면서 기체중량2kg 이상일경우 - true
private boolean isReport; // 신고 대상 - 비상업적이고 기체중량 2kg미만일 경우 - false / 그외 true
private String corpRegYn; // 사업자 여부
// 추가....
private boolean isCommercial; // 비행유형 사업자 - true / 비사업자 - false
private boolean isSpacialFlight; // 특별비행여부 - true
// TS 연동
// private String corpRegYn; // 사업자 여부
private List<PilotValidRs> pilotValidRsList; // 자격여부
// 활용안함.
@ -53,21 +60,32 @@ public class BasLaancValidatedRs {
&& isFlightArea // 비행가능여부
&& isCheckingLance();
}
return isCheckingLance()
return isFlightArea
// && !isPlanAreaDuplicatd
// && !isArcrftDuplicated
&& isFlightArea;
&& isCheckingLance();
}
public boolean isFlight() {
return !isCheckingLance() || isValid();
}
// LAANC 승인 대상 여부
@JsonIgnore
public boolean isCheckingLance() {
return isEvaluatedTargetArea // 관제구역여부
|| !isElev // 150m 초과
/*
관제권 고도 150m초과에 대해서 승인 대상이나 담당자와의 협의가 필요하여 미승인 처리함
|| !isElev // 150m 초과 -
*/
|| !isArcrftWeight; // 25kg 초과
}
// 특별승인 대상 여부
@JsonIgnore
public boolean isTargetSpacialFlight() {
return false;
}
}

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

@ -10,6 +10,7 @@ import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import com.palnet.biz.api.bas.laanc.model.*;
import org.locationtech.jts.geom.Coordinate;
import org.locationtech.jts.geom.Geometry;
import org.springframework.stereotype.Service;
@ -19,12 +20,6 @@ import org.springframework.util.StringUtils;
import com.palnet.biz.api.acnt.cstmr.model.AnctCstmrModel;
import com.palnet.biz.api.acnt.cstmr.model.AnctCstmrTermsModel;
import com.palnet.biz.api.acnt.jwt.utils.JwtTokenUtil;
import com.palnet.biz.api.bas.laanc.model.BasLaancArcrftModel;
import com.palnet.biz.api.bas.laanc.model.BasLaancAreaCoordModel;
import com.palnet.biz.api.bas.laanc.model.BasLaancAreaModel;
import com.palnet.biz.api.bas.laanc.model.BasLaancLastRs;
import com.palnet.biz.api.bas.laanc.model.BasLaancPlanRq;
import com.palnet.biz.api.bas.laanc.model.BasLaancValidatedRs;
import com.palnet.biz.api.comn.file.model.LaancPdfModel;
import com.palnet.biz.api.comn.file.service.ComnFileService;
import com.palnet.biz.api.comn.sms.model.ComnSmsLaancAprovModel;
@ -102,17 +97,30 @@ public class BasLaancService {
// LAANC 검증
public BasLaancValidatedRs validationLaanc(BasLaancPlanRq rq) {
if (!this.laancParamValid(rq)) throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
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 초과이거나 상업적일 경우 - true
// 신고 여부 - 비상업적이고 기체중량 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 -> {
@ -136,12 +144,6 @@ public class BasLaancService {
// TODO end - 조종사 자격 및 기체보험 확인
// 고도 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);
/* 비행구역 기체 중복여부 확인 안하기로 .
// 비행구역 중복여부, 기체 중복여부
@ -163,10 +165,10 @@ public class BasLaancService {
// 비행계획서 등록, 약관 등록, 공문 생성
@Transactional
public BasLaancLastRs createFlightPlan(BasLaancPlanRq rq) {
if (rq == null || rq.getValidatedRs() == null) throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
// if (rq == null || rq.getValidatedRs() == null) throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
if (!this.laancParamValid(rq)) throw new CustomException(ErrorCode.NON_VALID_PARAMETER);
BasLaancValidatedRs basLaancValidatedRs = rq.getValidatedRs();
// BasLaancValidatedRs basLaancValidatedRs = rq.getValidatedRs();
// BasLaancValidatedRs validatedAirspaceRs = this.validationPlanAirspace(rq);
@ -178,7 +180,7 @@ public class BasLaancService {
// BasLaancMapper.MAPPER.merge(basLaancValidatedRs, validatedRs);
// LAANC가 검증된것들만 DB저장
if (!basLaancValidatedRs.isValid()) throw new CustomException(ErrorCode.PLAN_LAANC_NOT_VALID);
// if (!basLaancValidatedRs.isValid()) throw new CustomException(ErrorCode.PLAN_LAANC_NOT_VALID);
// 비행계획서
@ -206,7 +208,12 @@ public class BasLaancService {
}
fltPlanBas.setGroupId(groupId);
// 사업자 유무 - TS 데이터
fltPlanBas.setCorpRegYn(rq.getValidatedRs().getCorpRegYn()); // 사업자유무
String corpRegYn = "N";
if (rq.getPilotValidRsList() != null && !rq.getPilotValidRsList().isEmpty()) {
boolean isCorpReg = rq.getPilotValidRsList().stream().anyMatch(pilotValidRs -> "Y".equals(pilotValidRs.getCorpregyn()));
corpRegYn = isCorpReg ? "Y" : "N";
}
fltPlanBas.setCorpRegYn(corpRegYn); // 사업자유무
fltPlanBas.setServiceType("PAV-KAC");
FltPlanBas rBasEntity = fltPlanBasRepository.save(fltPlanBas);
@ -240,8 +247,8 @@ public class BasLaancService {
Integer planAreaSno = rAreaEntity.getPlanAreaSno();
List<BasLaancAreaCoordModel> laancAreaModelCoordList = laancAreaModel.getCoordList();
if (laancAreaModelCoordList != null && !laancAreaModelCoordList.isEmpty()) {
laancAreaModelCoordList.get(0).getLat();
laancAreaModelCoordList.get(0).getLon();
// double lat = laancAreaModelCoordList.get(0).getLat();
// double lon = laancAreaModelCoordList.get(0).getLon();
for (BasLaancAreaCoordModel basLaancAreaCoordModel : laancAreaModelCoordList) {
FltPlanAreaCoord coordEntity = BasLaancMapper.MAPPER.modelToPlanEntity(basLaancAreaCoordModel);
coordEntity.setPlanAreaSno(planAreaSno);
@ -269,8 +276,17 @@ public class BasLaancService {
arcrftEntity.setPlanSno(planSno);
arcrftEntity.setCreateUserId(userId);
arcrftEntity.setUpdateUserId(userId);
String arcrftInsuranceYn = "N";
// 추가 필드
arcrftEntity.setAcrftInsuranceYn(rq.getValidatedRs().isArcrftInsurance() ? "Y" : "N"); // 보험여부
if (rq.getPilotValidRsList() != null && !rq.getPilotValidRsList().isEmpty() && basLaancArcrftModel.getIdntfNum() != null) {
PilotValidRs pilotValidRs = rq.getPilotValidRsList().stream().filter(pilotValid -> pilotValid.getRq().getDeclarationnum().equals(arcrftEntity.getIdntfNum())).findFirst().orElse(null);
if (pilotValidRs != null) {
String arcrftinsuranceyn = pilotValidRs.getArcrftinsuranceyn();
arcrftInsuranceYn = "Y".equals(arcrftinsuranceyn) ? "Y" : "N";
}
}
arcrftEntity.setAcrftInsuranceYn(arcrftInsuranceYn); // 보험여부
// arcrftEntity.setInsuranceExperiod(null); // 보헌 유효기간
// arcrftEntity.setCorporationNm(null); // 법인명
fltPlanArcrftRepository.save(arcrftEntity);
@ -458,7 +474,6 @@ public class BasLaancService {
return rs;
}
*/
private BasLaancValidatedRs validationPlanAirspace(BasLaancPlanRq rq) {
@ -586,4 +601,60 @@ public class BasLaancService {
timeToCheck.isBefore(sunset);
}
// 허용고도 조회
public List<Integer> getAllowableElevation(List<BasLaancAreaModel> rq) {
AirspaceUtils airspaceUtils = AirspaceUtils.getInstance();
List<Integer> allowElevationList = new ArrayList<>();
for (BasLaancAreaModel area : rq) {
//rq로 들어온 좌표로 버퍼좌표 생성
List<Coordinate> targetCoord = new ArrayList<>();
List<Coordinate> targetCoordBufferList = new ArrayList<>();
for (BasLaancAreaCoordModel basLaancAreaCoordModel : area.getCoordList()) {
Coordinate coords = new Coordinate(basLaancAreaCoordModel.getLon(), basLaancAreaCoordModel.getLat());
targetCoord.add(coords);
}
if ("LINE".equals(area.getAreaType())) {
List<Coordinate> trans = areaUtils.transform(targetCoord, "EPSG:4326", "EPSG:5181");
List<Coordinate> bufferList = areaUtils.buffer(trans, area.getBufferZone());
targetCoordBufferList = areaUtils.transform(bufferList, "EPSG:5181", "EPSG:4326");
} else if ("POLYGON".equals(area.getAreaType())) {
targetCoordBufferList.addAll(targetCoord);
} else if ("CIRCLE".equals(area.getAreaType())) {
targetCoordBufferList = areaUtils.createCircle(targetCoord.get(0), area.getBufferZone());
}
Geometry targetGeometry = airspaceUtils.createGeometryByCoordinate(targetCoordBufferList);
// Integer fltElev = area.getFltElev() != null ? Integer.parseInt(area.getFltElev()) : 0;
// AirspaceUtils.FeatureInfo featureInfo = new AirspaceUtils.FeatureInfo(null, null, 0, fltElev, rqGeometry);
Integer allowElevation = airspaceUtils.getAllowElevation(targetGeometry);
allowElevationList.add(allowElevation);
}
return allowElevationList;
}
public BasLaancTsRs getTsValid(List<String> idntfNumList) {
// List<String> idntfNumList = rq.getIdntfNumList();
Integer cstmrSno = jwtTokenUtil.getCstmrSnoByToken();
AnctCstmrModel cstmrInfo = ptyCstmrQueryRepository.findByCstmrSno(cstmrSno);
String userCi = cstmrInfo.getIpinCi();
List<PilotValidRq> pilotValidRqList = idntfNumList.stream().map(declarationnum -> {
// TODO 기체보험 확인, 조종사 자격 확인
return PilotValidRq.builder()
.pilotci(userCi)
.declarationnum(declarationnum)
.build();
}).collect(Collectors.toList());
List<PilotValidRs> pilotValidRsList = tsService.callPilotValid(pilotValidRqList);
boolean isValid = pilotValidRsList.stream().noneMatch(pilotValidRs -> !"Y".equals(pilotValidRs.getPilotcredentialyn()) || !"Y".equals(pilotValidRs.getArcrftinsuranceyn()));
BasLaancTsRs rs = BasLaancTsRs.builder()
.isValid(isValid)
.pilotValidRsList(pilotValidRsList)
.build();
return rs;
}
}

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

@ -119,11 +119,27 @@ public class AirspaceUtils {
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)) {

4
pav-server/src/main/resources/air/elev2d/gimpo-airport-2d-elev.json

@ -3900,7 +3900,7 @@
"type": "0006",
"use": true,
"lowElev": "0",
"highElev": "40"
"highElev": "45"
}
},
{
@ -4458,7 +4458,7 @@
"type": "0006",
"use": true,
"lowElev": "0",
"highElev": "40"
"highElev": "45"
}
},
{

Loading…
Cancel
Save