From f74d49f7e1308b9fbec69b3dcb0821801cfc1cdb Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Thu, 27 Nov 2025 18:13:42 +0900 Subject: [PATCH 1/2] =?UTF-8?q?:spotlessApply=20=EC=8B=A4=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeDetectionApiController.java | 21 +-- .../dto/ChangeDetectionDto.java | 47 ++++--- .../service/LearningModelResultProcessor.java | 133 ++++++++++++------ .../core/ChangeDetectionCoreService.java | 3 +- .../postgres/entity/ImageryEntity.java | 12 +- .../postgres/entity/MapInkx50kEntity.java | 7 +- .../postgres/entity/MapInkx5kEntity.java | 6 +- .../MapSheetLearnDataGeomRepository.java | 25 ++-- .../ChangeDetectionRepositoryCustom.java | 2 - .../ChangeDetectionRepositoryImpl.java | 116 +++++++-------- 10 files changed, 211 insertions(+), 161 deletions(-) diff --git a/src/main/java/com/kamco/cd/kamcoback/changedetection/ChangeDetectionApiController.java b/src/main/java/com/kamco/cd/kamcoback/changedetection/ChangeDetectionApiController.java index 9b18e568..01e6ab03 100644 --- a/src/main/java/com/kamco/cd/kamcoback/changedetection/ChangeDetectionApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/changedetection/ChangeDetectionApiController.java @@ -9,12 +9,10 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.transaction.Transactional; +import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.web.bind.annotation.*; -import java.time.LocalDate; -import java.util.List; - @Tag(name = "변화탐지", description = "변화탐지 API") @RequiredArgsConstructor @RestController @@ -33,34 +31,37 @@ public class ChangeDetectionApiController { /** * PolygonData -> JsonNode 변환 예제 + * * @return */ @Hidden @GetMapping("/json-data") - public ApiResponseDto> getPolygonToJson(){ + public ApiResponseDto> getPolygonToJson() { return ApiResponseDto.ok(changeDetectionService.getPolygonToJson()); } @Operation(summary = "변화탐지 분류별 건수", description = "변화탐지 분류별 건수") @GetMapping("/class-count/{id}") public ApiResponseDto> getChangeDetectionClassCount( - @Parameter(description = "변화탐지 년도(차수) /year-list 의 analUid", example = "1") @PathVariable Long id){ + @Parameter(description = "변화탐지 년도(차수) /year-list 의 analUid", example = "1") @PathVariable + Long id) { return ApiResponseDto.ok(changeDetectionService.getChangeDetectionClassCount(id)); } @Operation(summary = "변화탐지 COG Url", description = "변화탐지 COG Url") @GetMapping("/cog-url") public ApiResponseDto getChangeDetectionCogUrl( - @Parameter(description = "이전 년도", example = "2023") @RequestParam Integer beforeYear, - @Parameter(description = "이후 년도", example = "2024") @RequestParam Integer afterYear, - @Parameter(description = "도엽번호(5k)", example = "36809010") @RequestParam String mapSheetNum){ - ChangeDetectionDto.CogUrlReq req = new ChangeDetectionDto.CogUrlReq(beforeYear, afterYear, mapSheetNum); + @Parameter(description = "이전 년도", example = "2023") @RequestParam Integer beforeYear, + @Parameter(description = "이후 년도", example = "2024") @RequestParam Integer afterYear, + @Parameter(description = "도엽번호(5k)", example = "36809010") @RequestParam String mapSheetNum) { + ChangeDetectionDto.CogUrlReq req = + new ChangeDetectionDto.CogUrlReq(beforeYear, afterYear, mapSheetNum); return ApiResponseDto.ok(changeDetectionService.getChangeDetectionCogUrl(req)); } @Operation(summary = "변화탐지 년도(차수) 목록", description = "변화탐지 년도(차수) 목록") @GetMapping("/year-list") - public ApiResponseDto> getChangeDetectionYearList(){ + public ApiResponseDto> getChangeDetectionYearList() { return ApiResponseDto.ok(changeDetectionService.getChangeDetectionYearList()); } } diff --git a/src/main/java/com/kamco/cd/kamcoback/changedetection/dto/ChangeDetectionDto.java b/src/main/java/com/kamco/cd/kamcoback/changedetection/dto/ChangeDetectionDto.java index 59e5f6a4..4d237536 100644 --- a/src/main/java/com/kamco/cd/kamcoback/changedetection/dto/ChangeDetectionDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/changedetection/dto/ChangeDetectionDto.java @@ -8,7 +8,7 @@ import lombok.NoArgsConstructor; import lombok.Setter; import org.locationtech.jts.geom.Geometry; -public class ChangeDetectionDto{ +public class ChangeDetectionDto { @Schema(name = "TestDto", description = "테스트용") @Getter @@ -18,7 +18,8 @@ public class ChangeDetectionDto{ public static class TestDto { private Long id; private Geometry polygon; - private Double centroidX;; + private Double centroidX; + ; private Double centroidY; } @@ -28,9 +29,9 @@ public class ChangeDetectionDto{ @NoArgsConstructor @AllArgsConstructor public static class CountDto { - private String classCd; //영문코드 - private String className; //한글명 - private Long count; //건수 + private String classCd; // 영문코드 + private String className; // 한글명 + private Long count; // 건수 } @Schema(name = "CogUrlReq", description = "COG Url Req") @@ -73,12 +74,12 @@ public class ChangeDetectionDto{ @Setter @NoArgsConstructor @AllArgsConstructor - public static class PointGeometry{ + public static class PointGeometry { private Long geoUid; - private String type; // "Point" - private Geometry coordinates; //Point 값 - private String before_class; //기준 분류 - private String after_class; //비교 분류 + private String type; // "Point" + private Geometry coordinates; // Point 값 + private String before_class; // 기준 분류 + private String after_class; // 비교 분류 } @Schema(name = "PolygonGeometry", description = "폴리곤 리턴 객체") @@ -86,12 +87,12 @@ public class ChangeDetectionDto{ @Setter @NoArgsConstructor @AllArgsConstructor - public static class PolygonGeometry{ + public static class PolygonGeometry { private Long geoUid; - private String type; // "MultiPolygon" - private Geometry coordinates; //Polygon 값 - private Double center_latitude; //폴리곤 중심 위도 - private Double center_longitude; //폴리곤 중심 경도 + private String type; // "MultiPolygon" + private Geometry coordinates; // Polygon 값 + private Double center_latitude; // 폴리곤 중심 위도 + private Double center_longitude; // 폴리곤 중심 경도 } @Schema(name = "PolygonProperties", description = "폴리곤 정보") @@ -99,13 +100,13 @@ public class ChangeDetectionDto{ @Setter @NoArgsConstructor @AllArgsConstructor - public static class PolygonProperties{ - private Double area; //면적 - private String before_year; //기준년도 - private Double before_confidence; //기준 신뢰도(확률) - private String before_class; //기준 분류 - private String after_year; //비교년도 - private Double after_confidence; //비교 신뢰도(확률) - private String after_class; //비교 분류 + public static class PolygonProperties { + private Double area; // 면적 + private String before_year; // 기준년도 + private Double before_confidence; // 기준 신뢰도(확률) + private String before_class; // 기준 분류 + private String after_year; // 비교년도 + private Double after_confidence; // 비교 신뢰도(확률) + private String after_class; // 비교 분류 } } diff --git a/src/main/java/com/kamco/cd/kamcoback/inference/service/LearningModelResultProcessor.java b/src/main/java/com/kamco/cd/kamcoback/inference/service/LearningModelResultProcessor.java index e568e32c..48156819 100644 --- a/src/main/java/com/kamco/cd/kamcoback/inference/service/LearningModelResultProcessor.java +++ b/src/main/java/com/kamco/cd/kamcoback/inference/service/LearningModelResultProcessor.java @@ -3,7 +3,6 @@ package com.kamco.cd.kamcoback.inference.service; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnDataEntity; -import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnDataGeomEntity; import com.kamco.cd.kamcoback.postgres.repository.MapSheetLearnDataGeomRepository; import com.kamco.cd.kamcoback.postgres.repository.MapSheetLearnDataRepository; import java.io.IOException; @@ -15,7 +14,6 @@ import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.Map; -import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.io.geojson.GeoJsonReader; import org.slf4j.Logger; import org.slf4j.LoggerFactory; @@ -39,9 +37,7 @@ public class LearningModelResultProcessor { private final ObjectMapper objectMapper = new ObjectMapper(); private final GeoJsonReader geoJsonReader = new GeoJsonReader(); - /** - * Process large learning model result files with optimized batch processing - */ + /** Process large learning model result files with optimized batch processing */ public int processLearningModelResultOptimized(Path geoJsonFilePath) { try { logger.info("Processing learning model result file (optimized): {}", geoJsonFilePath); @@ -62,7 +58,8 @@ public class LearningModelResultProcessor { // Extract metadata from file name and content String fileName = geoJsonFilePath.getFileName().toString(); - String mapSheetName = rootNode.has("name") ? rootNode.get("name").asText() : fileName.replace(".geojson", ""); + String mapSheetName = + rootNode.has("name") ? rootNode.get("name").asText() : fileName.replace(".geojson", ""); // Parse years from filename String[] parts = mapSheetName.split("_"); @@ -70,44 +67,58 @@ public class LearningModelResultProcessor { if (parts.length >= 4) { beforeYear = parts[1]; - afterYear = parts[2]; + afterYear = parts[2]; mapSheetNum = parts[3]; } if (beforeYear == null || afterYear == null || mapSheetNum == null) { - throw new IllegalArgumentException("Cannot parse years and map sheet number from filename: " + fileName); + throw new IllegalArgumentException( + "Cannot parse years and map sheet number from filename: " + fileName); } int totalFeatures = features.size(); logger.info("Total features to process: {}", totalFeatures); // Step 1: Create main data record first - MapSheetLearnDataEntity savedMainData = createMainDataRecord(geoJsonContent, fileName, geoJsonFilePath.toString(), beforeYear, afterYear, mapSheetNum); + MapSheetLearnDataEntity savedMainData = + createMainDataRecord( + geoJsonContent, + fileName, + geoJsonFilePath.toString(), + beforeYear, + afterYear, + mapSheetNum); // Step 2: Process features in small batches to avoid transaction timeout int totalProcessed = 0; int batchSize = 25; // Smaller batch size for reliability - + for (int i = 0; i < totalFeatures; i += batchSize) { int endIndex = Math.min(i + batchSize, totalFeatures); logger.info("Processing batch {}-{} of {}", i + 1, endIndex, totalFeatures); - + List batch = new ArrayList<>(); for (int j = i; j < endIndex; j++) { batch.add(features.get(j)); } - + try { - int processed = processBatchSafely(batch, savedMainData.getId(), beforeYear, afterYear, mapSheetNum); + int processed = + processBatchSafely(batch, savedMainData.getId(), beforeYear, afterYear, mapSheetNum); totalProcessed += processed; - logger.info("Batch processed successfully. Total so far: {}/{}", totalProcessed, totalFeatures); + logger.info( + "Batch processed successfully. Total so far: {}/{}", totalProcessed, totalFeatures); } catch (Exception e) { logger.error("Failed to process batch {}-{}: {}", i + 1, endIndex, e.getMessage()); // Continue with next batch instead of failing completely } } - logger.info("Successfully processed {} out of {} features from file: {}", totalProcessed, totalFeatures, fileName); + logger.info( + "Successfully processed {} out of {} features from file: {}", + totalProcessed, + totalFeatures, + fileName); return totalProcessed; } catch (IOException e) { @@ -120,32 +131,49 @@ public class LearningModelResultProcessor { } @Transactional - private MapSheetLearnDataEntity createMainDataRecord(String geoJsonContent, String fileName, String filePath, String beforeYear, String afterYear, String mapSheetNum) { - MapSheetLearnDataEntity mainData = createMainDataEntity(geoJsonContent, fileName, filePath, beforeYear, afterYear, mapSheetNum); + private MapSheetLearnDataEntity createMainDataRecord( + String geoJsonContent, + String fileName, + String filePath, + String beforeYear, + String afterYear, + String mapSheetNum) { + MapSheetLearnDataEntity mainData = + createMainDataEntity( + geoJsonContent, fileName, filePath, beforeYear, afterYear, mapSheetNum); MapSheetLearnDataEntity saved = mapSheetLearnDataRepository.save(mainData); logger.info("Created main data record with ID: {}", saved.getId()); return saved; } @Transactional - private int processBatchSafely(List features, Long dataUid, String beforeYear, String afterYear, String mapSheetNum) { + private int processBatchSafely( + List features, + Long dataUid, + String beforeYear, + String afterYear, + String mapSheetNum) { int processed = 0; - + for (JsonNode feature : features) { try { long geoUid = System.currentTimeMillis() + (long) (Math.random() * 10000); insertFeatureWithPostGIS(feature, geoUid, dataUid, beforeYear, afterYear, mapSheetNum); processed++; - + // Small delay to prevent ID collisions - try { Thread.sleep(1); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } - + try { + Thread.sleep(1); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } + } catch (Exception e) { logger.warn("Failed to process individual feature: {}", e.getMessage()); // Continue processing other features in this batch } } - + return processed; } @@ -214,32 +242,38 @@ public class LearningModelResultProcessor { for (int i = 0; i < features.size(); i += batchSize) { int endIndex = Math.min(i + batchSize, features.size()); logger.info("Processing batch {}-{} of {} features", i + 1, endIndex, features.size()); - + // Process each feature individually within this logging batch for (int j = i; j < endIndex; j++) { JsonNode feature = features.get(j); try { // Generate unique ID for this geometry entity long geoUid = System.currentTimeMillis() + (long) (Math.random() * 10000) + j; - + // Extract feature data and insert directly with PostGIS - insertFeatureWithPostGIS(feature, geoUid, savedMainData.getId(), beforeYear, afterYear, mapSheetNum); + insertFeatureWithPostGIS( + feature, geoUid, savedMainData.getId(), beforeYear, afterYear, mapSheetNum); featureCount++; - + // Small delay to prevent issues if (j % 5 == 0) { - try { Thread.sleep(10); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } + try { + Thread.sleep(10); + } catch (InterruptedException e) { + Thread.currentThread().interrupt(); + } } - + } catch (Exception e) { logger.warn("Failed to process feature {}: {}", j + 1, e.getMessage()); } } - + // Log progress after each batch if (featureCount > 0 && endIndex % batchSize == 0) { - logger.info("Processed {} features so far, success rate: {:.1f}%", - featureCount, (featureCount * 100.0) / endIndex); + logger.info( + "Processed {} features so far, success rate: {:.1f}%", + featureCount, (featureCount * 100.0) / endIndex); } } @@ -293,7 +327,12 @@ public class LearningModelResultProcessor { /** Insert GeoJSON feature directly using PostGIS functions */ private void insertFeatureWithPostGIS( - JsonNode feature, Long geoUid, Long dataUid, String beforeYear, String afterYear, String mapSheetNum) + JsonNode feature, + Long geoUid, + Long dataUid, + String beforeYear, + String afterYear, + String mapSheetNum) throws Exception { JsonNode properties = feature.get("properties"); @@ -306,12 +345,12 @@ public class LearningModelResultProcessor { // Extract properties Double cdProb = properties.has("cd_prob") ? properties.get("cd_prob").asDouble() : null; Double area = properties.has("area") ? properties.get("area").asDouble() : null; - + String classBeforeName = null; Double classBeforeProb = null; String classAfterName = null; Double classAfterProb = null; - + // Classification data JsonNode classNode = properties.get("class"); if (classNode != null) { @@ -342,18 +381,26 @@ public class LearningModelResultProcessor { // Get geometry type String geoType = geometry.has("type") ? geometry.get("type").asText() : "Unknown"; - + // Convert geometry to JSON string for PostGIS String geometryJson = geometry.toString(); - + // Insert using PostGIS functions mapSheetLearnDataGeomRepository.insertWithPostGISGeometry( - geoUid, cdProb, classBeforeName, classBeforeProb, - classAfterName, classAfterProb, Long.parseLong(mapSheetNum), - Integer.parseInt(beforeYear), Integer.parseInt(afterYear), - area, geometryJson, geoType, dataUid - ); - + geoUid, + cdProb, + classBeforeName, + classBeforeProb, + classAfterName, + classAfterProb, + Long.parseLong(mapSheetNum), + Integer.parseInt(beforeYear), + Integer.parseInt(afterYear), + area, + geometryJson, + geoType, + dataUid); + logger.debug("Inserted geometry entity with ID: {} using PostGIS", geoUid); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/ChangeDetectionCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/ChangeDetectionCoreService.java index 8452bd34..adc3fb27 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/ChangeDetectionCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/ChangeDetectionCoreService.java @@ -57,7 +57,8 @@ public class ChangeDetectionCoreService { public ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req) { ObjectMapper mapper = new ObjectMapper(); - ChangeDetectionDto.CogUrlDto resultDto = changeDetectionRepository.getChangeDetectionCogUrl(req); + ChangeDetectionDto.CogUrlDto resultDto = + changeDetectionRepository.getChangeDetectionCogUrl(req); try { JsonNode geomNode = mapper.readTree(resultDto.getBbox().toString()); diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ImageryEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ImageryEntity.java index d6f00bc1..e14a1048 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ImageryEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ImageryEntity.java @@ -1,21 +1,23 @@ package com.kamco.cd.kamcoback.postgres.entity; import jakarta.persistence.*; +import java.time.ZonedDateTime; +import java.util.UUID; import lombok.Getter; import lombok.Setter; import org.hibernate.annotations.ColumnDefault; -import java.time.ZonedDateTime; -import java.util.UUID; - @Getter @Setter @Table(name = "imagery") @Entity public class ImageryEntity { @Id - @GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "imagery_id_seq_gen") - @SequenceGenerator(name = "imagery_id_seq_gen",sequenceName = "imagery_id_seq",allocationSize = 1) + @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "imagery_id_seq_gen") + @SequenceGenerator( + name = "imagery_id_seq_gen", + sequenceName = "imagery_id_seq", + allocationSize = 1) @Column(name = "id", nullable = false) private Long id; diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx50kEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx50kEntity.java index 6522cdec..42db8c9c 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx50kEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx50kEntity.java @@ -13,9 +13,9 @@ public class MapInkx50kEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_inkx_50k_fid_seq_gen") @SequenceGenerator( - name = "tb_map_inkx_50k_fid_seq_gen", - sequenceName = "tb_map_inkx_50k_fid_seq", - allocationSize = 1) + name = "tb_map_inkx_50k_fid_seq_gen", + sequenceName = "tb_map_inkx_50k_fid_seq", + allocationSize = 1) private Integer fid; @Column(name = "mapidcd_no") @@ -29,5 +29,4 @@ public class MapInkx50kEntity { @Column(name = "geom") private Geometry geom; - } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx5kEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx5kEntity.java index ff253a32..4e16d734 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx5kEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx5kEntity.java @@ -13,9 +13,9 @@ public class MapInkx5kEntity { @Id @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_inkx_5k_fid_seq_gen") @SequenceGenerator( - name = "tb_map_inkx_5k_fid_seq_gen", - sequenceName = "tb_map_inkx_5k_fid_seq", - allocationSize = 1) + name = "tb_map_inkx_5k_fid_seq_gen", + sequenceName = "tb_map_inkx_5k_fid_seq", + allocationSize = 1) private Integer fid; @Column(name = "mapidcd_no") diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/MapSheetLearnDataGeomRepository.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/MapSheetLearnDataGeomRepository.java index 99c75820..582b74c0 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/MapSheetLearnDataGeomRepository.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/MapSheetLearnDataGeomRepository.java @@ -29,26 +29,26 @@ public interface MapSheetLearnDataGeomRepository /** 데이터 UID로 기존 지오메트리 데이터 삭제 (재생성 전에 사용) */ void deleteByDataUid(Long dataUid); - /** - * PostGIS 함수를 사용하여 geometry 데이터를 직접 삽입 - * ST_SetSRID(ST_GeomFromGeoJSON(...), 5186) 형식으로 저장 - */ + /** PostGIS 함수를 사용하여 geometry 데이터를 직접 삽입 ST_SetSRID(ST_GeomFromGeoJSON(...), 5186) 형식으로 저장 */ @Modifying @Transactional - @Query(value = """ + @Query( + value = + """ INSERT INTO tb_map_sheet_learn_data_geom ( - geo_uid, cd_prob, class_before_name, class_before_prob, - class_after_name, class_after_prob, map_sheet_num, - before_yyyy, after_yyyy, area, geom, geo_type, data_uid, + geo_uid, cd_prob, class_before_name, class_before_prob, + class_after_name, class_after_prob, map_sheet_num, + before_yyyy, after_yyyy, area, geom, geo_type, data_uid, created_dttm, updated_dttm ) VALUES ( :geoUid, :cdProb, :classBeforeName, :classBeforeProb, :classAfterName, :classAfterProb, :mapSheetNum, - :beforeYyyy, :afterYyyy, :area, - ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geometryJson AS TEXT)), 5186), + :beforeYyyy, :afterYyyy, :area, + ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geometryJson AS TEXT)), 5186), :geoType, :dataUid, NOW(), NOW() ) ON CONFLICT (geo_uid) DO NOTHING - """, nativeQuery = true) + """, + nativeQuery = true) void insertWithPostGISGeometry( @Param("geoUid") Long geoUid, @Param("cdProb") Double cdProb, @@ -62,6 +62,5 @@ public interface MapSheetLearnDataGeomRepository @Param("area") Double area, @Param("geometryJson") String geometryJson, @Param("geoType") String geoType, - @Param("dataUid") Long dataUid - ); + @Param("dataUid") Long dataUid); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryCustom.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryCustom.java index 5bb02b64..228fa7fc 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryCustom.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryCustom.java @@ -1,8 +1,6 @@ package com.kamco.cd.kamcoback.postgres.repository.changedetection; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto; -import com.querydsl.core.Tuple; - import java.util.List; public interface ChangeDetectionRepositoryCustom { diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryImpl.java index bbe0b6f4..6b40b85e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryImpl.java @@ -1,5 +1,12 @@ package com.kamco.cd.kamcoback.postgres.repository.changedetection; +import static com.kamco.cd.kamcoback.postgres.entity.QImageryEntity.imageryEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity.mapInkx5kEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataEntity.mapSheetAnalDataEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataGeomEntity.mapSheetAnalDataGeomEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalEntity.mapSheetAnalEntity; +import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalSttcEntity.mapSheetAnalSttcEntity; + import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; @@ -11,17 +18,9 @@ import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.StringExpression; import com.querydsl.jpa.impl.JPAQueryFactory; -import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; - import java.util.List; import java.util.Objects; - -import static com.kamco.cd.kamcoback.postgres.entity.QImageryEntity.imageryEntity; -import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity.mapInkx5kEntity; -import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataEntity.mapSheetAnalDataEntity; -import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataGeomEntity.mapSheetAnalDataGeomEntity; -import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalEntity.mapSheetAnalEntity; -import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalSttcEntity.mapSheetAnalSttcEntity; +import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport implements ChangeDetectionRepositoryCustom { @@ -51,74 +50,77 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport @Override public List getChangeDetectionClassCount(Long id) { return queryFactory - .select(Projections.constructor( - ChangeDetectionDto.CountDto.class, - mapSheetAnalSttcEntity.classAfterCd.toUpperCase(), - mapSheetAnalSttcEntity.id.classAfterName, - mapSheetAnalSttcEntity.classAfterCnt.sum() - )) - .from(mapSheetAnalEntity) - .innerJoin(mapSheetAnalDataEntity) - .on(mapSheetAnalDataEntity.analUid.eq(mapSheetAnalEntity.id)) - .innerJoin(mapSheetAnalSttcEntity) - .on(mapSheetAnalSttcEntity.dataUid.eq(mapSheetAnalDataEntity.id)) - .where(mapSheetAnalEntity.id.eq(id)) - .groupBy(mapSheetAnalSttcEntity.classAfterCd, mapSheetAnalSttcEntity.id.classAfterName) - .fetch(); + .select( + Projections.constructor( + ChangeDetectionDto.CountDto.class, + mapSheetAnalSttcEntity.classAfterCd.toUpperCase(), + mapSheetAnalSttcEntity.id.classAfterName, + mapSheetAnalSttcEntity.classAfterCnt.sum())) + .from(mapSheetAnalEntity) + .innerJoin(mapSheetAnalDataEntity) + .on(mapSheetAnalDataEntity.analUid.eq(mapSheetAnalEntity.id)) + .innerJoin(mapSheetAnalSttcEntity) + .on(mapSheetAnalSttcEntity.dataUid.eq(mapSheetAnalDataEntity.id)) + .where(mapSheetAnalEntity.id.eq(id)) + .groupBy(mapSheetAnalSttcEntity.classAfterCd, mapSheetAnalSttcEntity.id.classAfterName) + .fetch(); } @Override public ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req) { - Tuple result = queryFactory - .select( - makeCogUrl(req.getBeforeYear()).max().as("beforeCogUrl"), - makeCogUrl(req.getAfterYear()).max().as("afterCogUrl"), - Expressions.stringTemplate("ST_AsGeoJSON({0})", mapInkx5kEntity.geom).as("bbox") - ) - .from(imageryEntity) - .innerJoin(mapInkx5kEntity) - .on(imageryEntity.scene5k.eq(mapInkx5kEntity.mapidcdNo)) - .where( - imageryEntity.year.eq(req.getBeforeYear()).or(imageryEntity.year.eq(req.getAfterYear())), - imageryEntity.scene5k.eq(req.getMapSheetNum()) - ) - .groupBy(mapInkx5kEntity.geom) - .fetchOne(); + Tuple result = + queryFactory + .select( + makeCogUrl(req.getBeforeYear()).max().as("beforeCogUrl"), + makeCogUrl(req.getAfterYear()).max().as("afterCogUrl"), + Expressions.stringTemplate("ST_AsGeoJSON({0})", mapInkx5kEntity.geom).as("bbox")) + .from(imageryEntity) + .innerJoin(mapInkx5kEntity) + .on(imageryEntity.scene5k.eq(mapInkx5kEntity.mapidcdNo)) + .where( + imageryEntity + .year + .eq(req.getBeforeYear()) + .or(imageryEntity.year.eq(req.getAfterYear())), + imageryEntity.scene5k.eq(req.getMapSheetNum())) + .groupBy(mapInkx5kEntity.geom) + .fetchOne(); - //Polygon -> JsonNode 로 변환 - JsonNode geometryJson = changeGeometryJson(String.valueOf(Objects.requireNonNull(result).get(2, StringExpression.class))); + // Polygon -> JsonNode 로 변환 + JsonNode geometryJson = + changeGeometryJson( + String.valueOf(Objects.requireNonNull(result).get(2, StringExpression.class))); - return new ChangeDetectionDto.CogUrlDto(result.get(0, String.class), result.get(1, String.class), geometryJson); + return new ChangeDetectionDto.CogUrlDto( + result.get(0, String.class), result.get(1, String.class), geometryJson); } @Override public List getChangeDetectionYearList() { return queryFactory .select( - Projections.constructor( - ChangeDetectionDto.AnalYearList.class, - mapSheetAnalEntity.id, - mapSheetAnalEntity.analTitle, - mapSheetAnalEntity.compareYyyy.as("beforeYear"), - mapSheetAnalEntity.targetYyyy.as("afterYear"), - mapSheetAnalEntity.baseMapSheetNum - ) - ) + Projections.constructor( + ChangeDetectionDto.AnalYearList.class, + mapSheetAnalEntity.id, + mapSheetAnalEntity.analTitle, + mapSheetAnalEntity.compareYyyy.as("beforeYear"), + mapSheetAnalEntity.targetYyyy.as("afterYear"), + mapSheetAnalEntity.baseMapSheetNum)) .from(mapSheetAnalEntity) .orderBy(mapSheetAnalEntity.id.asc()) - .fetch() - ; + .fetch(); } private StringExpression makeCogUrl(Integer year) { return new CaseBuilder() - .when(imageryEntity.year.eq(year)) - .then(Expressions.stringTemplate("{0} || {1}",imageryEntity.cogMiddlePath, imageryEntity.cogFilename)) - .otherwise("") - ; + .when(imageryEntity.year.eq(year)) + .then( + Expressions.stringTemplate( + "{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename)) + .otherwise(""); } - private JsonNode changeGeometryJson(String geometry){ + private JsonNode changeGeometryJson(String geometry) { ObjectMapper mapper = new ObjectMapper(); try { return mapper.readTree(geometry); From fe08b6a5de28b6f0b8f2e865dffcde192f9a4888 Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Thu, 27 Nov 2025 18:28:27 +0900 Subject: [PATCH 2/2] =?UTF-8?q?Geometry=20=EB=B3=80=ED=99=98=20hibernate-s?= =?UTF-8?q?patial=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 2 +- .../dto/ChangeDetectionDto.java | 3 +- .../core/ChangeDetectionCoreService.java | 10 ---- .../ChangeDetectionRepositoryImpl.java | 53 ++++++------------- 4 files changed, 18 insertions(+), 50 deletions(-) diff --git a/build.gradle b/build.gradle index 10b5c9a1..1d3e9ce0 100644 --- a/build.gradle +++ b/build.gradle @@ -39,7 +39,7 @@ dependencies { implementation 'com.fasterxml.jackson.core:jackson-databind' implementation 'org.locationtech.jts.io:jts-io-common:1.20.0' implementation 'org.locationtech.jts:jts-core:1.19.0' - //implementation 'org.hibernate:hibernate-spatial:6.2.7.Final' + implementation 'org.hibernate:hibernate-spatial:6.2.7.Final' // QueryDSL JPA implementation 'com.querydsl:querydsl-jpa:5.0.0:jakarta' diff --git a/src/main/java/com/kamco/cd/kamcoback/changedetection/dto/ChangeDetectionDto.java b/src/main/java/com/kamco/cd/kamcoback/changedetection/dto/ChangeDetectionDto.java index 4d237536..fa71e98b 100644 --- a/src/main/java/com/kamco/cd/kamcoback/changedetection/dto/ChangeDetectionDto.java +++ b/src/main/java/com/kamco/cd/kamcoback/changedetection/dto/ChangeDetectionDto.java @@ -1,6 +1,5 @@ package com.kamco.cd.kamcoback.changedetection.dto; -import com.fasterxml.jackson.databind.JsonNode; import io.swagger.v3.oas.annotations.media.Schema; import lombok.AllArgsConstructor; import lombok.Getter; @@ -53,7 +52,7 @@ public class ChangeDetectionDto { public static class CogUrlDto { private String beforeCogUrl; private String afterCogUrl; - private JsonNode bbox; + private Geometry bbox; } @Schema(name = "AnalYearList", description = "년도(차수) 목록") diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/core/ChangeDetectionCoreService.java b/src/main/java/com/kamco/cd/kamcoback/postgres/core/ChangeDetectionCoreService.java index adc3fb27..56290b7a 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/core/ChangeDetectionCoreService.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/core/ChangeDetectionCoreService.java @@ -56,16 +56,6 @@ public class ChangeDetectionCoreService { } public ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req) { - ObjectMapper mapper = new ObjectMapper(); - ChangeDetectionDto.CogUrlDto resultDto = - changeDetectionRepository.getChangeDetectionCogUrl(req); - - try { - JsonNode geomNode = mapper.readTree(resultDto.getBbox().toString()); - resultDto.setBbox(geomNode); - } catch (Exception e) { - throw new RuntimeException("Failed to parse geom JSON", e); - } return changeDetectionRepository.getChangeDetectionCogUrl(req); } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryImpl.java b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryImpl.java index 6b40b85e..ce53dfe7 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryImpl.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/repository/changedetection/ChangeDetectionRepositoryImpl.java @@ -7,19 +7,14 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataGeomEntity import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalEntity.mapSheetAnalEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalSttcEntity.mapSheetAnalSttcEntity; -import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonNode; -import com.fasterxml.jackson.databind.ObjectMapper; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto; import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataGeomEntity; -import com.querydsl.core.Tuple; import com.querydsl.core.types.Projections; import com.querydsl.core.types.dsl.CaseBuilder; import com.querydsl.core.types.dsl.Expressions; import com.querydsl.core.types.dsl.StringExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.List; -import java.util.Objects; import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport; public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport @@ -68,31 +63,24 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport @Override public ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req) { - Tuple result = - queryFactory - .select( + return queryFactory + .select( + Projections.constructor( + ChangeDetectionDto.CogUrlDto.class, makeCogUrl(req.getBeforeYear()).max().as("beforeCogUrl"), makeCogUrl(req.getAfterYear()).max().as("afterCogUrl"), - Expressions.stringTemplate("ST_AsGeoJSON({0})", mapInkx5kEntity.geom).as("bbox")) - .from(imageryEntity) - .innerJoin(mapInkx5kEntity) - .on(imageryEntity.scene5k.eq(mapInkx5kEntity.mapidcdNo)) - .where( - imageryEntity - .year - .eq(req.getBeforeYear()) - .or(imageryEntity.year.eq(req.getAfterYear())), - imageryEntity.scene5k.eq(req.getMapSheetNum())) - .groupBy(mapInkx5kEntity.geom) - .fetchOne(); - - // Polygon -> JsonNode 로 변환 - JsonNode geometryJson = - changeGeometryJson( - String.valueOf(Objects.requireNonNull(result).get(2, StringExpression.class))); - - return new ChangeDetectionDto.CogUrlDto( - result.get(0, String.class), result.get(1, String.class), geometryJson); + mapInkx5kEntity.geom.as("bbox"))) + .from(imageryEntity) + .innerJoin(mapInkx5kEntity) + .on(imageryEntity.scene5k.eq(mapInkx5kEntity.mapidcdNo)) + .where( + imageryEntity + .year + .eq(req.getBeforeYear()) + .or(imageryEntity.year.eq(req.getAfterYear())), + imageryEntity.scene5k.eq(req.getMapSheetNum())) + .groupBy(mapInkx5kEntity.geom) + .fetchOne(); } @Override @@ -119,13 +107,4 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport "{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename)) .otherwise(""); } - - private JsonNode changeGeometryJson(String geometry) { - ObjectMapper mapper = new ObjectMapper(); - try { - return mapper.readTree(geometry); - } catch (JsonProcessingException e) { - throw new RuntimeException(e); - } - } }