From 675d2f2ed156d1d86f1b677eee73a80ef38088d2 Mon Sep 17 00:00:00 2001 From: "gayoun.park" Date: Thu, 27 Nov 2025 18:01:15 +0900 Subject: [PATCH] =?UTF-8?q?=EB=B3=80=ED=99=94=ED=83=90=EC=A7=80=20API=20?= =?UTF-8?q?=EC=BB=A4=EB=B0=8B=20cog,count,yearlist?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../ChangeDetectionApiController.java | 31 +++++- .../dto/ChangeDetectionDto.java | 57 ++++++++-- .../service/ChangeDetectionService.java | 12 +++ .../core/ChangeDetectionCoreService.java | 21 ++++ .../postgres/entity/ImageryEntity.java | 55 ++++++++++ .../postgres/entity/MapInkx50kEntity.java | 33 ++++++ .../postgres/entity/MapInkx5kEntity.java | 32 ++++++ .../postgres/entity/MapSheetAnalEntity.java | 3 + .../entity/MapSheetAnalSttcEntity.java | 6 ++ .../ChangeDetectionRepositoryCustom.java | 9 ++ .../ChangeDetectionRepositoryImpl.java | 100 +++++++++++++++++- 11 files changed, 343 insertions(+), 16 deletions(-) create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/entity/ImageryEntity.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx50kEntity.java create mode 100644 src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx5kEntity.java 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 5e387fcf..9b18e568 100644 --- a/src/main/java/com/kamco/cd/kamcoback/changedetection/ChangeDetectionApiController.java +++ b/src/main/java/com/kamco/cd/kamcoback/changedetection/ChangeDetectionApiController.java @@ -5,13 +5,14 @@ import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto; import com.kamco.cd.kamcoback.changedetection.service.ChangeDetectionService; import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import io.swagger.v3.oas.annotations.Hidden; +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 lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.bind.annotation.*; +import java.time.LocalDate; import java.util.List; @Tag(name = "변화탐지", description = "변화탐지 API") @@ -34,8 +35,32 @@ public class ChangeDetectionApiController { * PolygonData -> JsonNode 변환 예제 * @return */ + @Hidden @GetMapping("/json-data") 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){ + 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); + return ApiResponseDto.ok(changeDetectionService.getChangeDetectionCogUrl(req)); + } + + @Operation(summary = "변화탐지 년도(차수) 목록", description = "변화탐지 년도(차수) 목록") + @GetMapping("/year-list") + 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 cb3c971e..59e5f6a4 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,5 +1,6 @@ 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; @@ -21,6 +22,52 @@ public class ChangeDetectionDto{ private Double centroidY; } + @Schema(name = "CountDto", description = "분류별 탐지 건수") + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class CountDto { + private String classCd; //영문코드 + private String className; //한글명 + private Long count; //건수 + } + + @Schema(name = "CogUrlReq", description = "COG Url Req") + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class CogUrlReq { + private Integer beforeYear; + private Integer afterYear; + private String mapSheetNum; + } + + @Schema(name = "CogUrlDto", description = "COG Url 정보") + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class CogUrlDto { + private String beforeCogUrl; + private String afterCogUrl; + private JsonNode bbox; + } + + @Schema(name = "AnalYearList", description = "년도(차수) 목록") + @Getter + @Setter + @NoArgsConstructor + @AllArgsConstructor + public static class AnalYearList { + private Long analUid; + private String analTitle; + private Integer beforeYear; + private Integer afterYear; + private String baseMapSheetNum; + } + @Schema(name = "PolygonGeometry", description = "폴리곤 리턴 객체") @Getter @Setter @@ -47,16 +94,6 @@ public class ChangeDetectionDto{ private Double center_longitude; //폴리곤 중심 경도 } - @Schema(name = "CogURL", description = "COG URL") - @Getter - @Setter - @NoArgsConstructor - @AllArgsConstructor - public static class CogURL{ - private String before_cog_url; //기준 COG URL - private String after_cog_url; //비교 COG URL - } - @Schema(name = "PolygonProperties", description = "폴리곤 정보") @Getter @Setter diff --git a/src/main/java/com/kamco/cd/kamcoback/changedetection/service/ChangeDetectionService.java b/src/main/java/com/kamco/cd/kamcoback/changedetection/service/ChangeDetectionService.java index 48a365dd..191b9ffe 100644 --- a/src/main/java/com/kamco/cd/kamcoback/changedetection/service/ChangeDetectionService.java +++ b/src/main/java/com/kamco/cd/kamcoback/changedetection/service/ChangeDetectionService.java @@ -20,4 +20,16 @@ public class ChangeDetectionService { public List getPolygonToJson(){ return changeDetectionCoreService.getPolygonToJson(); } + + public List getChangeDetectionClassCount(Long id) { + return changeDetectionCoreService.getChangeDetectionClassCount(id); + } + + public ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req) { + return changeDetectionCoreService.getChangeDetectionCogUrl(req); + } + + public List getChangeDetectionYearList() { + return changeDetectionCoreService.getChangeDetectionYearList(); + } } 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 491137b7..05972759 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 @@ -49,4 +49,25 @@ public class ChangeDetectionCoreService { }) .collect(Collectors.toList()); } + + public List getChangeDetectionClassCount(Long id) { + return changeDetectionRepository.getChangeDetectionClassCount(id); + } + + 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); + } + + public List getChangeDetectionYearList() { + return changeDetectionRepository.getChangeDetectionYearList(); + } } 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 new file mode 100644 index 00000000..d6f00bc1 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/ImageryEntity.java @@ -0,0 +1,55 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import jakarta.persistence.*; +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) + @Column(name = "id", nullable = false) + private Long id; + + @Column(name = "uuid", columnDefinition = "uuid default gen_random_uuid()") + private UUID uuid; + + @Column(name = "year") + private Integer year; + + @Column(name = "scene_50k") + private String scene50k; + + @Column(name = "scene_5k") + private String scene5k; + + @Column(name = "scene_id_50k") + private Integer sceneId50k; + + @Column(name = "scene_id_5k") + private Integer sceneId5k; + + @ColumnDefault("now()") + @Column(name = "created_date", columnDefinition = "TIMESTAMP WITH TIME ZONE DEFAULT now()") + private ZonedDateTime createdDate; + + @Column(name = "middle_path") + private String middlePath; + + @Column(name = "cog_middle_path") + private String cogMiddlePath; + + @Column(name = "filename") + private String filename; + + @Column(name = "cog_filename") + private String cogFilename; +} 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 new file mode 100644 index 00000000..6522cdec --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx50kEntity.java @@ -0,0 +1,33 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import org.locationtech.jts.geom.Geometry; + +@Getter +@Setter +@Table(name = "tb_map_inkx_50k") +@Entity +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) + private Integer fid; + + @Column(name = "mapidcd_no") + private String mapidcdNo; + + @Column(name = "mapid_nm") + private String mapidNm; + + @Column(name = "mapid_no") + private String mapidNo; + + @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 new file mode 100644 index 00000000..ff253a32 --- /dev/null +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapInkx5kEntity.java @@ -0,0 +1,32 @@ +package com.kamco.cd.kamcoback.postgres.entity; + +import jakarta.persistence.*; +import lombok.Getter; +import lombok.Setter; +import org.locationtech.jts.geom.Geometry; + +@Getter +@Setter +@Table(name = "tb_map_inkx_5k") +@Entity +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) + private Integer fid; + + @Column(name = "mapidcd_no") + private String mapidcdNo; + + @Column(name = "mapid_nm") + private String mapidNm; + + @Column(name = "geom") + private Geometry geom; + + @Column(name = "fid_k50") + private Long fidK50; +} diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalEntity.java index a507473c..440af411 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalEntity.java @@ -91,4 +91,7 @@ public class MapSheetAnalEntity { @Column(name = "detecting_cnt") private Long detectingCnt; + + @Column(name = "base_map_sheet_num") + private String baseMapSheetNum; } diff --git a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalSttcEntity.java b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalSttcEntity.java index 8e49e35a..e2d2c07e 100644 --- a/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalSttcEntity.java +++ b/src/main/java/com/kamco/cd/kamcoback/postgres/entity/MapSheetAnalSttcEntity.java @@ -47,6 +47,12 @@ public class MapSheetAnalSttcEntity { @Column(name = "data_uid", nullable = false) private Long dataUid; + @Column(name = "class_before_cd") + private String classBeforeCd; + + @Column(name = "class_after_cd") + private String classAfterCd; + public InferenceResultDto.Dashboard toDto() { return new InferenceResultDto.Dashboard( id.getCompareYyyy(), 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 db1375cd..5bb02b64 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,5 +1,8 @@ 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 { @@ -7,4 +10,10 @@ public interface ChangeDetectionRepositoryCustom { String getPolygonToPoint(); List findPolygonJson(); + + List getChangeDetectionClassCount(Long id); + + ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req); + + List getChangeDetectionYearList(); } 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 4ec6e2f8..0a062a6f 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,13 +1,28 @@ package com.kamco.cd.kamcoback.postgres.repository.changedetection; -import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataGeomEntity.mapSheetAnalDataGeomEntity; - +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 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; + public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport implements ChangeDetectionRepositoryCustom { @@ -34,4 +49,83 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport .orderBy(mapSheetAnalDataGeomEntity.id.desc()) .fetch(); } + + @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(); + } + + @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(); + + //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); + } + + @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 + ) + ) + .from(mapSheetAnalEntity) + .orderBy(mapSheetAnalEntity.id.asc()) + .fetch() + ; + } + + private StringExpression makeCogUrl(Integer year) { + return new CaseBuilder() + .when(imageryEntity.year.eq(year)) + .then(Expressions.stringTemplate("{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); + } + } }