From 73a3e1360caac9785f01213d0faeda4a691f62fd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dean=5B=EB=B0=B1=EB=B3=91=EB=82=A8=5D?= Date: Tue, 30 Dec 2025 18:02:09 +0900 Subject: [PATCH 1/4] api scene test --- build.gradle | 4 + .../common/geometry/GeoJsonFileWriter.java | 157 +++++++++++++ .../common/geometry/GeometryUtils.java | 17 ++ .../mapsheet/service/MapSheetMngService.java | 55 +++-- .../postgres/core/MapSheetMngCoreService.java | 57 ++++- .../mapsheet/MapSheetMngRepositoryCustom.java | 3 + .../mapsheet/MapSheetMngRepositoryImpl.java | 38 ++++ .../scene/MapInkxMngApiV2ControllerTest.java | 208 ------------------ 8 files changed, 315 insertions(+), 224 deletions(-) create mode 100644 src/main/java/com/kamco/cd/kamcoback/common/geometry/GeoJsonFileWriter.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/common/geometry/GeometryUtils.java delete mode 100644 src/test/java/com/kamco/cd/kamcoback/scene/MapInkxMngApiV2ControllerTest.java diff --git a/build.gradle b/build.gradle index c71c8a94..7546b58a 100644 --- a/build.gradle +++ b/build.gradle @@ -84,6 +84,10 @@ dependencies { implementation 'com.fasterxml.jackson.datatype:jackson-datatype-jsr310' implementation 'org.reflections:reflections:0.10.2' + + + implementation 'org.locationtech.jts:jts-core:1.19.0' + implementation 'org.locationtech.jts.io:jts-io-common:1.19.0' } configurations.configureEach { diff --git a/src/main/java/com/kamco/cd/kamcoback/common/geometry/GeoJsonFileWriter.java b/src/main/java/com/kamco/cd/kamcoback/common/geometry/GeoJsonFileWriter.java new file mode 100644 index 00000000..f7257919 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/common/geometry/GeoJsonFileWriter.java @@ -0,0 +1,157 @@ +package com.kamco.cd.kamcoback.common.geometry; + +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import java.io.FileWriter; +import java.io.IOException; +import java.util.List; +import org.locationtech.jts.geom.Geometry; + +/** GeoJSON 파일 생성 유틸리티 */ +public class GeoJsonFileWriter { + + private final ObjectMapper objectMapper; + + public GeoJsonFileWriter() { + this.objectMapper = new ObjectMapper(); + } + + public GeoJsonFileWriter(ObjectMapper objectMapper) { + this.objectMapper = objectMapper; + } + + /** + * GeoJSON 문자열 생성 + * + * @param features Feature 목록 + * @param name GeoJSON name 속성 + * @param srid CRS EPSG 코드 + * @return GeoJSON 문자열 + */ + public String buildGeoJson(List features, String name, int srid) { + try { + ObjectNode root = objectMapper.createObjectNode(); + root.put("type", "FeatureCollection"); + root.put("name", name); + + // CRS 정보 + ObjectNode crs = objectMapper.createObjectNode(); + crs.put("type", "name"); + ObjectNode crsProps = objectMapper.createObjectNode(); + crsProps.put("name", "urn:ogc:def:crs:EPSG::" + srid); + crs.set("properties", crsProps); + root.set("crs", crs); + + // Features 배열 + ArrayNode featuresArray = objectMapper.createArrayNode(); + for (ImageFeature f : features) { + featuresArray.add(buildFeature(f)); + } + root.set("features", featuresArray); + + return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(root); + + } catch (Exception e) { + throw new RuntimeException("GeoJSON 생성 실패", e); + } + } + + /** 단일 Feature 객체 생성 */ + private ObjectNode buildFeature(ImageFeature f) throws Exception { + ObjectNode feature = objectMapper.createObjectNode(); + feature.put("type", "Feature"); + + // Properties + ObjectNode properties = objectMapper.createObjectNode(); + properties.put("scene_id", f.getSceneId()); + properties.put("abs_path", f.getAbsPath()); + properties.put("basename", f.getFileName()); + properties.put("georef_source", "internal"); + properties.put("crs_source", "transformed"); + feature.set("properties", properties); + + // Geometry (CRS 제거) + ObjectNode geometry = (ObjectNode) objectMapper.readTree(f.getGeomJson()); + geometry.remove("crs"); + feature.set("geometry", geometry); + + return feature; + } + + /** + * 파일로 저장 + * + * @param geojson GeoJSON 문자열 + * @param filePath 저장 경로 + */ + public void writeToFile(String geojson, String filePath) throws IOException { + try (FileWriter writer = new FileWriter(filePath)) { + writer.write(geojson); + } + } + + /** Feature 목록을 바로 파일로 저장 */ + public void exportToFile(List features, String name, int srid, String filePath) + throws IOException { + String geojson = buildGeoJson(features, name, srid); + writeToFile(geojson, filePath); + } + + /** Feature 데이터 클래스 */ + public static class ImageFeature { + + private String sceneId; + private String filePath; + private String fileName; + private String geomJson; + + public ImageFeature() {} + + public ImageFeature(String sceneId, String filePath, String fileName, Geometry geomJson) { + this.sceneId = sceneId; + this.filePath = filePath; + this.fileName = fileName; + if (geomJson != null) { + + this.geomJson = GeometryUtils.toGeoJson(geomJson); + } + } + + public String getSceneId() { + return sceneId; + } + + public void setSceneId(String sceneId) { + this.sceneId = sceneId; + } + + public String getFilePath() { + return filePath; + } + + public void setFilePath(String filePath) { + this.filePath = filePath; + } + + public String getFileName() { + return fileName; + } + + public void setFileName(String fileName) { + this.fileName = fileName; + } + + public String getGeomJson() { + return geomJson; + } + + public void setGeomJson(String geomJson) { + this.geomJson = geomJson; + } + + public String getAbsPath() { + return filePath + "/" + fileName; + } + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/common/geometry/GeometryUtils.java b/src/main/java/com/kamco/cd/kamcoback/common/geometry/GeometryUtils.java new file mode 100644 index 00000000..dff401d3 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/common/geometry/GeometryUtils.java @@ -0,0 +1,17 @@ +package com.kamco.cd.kamcoback.common.geometry; + +import org.locationtech.jts.geom.Geometry; +import org.locationtech.jts.io.geojson.GeoJsonWriter; + +public class GeometryUtils { + + private static final GeoJsonWriter GEOJSON_WRITER = new GeoJsonWriter(8); + + /** JTS Geometry를 GeoJSON 문자열로 변환 */ + public static String toGeoJson(Geometry geometry) { + if (geometry == null) { + return null; + } + return GEOJSON_WRITER.write(geometry); + } +} diff --git a/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java b/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java index 19613312..07a1c1f1 100644 --- a/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java +++ b/src/main/java/com/kamco/cd/kamcoback/mapsheet/service/MapSheetMngService.java @@ -116,7 +116,9 @@ public class MapSheetMngService { // 업로드 파일 사이즈,확장자명 체크 dmlReturn = this.validationFile(tfwFile, tifFile); - if (dmlReturn.getFlag().equals("fail")) return dmlReturn; + if (dmlReturn.getFlag().equals("fail")) { + return dmlReturn; + } MngDto mngDto = mapSheetMngCoreService.findMapSheetMng(errDto.getMngYyyy()); String targetYearDir = mngDto.getMngPath(); @@ -126,20 +128,28 @@ public class MapSheetMngService { dmlReturn = this.duplicateFile( errDto.getMngYyyy(), tfwFile.getOriginalFilename(), tifFile.getOriginalFilename()); - if (dmlReturn.getFlag().equals("duplicate")) return dmlReturn; + if (dmlReturn.getFlag().equals("duplicate")) { + return dmlReturn; + } } // 멀티파트 파일 tmp폴더 저장(파일형식 체크를 위해) String tfwTmpPath = tmpPath + tfwFile.getOriginalFilename(); String tifTmpPath = tmpPath + tifFile.getOriginalFilename(); - if (!FIleChecker.multipartSaveTo(tfwFile, tfwTmpPath)) + if (!FIleChecker.multipartSaveTo(tfwFile, tfwTmpPath)) { return new DmlReturn("fail", "UPLOAD ERROR"); - if (!FIleChecker.multipartSaveTo(tifFile, tifTmpPath)) + } + if (!FIleChecker.multipartSaveTo(tifFile, tifTmpPath)) { return new DmlReturn("fail", "UPLOAD ERROR"); + } - if (!FIleChecker.cmmndGdalInfo(tifTmpPath)) return new DmlReturn("fail", "TIF TYPE ERROR"); - if (!FIleChecker.checkTfw(tfwTmpPath)) return new DmlReturn("fail", "TFW TYPE ERROR"); + if (!FIleChecker.cmmndGdalInfo(tifTmpPath)) { + return new DmlReturn("fail", "TIF TYPE ERROR"); + } + if (!FIleChecker.checkTfw(tfwTmpPath)) { + return new DmlReturn("fail", "TFW TYPE ERROR"); + } // 싱크파일목록으로 업로드 경로 확인 List mngFiles = mapSheetMngCoreService.findByHstUidMapSheetFileList(hstUid); @@ -232,8 +242,11 @@ public class MapSheetMngService { reqDto.setFilePath(dto.getFilePath()); reqDto.setSyncCheckState("DONE"); - if (dto.getFileExt().equals("tif")) reqDto.setSyncCheckTifFileName(dto.getFileName()); - else if (dto.getFileExt().equals("tfw")) reqDto.setSyncCheckTfwFileName(dto.getFileName()); + if (dto.getFileExt().equals("tif")) { + reqDto.setSyncCheckTifFileName(dto.getFileName()); + } else if (dto.getFileExt().equals("tfw")) { + reqDto.setSyncCheckTfwFileName(dto.getFileName()); + } mapSheetMngCoreService.updateByFileUidFileState(uid, "DONE"); } @@ -247,12 +260,15 @@ public class MapSheetMngService { } public DmlReturn validationFile(MultipartFile tfwFile, MultipartFile tifFile) { - if (!FIleChecker.validationMultipart(tfwFile)) return new DmlReturn("fail", "TFW SIZE 오류"); - else if (!FIleChecker.validationMultipart(tifFile)) return new DmlReturn("fail", "TFW SIZE 오류"); - else if (!FIleChecker.checkExtensions(tfwFile.getOriginalFilename(), "tfw")) + if (!FIleChecker.validationMultipart(tfwFile)) { + return new DmlReturn("fail", "TFW SIZE 오류"); + } else if (!FIleChecker.validationMultipart(tifFile)) { + return new DmlReturn("fail", "TFW SIZE 오류"); + } else if (!FIleChecker.checkExtensions(tfwFile.getOriginalFilename(), "tfw")) { return new DmlReturn("fail", "TFW FILENAME ERROR"); - else if (!FIleChecker.checkExtensions(tifFile.getOriginalFilename(), "tif")) + } else if (!FIleChecker.checkExtensions(tifFile.getOriginalFilename(), "tif")) { return new DmlReturn("fail", "TIF FILENAME ERROR"); + } return new DmlReturn("success", "파일체크"); } @@ -263,11 +279,16 @@ public class MapSheetMngService { if (tfwCnt > 0 || tifCnt > 0) { String resMsg = ""; - if (tfwCnt > 0) resMsg = tfwFileName; + if (tfwCnt > 0) { + resMsg = tfwFileName; + } if (tifCnt > 0) { - if (tfwCnt > 0) resMsg = resMsg + "," + tifFileName; - else resMsg = tifFileName; + if (tfwCnt > 0) { + resMsg = resMsg + "," + tifFileName; + } else { + resMsg = tifFileName; + } } return new DmlReturn("duplicate", resMsg); } @@ -313,4 +334,8 @@ public class MapSheetMngService { return new FilesDto(dirPath, fileTotCnt, fileTotSize, files); } + + public void getSceneInference(String yyyy) { + mapSheetMngCoreService.getSceneInference(yyyy); + } } 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 678f06e0..fb84e790 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 @@ -1,5 +1,7 @@ package com.kamco.cd.kamcoback.postgres.core; +import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter; +import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.YearSearchReq; import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngEntity; @@ -8,24 +10,33 @@ import com.kamco.cd.kamcoback.postgres.entity.YearEntity; import com.kamco.cd.kamcoback.postgres.repository.mapsheet.MapSheetMngRepository; import jakarta.persistence.EntityNotFoundException; import jakarta.validation.Valid; +import java.io.IOException; +import java.nio.file.Files; +import java.nio.file.Paths; +import java.util.Arrays; import java.util.List; import java.util.Objects; import java.util.Optional; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Value; import org.springframework.data.domain.Page; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +@Slf4j @Service @RequiredArgsConstructor public class MapSheetMngCoreService { private final MapSheetMngRepository mapSheetMngRepository; - @Value("{spring.profiles.active}") + @Value("${spring.profiles.active}") private String activeEnv; + @Value("${file.sync-root-dir}") + private String syncRootDir; + public List findMapSheetMngList() { return mapSheetMngRepository.findMapSheetMngList(); } @@ -145,4 +156,48 @@ public class MapSheetMngCoreService { public void deleteByNotInFileUidMngFile(Long hstUid, List fileUids) { mapSheetMngRepository.deleteByNotInFileUidMngFile(hstUid, fileUids); } + + /** + * 추론용 장면 데이터를 GeoJSON 파일로 내보내기 + * + * @param yyyy 연도 + */ + public void getSceneInference(String yyyy) { + try { + List scenes = + Arrays.asList( + "34602060", + "35615072", + "35813026", + "35813027", + "35813028", + "35813029", + "35813021", + "35813030", + "35813022"); + List sceneInference = mapSheetMngRepository.getSceneInference(yyyy, scenes); + + if (sceneInference == null || sceneInference.isEmpty()) { + log.warn("No scene data found for year: {}", yyyy); + return; + } + + String filename = String.format("%s_%s.geojson", yyyy, activeEnv); + String outputPath = Paths.get(syncRootDir, filename).toString(); + + // 디렉토리가 없으면 생성 + Files.createDirectories(Paths.get(syncRootDir)); + + // GeoJSON 파일 생성 (EPSG:5186 - Korea 2000 / Central Belt 2010) + GeoJsonFileWriter writer = new GeoJsonFileWriter(); + writer.exportToFile(sceneInference, "scene_inference_" + yyyy, 5186, outputPath); + + log.info("GeoJSON file created successfully: {}", outputPath); + log.info("Total features exported: {}", sceneInference.size()); + + } catch (IOException e) { + log.error("Failed to create GeoJSON file for year: {}", yyyy, e); + throw new RuntimeException("GeoJSON 파일 생성 실패: " + e.getMessage(), e); + } + } } 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 e5729585..63ddbf0b 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 @@ -1,5 +1,6 @@ package com.kamco.cd.kamcoback.postgres.repository.mapsheet; +import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.YearSearchReq; import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity; @@ -65,4 +66,6 @@ public interface MapSheetMngRepositoryCustom { int findByYearFileNameFileCount(int mngYyyy, String fileName); Page getYears(YearSearchReq req); + + List getSceneInference(String yyyy, List mapSheetNums); } 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 8fc981c8..fad395a7 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 @@ -7,6 +7,7 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngFileEntity.mapS import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetMngHstEntity.mapSheetMngHstEntity; import static com.kamco.cd.kamcoback.postgres.entity.QYearEntity.yearEntity; +import com.kamco.cd.kamcoback.common.geometry.GeoJsonFileWriter.ImageFeature; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto; import com.kamco.cd.kamcoback.mapsheet.dto.MapSheetMngDto.YearSearchReq; import com.kamco.cd.kamcoback.postgres.entity.MapSheetMngHstEntity; @@ -819,6 +820,43 @@ public class MapSheetMngRepositoryImpl extends QuerydslRepositorySupport return new PageImpl<>(content, pageable, total); } + @Override + public List getSceneInference(String yyyy, List mapSheetNums) { + BooleanBuilder whereBuilder = new BooleanBuilder(); + + // sync_state = 'DONE' + whereBuilder.and(mapSheetMngHstEntity.syncState.eq("DONE")); + + // file_ext = 'tif' + whereBuilder.and(mapSheetMngFileEntity.fileExt.eq("tif")); + + // mng_yyyy = '2023' + if (yyyy != null && !yyyy.isEmpty()) { + whereBuilder.and(mapSheetMngHstEntity.mngYyyy.eq(Integer.parseInt(yyyy))); + } + + // mapidcd_no in (...) + if (mapSheetNums != null && !mapSheetNums.isEmpty()) { + whereBuilder.and(mapInkx5kEntity.mapidcdNo.in(mapSheetNums)); + } + + return queryFactory + .select( + Projections.constructor( + ImageFeature.class, + mapSheetMngHstEntity.mapSheetNum, + mapSheetMngFileEntity.filePath, + mapSheetMngFileEntity.fileName, + mapInkx5kEntity.geom)) + .from(mapSheetMngHstEntity) + .innerJoin(mapSheetMngFileEntity) + .on(mapSheetMngHstEntity.hstUid.eq(mapSheetMngFileEntity.hstUid)) + .innerJoin(mapSheetMngHstEntity.mapInkx5kByCode, mapInkx5kEntity) + .where(whereBuilder) + .orderBy(mapSheetMngHstEntity.mapSheetNum.desc()) + .fetch(); + } + private BooleanExpression eqYearStatus(QYearEntity years, String status) { if (status == null) { return null; diff --git a/src/test/java/com/kamco/cd/kamcoback/scene/MapInkxMngApiV2ControllerTest.java b/src/test/java/com/kamco/cd/kamcoback/scene/MapInkxMngApiV2ControllerTest.java deleted file mode 100644 index 3d6c5355..00000000 --- a/src/test/java/com/kamco/cd/kamcoback/scene/MapInkxMngApiV2ControllerTest.java +++ /dev/null @@ -1,208 +0,0 @@ -package com.kamco.cd.kamcoback.scene; - -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.ArgumentMatchers.eq; -import static org.mockito.Mockito.when; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.result.MockMvcResultHandlers.print; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import com.kamco.cd.kamcoback.auth.JwtTokenProvider; -import com.kamco.cd.kamcoback.common.enums.CommonUseStatus; -import com.kamco.cd.kamcoback.inference.dto.InferenceResultDto; -import com.kamco.cd.kamcoback.menu.service.MenuService; -import com.kamco.cd.kamcoback.postgres.repository.log.AuditLogRepository; -import com.kamco.cd.kamcoback.postgres.repository.log.ErrorLogRepository; -import com.kamco.cd.kamcoback.scene.dto.MapInkxMngDto; -import com.kamco.cd.kamcoback.scene.service.MapInkxMngService; -import java.time.ZonedDateTime; -import java.util.List; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; -import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.PageRequest; -import org.springframework.test.context.bean.override.mockito.MockitoBean; -import org.springframework.test.web.servlet.MockMvc; - -@WebMvcTest(MapInkxMngApiV2Controller.class) -@AutoConfigureMockMvc(addFilters = false) -class MapInkxMngApiV2ControllerTest { - - @Autowired private MockMvc mockMvc; - - @MockitoBean private MapInkxMngService mapInkxMngService; - - @MockitoBean private JwtTokenProvider jwtTokenProvider; - - @MockitoBean private ErrorLogRepository errorLogRepository; - - @MockitoBean private AuditLogRepository auditLogRepository; - - @MockitoBean private MenuService menuService; - - @Test - @DisplayName("도엽 목록 조회 - 기본 파라미터") - void findMapInkxMngList_withDefaultParams() throws Exception { - // Given - InferenceResultDto.MapSheet scene50k = new InferenceResultDto.MapSheet("36713", "논산"); - InferenceResultDto.MapSheet scene5k1 = new InferenceResultDto.MapSheet("36713029", "논산"); - InferenceResultDto.MapSheet scene5k2 = new InferenceResultDto.MapSheet("36713085", "논산"); - - MapInkxMngDto.MapListEntity entity1 = - MapInkxMngDto.MapListEntity.builder() - .scene50k(scene50k) - .scene5k(scene5k1) - .useInference(CommonUseStatus.USE) - .createdDttm(ZonedDateTime.now()) - .updatedDttm(ZonedDateTime.now()) - .build(); - - MapInkxMngDto.MapListEntity entity2 = - MapInkxMngDto.MapListEntity.builder() - .scene50k(scene50k) - .scene5k(scene5k2) - .useInference(CommonUseStatus.USE) - .createdDttm(ZonedDateTime.now()) - .updatedDttm(ZonedDateTime.now()) - .build(); - - Page page = - new PageImpl<>(List.of(entity1, entity2), PageRequest.of(0, 20), 2); - - when(mapInkxMngService.findMapInkxMngLists(eq(null), eq(null), any())).thenReturn(page); - - // When & Then - mockMvc - .perform(get("/api/v2/scene")) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.data.content").isArray()) - .andExpect(jsonPath("$.data.content[0].scene5k.number").value("36713029")) - .andExpect(jsonPath("$.data.content[0].scene5k.name").value("논산")) - .andExpect(jsonPath("$.data.content[1].scene5k.number").value("36713085")) - .andExpect(jsonPath("$.data.totalElements").value(2)); - } - - @Test - @DisplayName("도엽 목록 조회 - useInference 파라미터 포함") - void findMapInkxMngList_withUseInferenceParam() throws Exception { - // Given - InferenceResultDto.MapSheet scene50k = new InferenceResultDto.MapSheet("36713", "논산"); - InferenceResultDto.MapSheet scene5k = new InferenceResultDto.MapSheet("36713029", "논산"); - - MapInkxMngDto.MapListEntity entity = - MapInkxMngDto.MapListEntity.builder() - .scene50k(scene50k) - .scene5k(scene5k) - .useInference(CommonUseStatus.EXCEPT) - .createdDttm(ZonedDateTime.now()) - .updatedDttm(ZonedDateTime.now()) - .build(); - - Page page = - new PageImpl<>(List.of(entity), PageRequest.of(0, 20), 1); - - when(mapInkxMngService.findMapInkxMngLists(eq(CommonUseStatus.EXCEPT), eq(null), any())) - .thenReturn(page); - - // When & Then - mockMvc - .perform(get("/api/v2/scene").param("useInference", "EXCEPT")) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.data.content").isArray()) - .andExpect(jsonPath("$.data.content[0].useInference.id").value("EXCEPT")) - .andExpect(jsonPath("$.data.totalElements").value(1)); - } - - @Test - @DisplayName("도엽 목록 조회 - searchVal 파라미터로 검색") - void findMapInkxMngList_withSearchVal() throws Exception { - // Given - InferenceResultDto.MapSheet scene50k = new InferenceResultDto.MapSheet("36713029", "공덕"); - InferenceResultDto.MapSheet scene5k = new InferenceResultDto.MapSheet("31540687", "공덕"); - - MapInkxMngDto.MapListEntity entity = - MapInkxMngDto.MapListEntity.builder() - .scene50k(scene50k) - .scene5k(scene5k) - .useInference(CommonUseStatus.USE) - .createdDttm(ZonedDateTime.now()) - .updatedDttm(ZonedDateTime.now()) - .build(); - - Page page = - new PageImpl<>(List.of(entity), PageRequest.of(0, 20), 1); - - when(mapInkxMngService.findMapInkxMngLists(eq(null), eq("공덕"), any())).thenReturn(page); - - // When & Then - mockMvc - .perform(get("/api/v2/scene").param("searchVal", "공덕")) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.data.content[0].scene5k.name").value("공덕")) - .andExpect(jsonPath("$.data.totalElements").value(1)); - } - - @Test - @DisplayName("도엽 목록 조회 - 페이징 파라미터") - void findMapInkxMngList_withPagingParams() throws Exception { - // Given - Page page = new PageImpl<>(List.of(), PageRequest.of(1, 10), 0); - - when(mapInkxMngService.findMapInkxMngLists(eq(null), eq(null), any())).thenReturn(page); - - // When & Then - mockMvc - .perform(get("/api/v2/scene").param("page", "1").param("size", "10")) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.data.pageable.pageNumber").value(1)) - .andExpect(jsonPath("$.data.pageable.pageSize").value(10)); - } - - @Test - @DisplayName("도엽 목록 조회 - 모든 파라미터 포함") - void findMapInkxMngList_withAllParams() throws Exception { - // Given - InferenceResultDto.MapSheet scene50k = new InferenceResultDto.MapSheet("31540", "공덕"); - InferenceResultDto.MapSheet scene5k = new InferenceResultDto.MapSheet("31540687", "공덕"); - - MapInkxMngDto.MapListEntity entity = - MapInkxMngDto.MapListEntity.builder() - .scene50k(scene50k) - .scene5k(scene5k) - .useInference(CommonUseStatus.USE) - .createdDttm(ZonedDateTime.now()) - .updatedDttm(ZonedDateTime.now()) - .build(); - - Page page = - new PageImpl<>(List.of(entity), PageRequest.of(0, 5), 1); - - when(mapInkxMngService.findMapInkxMngLists(eq(CommonUseStatus.USE), eq("공덕"), any())) - .thenReturn(page); - - // When & Then - mockMvc - .perform( - get("/api/v2/scene") - .param("page", "0") - .param("size", "5") - .param("useInference", "USE") - .param("searchVal", "공덕")) - .andDo(print()) - .andExpect(status().isOk()) - .andExpect(jsonPath("$.data.content[0].scene5k.number").value("31540687")) - .andExpect(jsonPath("$.data.content[0].scene5k.name").value("공덕")) - .andExpect(jsonPath("$.data.content[0].useInference.id").value("USE")) - .andExpect(jsonPath("$.data.totalElements").value(1)) - .andExpect(jsonPath("$.data.pageable.pageSize").value(5)); - } -} From 8bf404d257265332eea18b9a9a3a60e3d92dc8cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dean=5B=EB=B0=B1=EB=B3=91=EB=82=A8=5D?= Date: Mon, 5 Jan 2026 16:53:01 +0900 Subject: [PATCH 2/4] feat: test code --- .../MapSheetMngServiceSceneInferenceTest.java | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 src/test/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngServiceSceneInferenceTest.java diff --git a/src/test/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngServiceSceneInferenceTest.java b/src/test/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngServiceSceneInferenceTest.java new file mode 100644 index 00000000..d71b2e1d --- /dev/null +++ b/src/test/java/com/kamco/cd/kamcoback/mapsheet/MapSheetMngServiceSceneInferenceTest.java @@ -0,0 +1,29 @@ +package com.kamco.cd.kamcoback.mapsheet; + +import com.kamco.cd.kamcoback.mapsheet.service.MapSheetMngService; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.context.TestPropertySource; + +@SpringBootTest +@ActiveProfiles("local") +@TestPropertySource(properties = {"file.sync-root-dir=/tmp/kamco-test"}) +class MapSheetMngServiceSceneInferenceTest { + + @Autowired private MapSheetMngService mapSheetMngService; + + @Test + @DisplayName("2023년 장면 추론 GeoJSON 파일 생성") + void createSceneInference2023() { + mapSheetMngService.getSceneInference("2023"); + } + + @Test + @DisplayName("2024년 장면 추론 GeoJSON 파일 생성") + void createSceneInference2024() { + mapSheetMngService.getSceneInference("2024"); + } +} From 47ab483d4269063e45f284c8ea3aed90fda31749 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dean=5B=EB=B0=B1=EB=B3=91=EB=82=A8=5D?= Date: Mon, 5 Jan 2026 17:00:35 +0900 Subject: [PATCH 3/4] =?UTF-8?q?json=20=ED=98=95=EC=8B=9D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../cd/kamcoback/postgres/core/MapSheetMngCoreService.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 fb84e790..25e1aab1 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 @@ -158,7 +158,7 @@ public class MapSheetMngCoreService { } /** - * 추론용 장면 데이터를 GeoJSON 파일로 내보내기 + * 추론용 장면 데이터를 GeoJSON 파일로 내보내기 연도 및 지역대상도 받아야함 * * @param yyyy 연도 */ From ef78498d84f02c6fdceaddc4735a7e5cdb87e49c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?dean=5B=EB=B0=B1=EB=B3=91=EB=82=A8=5D?= Date: Mon, 5 Jan 2026 17:15:43 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=EC=B6=94=EB=A1=A0=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20json?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/etc/2023_requests.json | 271 ++++++++++++++++++++++ src/main/resources/etc/2024_requests.json | 271 ++++++++++++++++++++++ 2 files changed, 542 insertions(+) create mode 100644 src/main/resources/etc/2023_requests.json create mode 100644 src/main/resources/etc/2024_requests.json diff --git a/src/main/resources/etc/2023_requests.json b/src/main/resources/etc/2023_requests.json new file mode 100644 index 00000000..ea841d74 --- /dev/null +++ b/src/main/resources/etc/2023_requests.json @@ -0,0 +1,271 @@ +{ + "type": "FeatureCollection", + "name": "scene_inference_2023", + "crs": { + "type": "name", + "properties": { + "name": "urn:ogc:def:crs:EPSG::5186" + } + }, + "features": [ + { + "type": "Feature", + "properties": { + "scene_id": "35813030", + "abs_path": "/kamco-nfs/images/2023/정사영상(12)/12/5. 경남제주도시권역/35813/35813030.tif", + "basename": "35813030.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.25000001, + 35.175 + ], + [ + 128.22499996, + 35.17500002 + ], + [ + 128.22499996, + 35.19999998 + ], + [ + 128.24999999, + 35.19999997 + ], + [ + 128.25000001, + 35.175 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813029", + "abs_path": "/kamco-nfs/images/2023/정사영상(12)/12/5. 경남제주도시권역/35813/35813029.tif", + "basename": "35813029.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.22499996, + 35.17500002 + ], + [ + 128.20000004, + 35.17500002 + ], + [ + 128.19999995, + 35.19999996 + ], + [ + 128.22499996, + 35.19999998 + ], + [ + 128.22499996, + 35.17500002 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813028", + "abs_path": "/kamco-nfs/images/2023/정사영상(12)/12/5. 경남제주도시권역/35813/35813028.tif", + "basename": "35813028.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.20000004, + 35.17500002 + ], + [ + 128.17500005, + 35.17499998 + ], + [ + 128.17499998, + 35.2 + ], + [ + 128.19999995, + 35.19999996 + ], + [ + 128.20000004, + 35.17500002 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813027", + "abs_path": "/kamco-nfs/images/2023/정사영상(12)/12/5. 경남제주도시권역/35813/35813027.tif", + "basename": "35813027.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.17500005, + 35.17499998 + ], + [ + 128.14999998, + 35.17500001 + ], + [ + 128.15000005, + 35.20000002 + ], + [ + 128.17499998, + 35.2 + ], + [ + 128.17500005, + 35.17499998 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813026", + "abs_path": "/kamco-nfs/images/2023/정사영상(12)/12/5. 경남제주도시권역/35813/35813026.tif", + "basename": "35813026.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.14999998, + 35.17500001 + ], + [ + 128.12499995, + 35.17500002 + ], + [ + 128.12500004, + 35.20000001 + ], + [ + 128.15000005, + 35.20000002 + ], + [ + 128.14999998, + 35.17500001 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813022", + "abs_path": "/kamco-nfs/images/2023/정사영상(12)/12/5. 경남제주도시권역/35813/35813022.tif", + "basename": "35813022.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.04999996, + 35.17499999 + ], + [ + 128.02499998, + 35.17499999 + ], + [ + 128.02499995, + 35.19999998 + ], + [ + 128.05000002, + 35.2 + ], + [ + 128.04999996, + 35.17499999 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813021", + "abs_path": "/kamco-nfs/images/2023/정사영상(12)/12/5. 경남제주도시권역/35813/35813021.tif", + "basename": "35813021.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.02499998, + 35.17499999 + ], + [ + 128.00000004, + 35.17499996 + ], + [ + 128.00000004, + 35.20000002 + ], + [ + 128.02499995, + 35.19999998 + ], + [ + 128.02499998, + 35.17499999 + ] + ] + ] + } + } + ] +} diff --git a/src/main/resources/etc/2024_requests.json b/src/main/resources/etc/2024_requests.json new file mode 100644 index 00000000..8433be54 --- /dev/null +++ b/src/main/resources/etc/2024_requests.json @@ -0,0 +1,271 @@ +{ + "type": "FeatureCollection", + "name": "scene_inference_2024", + "crs": { + "type": "name", + "properties": { + "name": "urn:ogc:def:crs:EPSG::5186" + } + }, + "features": [ + { + "type": "Feature", + "properties": { + "scene_id": "35813030", + "abs_path": "/kamco-nfs/images/2024/10.24년정사영상_25cm/0013/35813/35813030.tif", + "basename": "35813030.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.25000001, + 35.175 + ], + [ + 128.22499996, + 35.17500002 + ], + [ + 128.22499996, + 35.19999998 + ], + [ + 128.24999999, + 35.19999997 + ], + [ + 128.25000001, + 35.175 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813029", + "abs_path": "/kamco-nfs/images/2024/10.24년정사영상_25cm/0013/35813/35813029.tif", + "basename": "35813029.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.22499996, + 35.17500002 + ], + [ + 128.20000004, + 35.17500002 + ], + [ + 128.19999995, + 35.19999996 + ], + [ + 128.22499996, + 35.19999998 + ], + [ + 128.22499996, + 35.17500002 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813028", + "abs_path": "/kamco-nfs/images/2024/10.24년정사영상_25cm/0013/35813/35813028.tif", + "basename": "35813028.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.20000004, + 35.17500002 + ], + [ + 128.17500005, + 35.17499998 + ], + [ + 128.17499998, + 35.2 + ], + [ + 128.19999995, + 35.19999996 + ], + [ + 128.20000004, + 35.17500002 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813027", + "abs_path": "/kamco-nfs/images/2024/10.24년정사영상_25cm/0013/35813/35813027.tif", + "basename": "35813027.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.17500005, + 35.17499998 + ], + [ + 128.14999998, + 35.17500001 + ], + [ + 128.15000005, + 35.20000002 + ], + [ + 128.17499998, + 35.2 + ], + [ + 128.17500005, + 35.17499998 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813026", + "abs_path": "/kamco-nfs/images/2024/10.24년정사영상_25cm/0013/35813/35813026.tif", + "basename": "35813026.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.14999998, + 35.17500001 + ], + [ + 128.12499995, + 35.17500002 + ], + [ + 128.12500004, + 35.20000001 + ], + [ + 128.15000005, + 35.20000002 + ], + [ + 128.14999998, + 35.17500001 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813022", + "abs_path": "/kamco-nfs/images/2024/10.24년정사영상_25cm/0013/35813/35813022.tif", + "basename": "35813022.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.04999996, + 35.17499999 + ], + [ + 128.02499998, + 35.17499999 + ], + [ + 128.02499995, + 35.19999998 + ], + [ + 128.05000002, + 35.2 + ], + [ + 128.04999996, + 35.17499999 + ] + ] + ] + } + }, + { + "type": "Feature", + "properties": { + "scene_id": "35813021", + "abs_path": "/kamco-nfs/images/2024/10.24년정사영상_25cm/0013/35813/35813021.tif", + "basename": "35813021.tif", + "georef_source": "internal", + "crs_source": "transformed" + }, + "geometry": { + "type": "Polygon", + "coordinates": [ + [ + [ + 128.02499998, + 35.17499999 + ], + [ + 128.00000004, + 35.17499996 + ], + [ + 128.00000004, + 35.20000002 + ], + [ + 128.02499995, + 35.19999998 + ], + [ + 128.02499998, + 35.17499999 + ] + ] + ] + } + } + ] +}