From 18dc831b0597ec3aff7207f8f829b3941dea4fae Mon Sep 17 00:00:00 2001 From: teddy Date: Wed, 25 Feb 2026 22:30:36 +0900 Subject: [PATCH] =?UTF-8?q?=EC=B6=94=EB=A1=A0=20run=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../inference/InferenceRunController.java | 57 ---- .../inference/dto/InferenceResultDto.java | 9 + .../service/InferenceManualService.java | 4 +- .../service/InferenceResultService.java | 298 +++++++++++++----- .../service/InferenceRunService.java | 220 ------------- .../postgres/core/MapSheetMngCoreService.java | 110 ++++++- .../mapsheet/MapSheetMngRepositoryCustom.java | 4 + .../mapsheet/MapSheetMngRepositoryImpl.java | 22 ++ 8 files changed, 368 insertions(+), 356 deletions(-) delete mode 100644 src/main/java/com/kamco/cd/kamcoback/inference/InferenceRunController.java delete mode 100644 src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceRunService.java diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceRunController.java b/src/main/java/com/kamco/cd/kamcoback/inference/InferenceRunController.java deleted file mode 100644 index 7dd6801e..00000000 --- a/src/main/java/com/kamco/cd/kamcoback/inference/InferenceRunController.java +++ /dev/null @@ -1,57 +0,0 @@ -package com.kamco.cd.kamcoback.inference; - -import com.kamco.cd.kamcoback.config.api.ApiResponseDto; -import com.kamco.cd.kamcoback.inference.service.InferenceRunService; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import io.swagger.v3.oas.annotations.media.Content; -import io.swagger.v3.oas.annotations.media.Schema; -import io.swagger.v3.oas.annotations.responses.ApiResponse; -import io.swagger.v3.oas.annotations.responses.ApiResponses; -import io.swagger.v3.oas.annotations.tags.Tag; -import java.util.UUID; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.bind.annotation.RestController; - -@Tag(name = "추론 실행", description = "추론 실행") -@Log4j2 -@RequestMapping("/api/inference/run") -@RequiredArgsConstructor -@RestController -public class InferenceRunController { - - private final InferenceRunService inferenceRunService; - - @Operation(summary = "추론 진행 여부 확인", description = "어드민 홈 > 추론관리 > 추론관리 > 추론관리 목록") - @ApiResponses( - value = { - @ApiResponse( - responseCode = "200", - description = "검색 성공", - content = - @Content( - mediaType = "application/json", - schema = - @Schema( - description = "진행 여부 (UUID 있으면 진행중)", - type = "UUID", - example = "44709877-2e27-4fc5-bacb-8e0328c69b64"))), - @ApiResponse(responseCode = "400", description = "잘못된 검색 조건", content = @Content), - @ApiResponse(responseCode = "500", description = "서버 오류", content = @Content) - }) - @GetMapping - public ApiResponseDto getProcessing( - @Parameter(description = "비교년도", example = "2021") @RequestParam(required = false) - Integer compareYear, - @Parameter(description = "기준년도", example = "2022") @RequestParam(required = false) - Integer targetYear, - @Parameter(description = "모델 uuid") @RequestParam(required = false) UUID modelUuid) { - - inferenceRunService.run(compareYear, targetYear, modelUuid); - return ApiResponseDto.ok(null); - } -} diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java index 452a106b..1ff7b677 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/dto/InferenceResultDto.java @@ -676,4 +676,13 @@ public class InferenceResultDto { private Long m2ModelBatchId; private Long m3ModelBatchId; } + + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class MapSheetFallbackYearDto { + private String mapSheetNum; + private Integer mngYyyy; + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceManualService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceManualService.java index e2128359..2035fb20 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceManualService.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceManualService.java @@ -17,6 +17,8 @@ public class InferenceManualService { if (resultList.isEmpty()) {} - for (InferenceResultsTestingDto.Basic result : resultList) {} + for (InferenceResultsTestingDto.Basic result : resultList) { + System.out.println(result); + } } } diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java index 412e2133..a8cfe00b 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceResultService.java @@ -21,6 +21,7 @@ import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.DetectOption; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceLearnDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceServerStatusDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.InferenceStatusDetailDto; +import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetFallbackYearDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetNumDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.ResultList; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.SaveInferenceAiDto; @@ -46,12 +47,14 @@ import java.util.ArrayList; import java.util.Comparator; import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.Set; import java.util.UUID; +import java.util.function.Function; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.log4j.Log4j2; @@ -119,38 +122,120 @@ public class InferenceResultService { @Transactional public UUID run(InferenceResultDto.RegReq req) { + if (req.getDetectOption().equals(DetectOption.EXCL.getId())) { + return runExcl(req); + } + return runPrev(req); + } + + /** + * 변화탐지 옵션 추론제외 실행 + * + * @param req + * @return + */ + public UUID runExcl(InferenceResultDto.RegReq req) { List targetDtoList = mapSheetMngCoreService.getHstMapSheetList(req); - List compareList = mapSheetMngCoreService.getMapSheetMngHst(req.getCompareYyyy()); + List targetList = - mapSheetMngCoreService.getHstMapSheetList(req).stream() + targetDtoList.stream() .map(MngListDto::getMapSheetNum) .filter(Objects::nonNull) .distinct() .toList(); - log.info( - "hst list count compareList = {}, targetList = {}", compareList.size(), targetList.size()); + List compareList = + mapSheetMngCoreService.getMapSheetNumByHst(req.getCompareYyyy()).stream() + .filter(Objects::nonNull) + .distinct() + .toList(); Set compareSet = new HashSet<>(compareList); + List filteredTargetList = targetList.stream().filter(compareSet::contains).toList(); + + logYearComparison(targetList, compareList, filteredTargetList); + + Scene compareScene = + getSceneInference( + req.getCompareYyyy().toString(), + filteredTargetList, + req.getMapSheetScope(), + req.getDetectOption()); + + Scene targetScene = + getSceneInference( + req.getTargetYyyy().toString(), + filteredTargetList, + req.getMapSheetScope(), + req.getDetectOption()); + + return executeInference(req, targetDtoList, filteredTargetList, compareScene, targetScene); + } + + /** + * 변화탐지 옵션 이전 년도 도엽 사용 실행 + * + * @param req + * @return + */ + @Transactional + public UUID runPrev(InferenceResultDto.RegReq req) { + List targetDtoList = mapSheetMngCoreService.getHstMapSheetList(req); + + List targetList = + targetDtoList.stream() + .map(MngListDto::getMapSheetNum) + .filter(Objects::nonNull) + .distinct() + .toList(); + Set targetSet = new HashSet<>(targetList); - long intersectionCount = - targetSet.stream() - .distinct() - .filter(compareSet::contains) - .count(); // compare와 target에 공통으로 존재하는 도협 수 + List compareDtoList = + new ArrayList<>(mapSheetMngCoreService.getMapSheetNumDtoByHst(req.getCompareYyyy())); - long excludedTargetCount = - targetSet.stream() - .distinct() - .filter(s -> !compareSet.contains(s)) - .count(); // target 에만 존재하는 도협 수 (compare 에는 없음) + Set compareSet = + compareDtoList.stream() + .map(MapSheetFallbackYearDto::getMapSheetNum) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); - long onlyCompareCount = - compareSet.stream() - .distinct() - .filter(s -> !targetSet.contains(s)) - .count(); // compare 에만 존재하는 도협 수 (target 에는 없음) + List remainingTargetList = + targetList.stream().filter(s -> !compareSet.contains(s)).toList(); + + List fallbackYearDtoList = + mapSheetMngCoreService.findFallbackCompareYearByMapSheets( + req.getTargetYyyy(), remainingTargetList); + + compareDtoList.addAll(fallbackYearDtoList); + + // mapSheetNum 기준 중복 제거(기존 compare 우선) + compareDtoList = + new ArrayList<>( + compareDtoList.stream() + .filter(d -> d.getMapSheetNum() != null) + .collect( + Collectors.toMap( + MapSheetFallbackYearDto::getMapSheetNum, + Function.identity(), + (existing, replacement) -> existing, + LinkedHashMap::new)) + .values()); + + Set availableCompareSheets = + compareDtoList.stream() + .map(MapSheetFallbackYearDto::getMapSheetNum) + .filter(Objects::nonNull) + .collect(Collectors.toSet()); + + List filteredTargetList = + targetList.stream().filter(availableCompareSheets::contains).toList(); + + long compareOnlyCount = + compareDtoList.stream() + .map(MapSheetFallbackYearDto::getMapSheetNum) + .filter(s -> s != null && !targetSet.contains(s)) + .count(); log.info( """ @@ -158,53 +243,67 @@ public class InferenceResultService { target Total: {} compare Total: {} Intersection: {} - target Only (Excluded from compare): {} + target Only (Excluded): {} compare Only: {} ==================================== """, - targetSet.size(), - compareSet.size(), - intersectionCount, - excludedTargetCount, - onlyCompareCount); + targetList.size(), + compareDtoList.size(), + filteredTargetList.size(), + targetList.size() - filteredTargetList.size(), + compareOnlyCount); - List filteredTargetList = - targetSet.stream() // target 기준으로 - .filter(compareSet::contains) // compare에 있는 도협만 남김 + // compareDtoList도 filteredTargetList 기준으로 필터 + Set filteredTargetSet = new HashSet<>(filteredTargetList); + List filteredCompareDtoList = + compareDtoList.stream() + .filter(d -> d.getMapSheetNum() != null) + .filter(d -> filteredTargetSet.contains(d.getMapSheetNum())) .toList(); - Scene modelComparePath = - getSceneInference(req.getCompareYyyy().toString(), filteredTargetList, "", ""); + Scene compareScene = + getSceneInference( + filteredCompareDtoList, + req.getCompareYyyy().toString(), + req.getMapSheetScope(), + req.getDetectOption()); - Scene modelTargetPath = - getSceneInference(req.getTargetYyyy().toString(), filteredTargetList, "", ""); + Scene targetScene = + getSceneInference( + req.getTargetYyyy().toString(), + filteredTargetList, + req.getMapSheetScope(), + req.getDetectOption()); - // 작은 쪽 기준으로 탐지건수/파일생성 리스트 결정 - List imageFeatureList; - if (modelComparePath.getFeatures().size() <= modelTargetPath.getFeatures().size()) { - imageFeatureList = modelComparePath.getFeatures(); - } else { - imageFeatureList = modelTargetPath.getFeatures(); - } + return executeInference(req, targetDtoList, filteredTargetList, compareScene, targetScene); + } - // imageFeatureList 기준 sceneId Set - Set sceneIdSet = - imageFeatureList.stream() - .map(ImageFeature::getSceneId) - .filter(Objects::nonNull) - .collect(Collectors.toSet()); + /** + * learn 테이블 저장 및 AI 추론 API 호출 + * + * @param req + * @param targetDtoList + * @param filteredTargetList + * @param modelComparePath + * @param modelTargetPath + * @return + */ + private UUID executeInference( + InferenceResultDto.RegReq req, + List targetDtoList, + List filteredTargetList, + Scene modelComparePath, + Scene modelTargetPath) { + Set filteredSet = new HashSet<>(filteredTargetList); - // targetList(List) 리턴용으로 필터링 List newTargetList = targetDtoList.stream() .filter(m -> m.getMapSheetNum() != null) - .filter(m -> sceneIdSet.contains(m.getMapSheetNum())) + .filter(m -> filteredSet.contains(m.getMapSheetNum())) .toList(); - // 목록 및 추론 대상 도엽정보 저장 UUID uuid = inferenceResultCoreService.saveInferenceInfo(req, newTargetList); - // ai 서버에 전달할 파라미터 생성 pred_requests_areas predRequestsAreas = new pred_requests_areas(); predRequestsAreas.setInput1_year(req.getCompareYyyy()); predRequestsAreas.setInput2_year(req.getTargetYyyy()); @@ -215,12 +314,9 @@ public class InferenceResultService { m1.setPred_requests_areas(predRequestsAreas); log.info("[INFERENCE] Start m1 = {}", m1); - m1.setPred_requests_areas(predRequestsAreas); - // ai 추론 실행 api 호출 Long batchId = ensureAccepted(m1); - // ai 추론 실행후 응답값 update SaveInferenceAiDto saveInferenceAiDto = new SaveInferenceAiDto(); saveInferenceAiDto.setUuid(uuid); saveInferenceAiDto.setBatchId(batchId); @@ -230,11 +326,42 @@ public class InferenceResultService { saveInferenceAiDto.setModelComparePath(modelComparePath.getFilePath()); saveInferenceAiDto.setModelTargetPath(modelTargetPath.getFilePath()); saveInferenceAiDto.setModelStartDttm(ZonedDateTime.now()); + inferenceResultCoreService.update(saveInferenceAiDto); return uuid; } + /** + * EXCL 로그 + * + * @param targetList + * @param compareList + * @param filteredTargetList + */ + private void logYearComparison( + List targetList, List compareList, List filteredTargetList) { + Set targetSet = new HashSet<>(targetList); + + long compareOnlyCount = compareList.stream().filter(s -> !targetSet.contains(s)).count(); + + log.info( + """ + ===== MapSheet Year Comparison ===== + target Total: {} + compare Total: {} + Intersection: {} + target Only (Excluded): {} + compare Only: {} + ==================================== + """, + targetList.size(), + compareList.size(), + filteredTargetList.size(), + targetList.size() - filteredTargetList.size(), + compareOnlyCount); + } + /** * 변화탐지 실행 정보 생성 * @@ -265,31 +392,31 @@ public class InferenceResultService { List totalNumList = new ArrayList<>(); - // if (DetectOption.EXCL.getId().equals(req.getDetectOption())) { - // // "추론제외" 일때 전년도 이전 값이 있어도 전년도 도엽이 없으면 비교 안함 - // for (MngListCompareDto dto : compareList) { - // if (Objects.equals(dto.getBeforeYear(), req.getCompareYyyy())) { - // TotalListDto totalDto = new TotalListDto(); - // totalDto.setBeforeYear(dto.getBeforeYear() == null ? 0 : dto.getBeforeYear()); - // totalDto.setMapSheetNum(dto.getMapSheetNum()); - // totalNumList.add(totalDto); - // } - // } - // } else if (DetectOption.PREV.getId().equals(req.getDetectOption())) { - // // "이전 년도 도엽 사용" 이면 전년도 이전 도엽도 사용 - // for (MngListCompareDto dto : compareList) { - // if (dto.getBeforeYear() != 0) { - // TotalListDto totalDto = new TotalListDto(); - // totalDto.setBeforeYear(dto.getBeforeYear() == null ? 0 : dto.getBeforeYear()); - // totalDto.setMapSheetNum(dto.getMapSheetNum()); - // totalNumList.add(totalDto); - // } - // } - // } - // - // if (totalNumList.isEmpty()) { - // throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND); - // } + if (DetectOption.EXCL.getId().equals(req.getDetectOption())) { + // "추론제외" 일때 전년도 이전 값이 있어도 전년도 도엽이 없으면 비교 안함 + for (MngListCompareDto dto : compareList) { + if (Objects.equals(dto.getBeforeYear(), req.getCompareYyyy())) { + TotalListDto totalDto = new TotalListDto(); + totalDto.setBeforeYear(dto.getBeforeYear() == null ? 0 : dto.getBeforeYear()); + totalDto.setMapSheetNum(dto.getMapSheetNum()); + totalNumList.add(totalDto); + } + } + } else if (DetectOption.PREV.getId().equals(req.getDetectOption())) { + // "이전 년도 도엽 사용" 이면 전년도 이전 도엽도 사용 + for (MngListCompareDto dto : compareList) { + if (dto.getBeforeYear() != 0) { + TotalListDto totalDto = new TotalListDto(); + totalDto.setBeforeYear(dto.getBeforeYear() == null ? 0 : dto.getBeforeYear()); + totalDto.setMapSheetNum(dto.getMapSheetNum()); + totalNumList.add(totalDto); + } + } + } + + if (totalNumList.isEmpty()) { + throw new CustomApiException("NOT_FOUND_COMPARE_YEAR", HttpStatus.NOT_FOUND); + } // 사용할 영상파일 년도 기록 및 추론에 포함되는지 설정 for (MngListDto target : targetList) { @@ -568,6 +695,23 @@ public class InferenceResultService { yyyy, mapSheetNums, mapSheetScope, detectOption); } + /** + * 년도 별로 조회하여 geojson 파일 생성 + * + * @param yearDtos + * @param year + * @param mapSheetScope + * @param detectOption + * @return + */ + private Scene getSceneInference( + List yearDtos, + String year, + String mapSheetScope, + String detectOption) { + return mapSheetMngCoreService.getSceneInference(yearDtos, year, mapSheetScope, detectOption); + } + /** * 분석결과 요약정보 * diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceRunService.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceRunService.java deleted file mode 100644 index 6515bd56..00000000 --- a/src/main/java/com/kamco/cd/kamcoback/inference/service/InferenceRunService.java +++ /dev/null @@ -1,220 +0,0 @@ -package com.kamco.cd.kamcoback.inference.service; - -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.core.type.TypeReference; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.kamco.cd.kamcoback.common.exception.CustomApiException; -import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.Scene; -import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient; -import com.kamco.cd.kamcoback.config.resttemplate.ExternalHttpClient.ExternalCallResult; -import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto; -import com.kamco.cd.kamcoback.inference.dto.InferenceSendDto.pred_requests_areas; -import com.kamco.cd.kamcoback.model.dto.ModelMngDto.Basic; -import com.kamco.cd.kamcoback.model.dto.ModelMngDto.ModelType; -import com.kamco.cd.kamcoback.postgres.core.MapSheetMngCoreService; -import com.kamco.cd.kamcoback.postgres.core.ModelMngCoreService; -import java.nio.file.Paths; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.UUID; -import lombok.RequiredArgsConstructor; -import lombok.extern.log4j.Log4j2; -import org.springframework.beans.factory.annotation.Value; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.stereotype.Service; - -@Service -@Log4j2 -@RequiredArgsConstructor -public class InferenceRunService { - private final ExternalHttpClient externalHttpClient; - private final MapSheetMngCoreService mapSheetMngCoreService; - private final ModelMngCoreService modelMngCoreService; - private final ObjectMapper objectMapper; - - @Value("${inference.url}") - private String inferenceUrl; - - public void run(Integer compareYear, Integer targetYear, UUID modelUuid) { - - List compareList = mapSheetMngCoreService.getMapSheetMngHst(compareYear); - List targetList = mapSheetMngCoreService.getMapSheetMngHst(targetYear); - - log.info( - "hst list count compareList = {}, targetList = {}", compareList.size(), targetList.size()); - - Set compareSet = new HashSet<>(compareList); - Set targetSet = new HashSet<>(targetList); - - long intersectionCount = - targetSet.stream() - .distinct() - .filter(compareSet::contains) - .count(); // compare와 target에 공통으로 존재하는 도협 수 - - long excludedTargetCount = - targetSet.stream() - .distinct() - .filter(s -> !compareSet.contains(s)) - .count(); // target 에만 존재하는 도협 수 (compare 에는 없음) - - long onlyCompareCount = - compareSet.stream() - .distinct() - .filter(s -> !targetSet.contains(s)) - .count(); // compare 에만 존재하는 도협 수 (target 에는 없음) - - log.info( - """ - ===== MapSheet Year Comparison ===== - target Total: {} - compare Total: {} - Intersection: {} - target Only (Excluded from compare): {} - compare Only: {} - ==================================== - """, - targetSet.size(), - compareSet.size(), - intersectionCount, - excludedTargetCount, - onlyCompareCount); - - List filteredTargetList = - targetSet.stream() // target 기준으로 - .filter(compareSet::contains) // compare에 있는 도협만 남김 - .toList(); - - Scene modelComparePath = getSceneInference(compareYear.toString(), filteredTargetList, "", ""); - - Scene modelTargetPath = getSceneInference(targetYear.toString(), filteredTargetList, "", ""); - - // ai 서버에 전달할 파라미터 생성 - pred_requests_areas predRequestsAreas = new pred_requests_areas(); - predRequestsAreas.setInput1_year(compareYear); - predRequestsAreas.setInput2_year(targetYear); - predRequestsAreas.setInput1_scene_path(modelComparePath.getFilePath()); - predRequestsAreas.setInput2_scene_path(modelTargetPath.getFilePath()); - - InferenceSendDto m1 = this.getModelInfo(modelUuid); - m1.setPred_requests_areas(predRequestsAreas); - - // ai 추론 실행 api 호출 - Long batchId = ensureAccepted(m1); - } - - private Scene getSceneInference( - String yyyy, List mapSheetNums, String mapSheetScope, String detectOption) { - return mapSheetMngCoreService.getSceneInference( - yyyy, mapSheetNums, mapSheetScope, detectOption); - } - - /** - * 추론 AI API 호출 - * - * @param dto - */ - private Long ensureAccepted(InferenceSendDto dto) { - - if (dto == null) { - log.warn("not InferenceSendDto dto"); - throw new CustomApiException("BAD_REQUEST", HttpStatus.BAD_REQUEST); - } - - // 1) 요청 로그 - try { - log.debug("Inference request dto={}", objectMapper.writeValueAsString(dto)); - } catch (JsonProcessingException e) { - log.warn("Failed to serialize inference dto", e); - } - - // 3) HTTP 호출 - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.setAccept(List.of(MediaType.APPLICATION_JSON)); - - ExternalCallResult result = - externalHttpClient.call(inferenceUrl, HttpMethod.POST, dto, headers, String.class); - - if (result.statusCode() < 200 || result.statusCode() >= 300) { - log.error("Inference API failed. status={}, body={}", result.statusCode(), result.body()); - throw new CustomApiException("BAD_GATEWAY", HttpStatus.BAD_GATEWAY); - } - - // 4) 응답 파싱 - try { - List> list = - objectMapper.readValue(result.body(), new TypeReference<>() {}); - - if (list.isEmpty()) { - throw new IllegalStateException("Inference response is empty"); - } - - Object batchIdObj = list.get(0).get("batch_id"); - if (batchIdObj == null) { - throw new IllegalStateException("batch_id not found in response"); - } - - return Long.valueOf(batchIdObj.toString()); - - } catch (Exception e) { - log.error("Failed to parse inference response. body={}", result.body(), e); - throw new CustomApiException("INVALID_INFERENCE_RESPONSE", HttpStatus.BAD_GATEWAY); - } - } - - /** - * 모델정보 조회 dto 생성 후 반환 - * - * @param uuid - * @return - */ - private InferenceSendDto getModelInfo(UUID uuid) { - - Basic modelInfo = modelMngCoreService.findByModelUuid(uuid); - - String cdModelPath = ""; - String cdModelConfigPath = ""; - String cdClsModelPath = ""; - - if (modelInfo.getCdModelPath() != null && modelInfo.getCdModelFileName() != null) { - cdModelPath = - Paths.get(modelInfo.getCdModelPath(), modelInfo.getCdModelFileName()).toString(); - } - - if (modelInfo.getCdModelConfig() != null && modelInfo.getCdModelConfigFileName() != null) { - cdModelConfigPath = - Paths.get(modelInfo.getCdModelConfig(), modelInfo.getCdModelConfigFileName()).toString(); - } - - if (modelInfo.getClsModelPath() != null && modelInfo.getClsModelFileName() != null) { - cdClsModelPath = - Paths.get(modelInfo.getClsModelPath(), modelInfo.getClsModelFileName()).toString(); - } - - String modelType = ""; - - if (modelInfo.getModelType().equals(ModelType.G1.getId())) { - modelType = ModelType.G1.getId(); - } else if (modelInfo.getModelType().equals(ModelType.G2.getId())) { - modelType = ModelType.G2.getId(); - } else { - modelType = ModelType.G3.getId(); - } - - InferenceSendDto sendDto = new InferenceSendDto(); - sendDto.setModel_version(modelInfo.getModelVer()); - sendDto.setCd_model_path(cdModelPath); - sendDto.setCd_model_config(cdModelConfigPath); - sendDto.setCls_model_path(cdClsModelPath); - sendDto.setCls_model_version(modelInfo.getModelVer()); - sendDto.setCd_model_type(modelType); - sendDto.setPriority(modelInfo.getPriority()); - return sendDto; - } -} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngCoreService.java index 45d8136c..a06dda7a 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/MapSheetMngCoreService.java @@ -7,6 +7,7 @@ import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature; import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.Scene; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.DetectOption; +import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetFallbackYearDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListCompareDto; @@ -24,6 +25,7 @@ import java.nio.file.Files; import java.nio.file.Path; import java.nio.file.Paths; import java.time.ZonedDateTime; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashMap; import java.util.List; @@ -31,6 +33,7 @@ import java.util.Map; import java.util.Objects; import java.util.Optional; import java.util.UUID; +import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; @@ -309,6 +312,95 @@ public class MapSheetMngCoreService { } } + /** + * 년도별로 추론 실행에 필요한 geojson 파일 생성 + * + * @param yearDtos + * @param yyyy + * @param mapSheetScope + * @param detectOption + * @return + */ + public Scene getSceneInference( + List yearDtos, + String yyyy, + String mapSheetScope, + String detectOption) { + Map result = new HashMap<>(); + boolean isAll = MapSheetScope.ALL.getId().equals(mapSheetScope); + + String optionSuffix = ""; + if (DetectOption.EXCL.getId().equals(detectOption)) { + optionSuffix = "_EXCL"; + } else if (DetectOption.PREV.getId().equals(detectOption)) { + optionSuffix = "_PREV"; + } + + // 1) 경로/파일명 결정 + String targetDir = + "local".equals(activeEnv) ? System.getProperty("user.home") + "/geojson" : inferenceDir; + + String filename = + isAll + ? String.format("%s_%s_ALL%s.geojson", yyyy, activeEnv, optionSuffix) + : String.format("%s_%s%s.geojson", yyyy, activeEnv, optionSuffix); + + Path outputPath = Paths.get(targetDir, filename); + + // 3) 데이터 조회 + Map> groupedByYear = + yearDtos.stream().collect(Collectors.groupingBy(MapSheetFallbackYearDto::getMngYyyy)); + + List sceneInference = new ArrayList<>(); + + // 년도별로 루프 돌리기 + for (Map.Entry> entry : groupedByYear.entrySet()) { + + Integer compareYear = entry.getKey(); + List listByYear = entry.getValue(); + + // 도엽번호만 추출 + List sheetNums = + listByYear.stream().map(MapSheetFallbackYearDto::getMapSheetNum).toList(); + + List tempSceneInference = + mapSheetMngRepository.getSceneInference(compareYear.toString(), sheetNums); + tempSceneInference.addAll(sceneInference); + } + + if (sceneInference == null || sceneInference.isEmpty()) { + log.warn("NOT_FOUND_TARGET_YEAR: yyyy={}, isAll={}, scenesSize={}", yyyy, isAll, 0); + throw new CustomApiException("NOT_FOUND_TARGET_YEAR", HttpStatus.NOT_FOUND); + } + + // 4) 파일 생성 + try { + log.info("create Directories outputPath: {}", outputPath); + log.info( + "activeEnv={}, inferenceDir={}, targetDir={}, filename={}", + activeEnv, + inferenceDir, + targetDir, + filename); + log.info("outputPath={}, parent={}", outputPath.toAbsolutePath(), outputPath.getParent()); + Files.createDirectories(outputPath.getParent()); + + new GeoJsonFileWriter() + .exportToFile(sceneInference, "scene_inference_" + yyyy, 5186, outputPath.toString()); + log.info("GeoJsonFileWriter: {}", "scene_inference_" + yyyy); + Scene scene = new Scene(); + scene.setFeatures(sceneInference); + scene.setFilePath(outputPath.toString()); + + return scene; + + } catch (IOException e) { + log.error( + "FAIL_CREATE_MAP_SHEET_FILE: yyyy={}, isAll={}, path={}", yyyy, isAll, outputPath, e); + throw new CustomApiException("INTERNAL_SERVER_ERROR", HttpStatus.INTERNAL_SERVER_ERROR, e); + } + } + /** * 변화탐지 실행 가능 기준 년도 조회 * @@ -343,8 +435,24 @@ public class MapSheetMngCoreService { return mapSheetMngYearRepository.findByHstMapSheetCompareList(mngYyyy, mapId); } - public List getMapSheetMngHst(Integer year) { + public List getMapSheetNumByHst(Integer year) { List entity = mapSheetMngRepository.getMapSheetMngHst(year); return entity.stream().map(MapSheetMngHstEntity::getMapSheetNum).toList(); } + + public List getMapSheetNumDtoByHst(Integer year) { + List entity = mapSheetMngRepository.getMapSheetMngHst(year); + return entity.stream() + .map( + e -> + new MapSheetFallbackYearDto( + e.getMapSheetNum(), e.getMngYyyy() // 조회 기준 연도 + )) + .toList(); + } + + public List findFallbackCompareYearByMapSheets( + Integer year, List mapIds) { + return mapSheetMngRepository.findFallbackCompareYearByMapSheets(year, mapIds); + } } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryCustom.java index a5e14356..9cfa1af2 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryCustom.java @@ -2,6 +2,7 @@ package com.kamco.cd.kamcoback.postgres.repository.mapsheet; import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; +import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetFallbackYearDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.AddReq; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.MngListDto; @@ -82,4 +83,7 @@ public interface MapSheetMngRepositoryCustom { void insertMapSheetMngTile(@Valid AddReq addReq); List getMapSheetMngHst(Integer year); + + List findFallbackCompareYearByMapSheets( + Integer year, List mapIds); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryImpl.java index 68c181e3..e6bbc270 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/mapsheet/MapSheetMngRepositoryImpl.java @@ -5,12 +5,14 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngEntity.mapSheet import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngFileEntity.mapSheetMngFileEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngHstEntity.mapSheetMngHstEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngTileEntity.mapSheetMngTileEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngYearYnEntity.mapSheetMngYearYnEntity; import static com.kamco.cd.kamcoback.postgres.entity.QYearEntity.yearEntity; import static com.querydsl.core.types.dsl.Expressions.nullExpression; import com.kamco.cd.kamcoback.common.enums.CommonUseStatus; import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; +import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetFallbackYearDto; import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto.MapSheetScope; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.AddReq; @@ -1119,4 +1121,24 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport .and(mapSheetMngFileEntity.fileExt.eq("tif"))) .fetch(); } + + @Override + public List findFallbackCompareYearByMapSheets( + Integer year, List mapIds) { + BooleanBuilder builder = new BooleanBuilder(); + builder.and(mapSheetMngYearYnEntity.id.mapSheetNum.in(mapIds)); + builder.and(mapSheetMngYearYnEntity.id.mngYyyy.lt(year)); + builder.and(mapSheetMngYearYnEntity.yn.eq("Y")); + + return queryFactory + .select( + Projections.constructor( + MapSheetFallbackYearDto.class, + mapSheetMngYearYnEntity.id.mapSheetNum, + mapSheetMngYearYnEntity.id.mngYyyy.max())) + .from(mapSheetMngYearYnEntity) + .where(builder) + .groupBy(mapSheetMngYearYnEntity.id.mapSheetNum) + .fetch(); + } }