코드 컨벤션 적용

This commit is contained in:
2025-11-28 09:39:11 +09:00
parent 8bab44d7e4
commit 4c1083067e
10 changed files with 211 additions and 161 deletions

View File

@@ -9,12 +9,10 @@ import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag; import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.transaction.Transactional; import jakarta.transaction.Transactional;
import java.util.List;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*; import org.springframework.web.bind.annotation.*;
import java.time.LocalDate;
import java.util.List;
@Tag(name = "변화탐지", description = "변화탐지 API") @Tag(name = "변화탐지", description = "변화탐지 API")
@RequiredArgsConstructor @RequiredArgsConstructor
@RestController @RestController
@@ -33,34 +31,37 @@ public class ChangeDetectionApiController {
/** /**
* PolygonData -> JsonNode 변환 예제 * PolygonData -> JsonNode 변환 예제
*
* @return * @return
*/ */
@Hidden @Hidden
@GetMapping("/json-data") @GetMapping("/json-data")
public ApiResponseDto<List<JsonNode>> getPolygonToJson(){ public ApiResponseDto<List<JsonNode>> getPolygonToJson() {
return ApiResponseDto.ok(changeDetectionService.getPolygonToJson()); return ApiResponseDto.ok(changeDetectionService.getPolygonToJson());
} }
@Operation(summary = "변화탐지 분류별 건수", description = "변화탐지 분류별 건수") @Operation(summary = "변화탐지 분류별 건수", description = "변화탐지 분류별 건수")
@GetMapping("/class-count/{id}") @GetMapping("/class-count/{id}")
public ApiResponseDto<List<ChangeDetectionDto.CountDto>> getChangeDetectionClassCount( public ApiResponseDto<List<ChangeDetectionDto.CountDto>> 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)); return ApiResponseDto.ok(changeDetectionService.getChangeDetectionClassCount(id));
} }
@Operation(summary = "변화탐지 COG Url", description = "변화탐지 COG Url") @Operation(summary = "변화탐지 COG Url", description = "변화탐지 COG Url")
@GetMapping("/cog-url") @GetMapping("/cog-url")
public ApiResponseDto<ChangeDetectionDto.CogUrlDto> getChangeDetectionCogUrl( public ApiResponseDto<ChangeDetectionDto.CogUrlDto> getChangeDetectionCogUrl(
@Parameter(description = "이전 년도", example = "2023") @RequestParam Integer beforeYear, @Parameter(description = "이전 년도", example = "2023") @RequestParam Integer beforeYear,
@Parameter(description = "이후 년도", example = "2024") @RequestParam Integer afterYear, @Parameter(description = "이후 년도", example = "2024") @RequestParam Integer afterYear,
@Parameter(description = "도엽번호(5k)", example = "36809010") @RequestParam String mapSheetNum){ @Parameter(description = "도엽번호(5k)", example = "36809010") @RequestParam String mapSheetNum) {
ChangeDetectionDto.CogUrlReq req = new ChangeDetectionDto.CogUrlReq(beforeYear, afterYear, mapSheetNum); ChangeDetectionDto.CogUrlReq req =
new ChangeDetectionDto.CogUrlReq(beforeYear, afterYear, mapSheetNum);
return ApiResponseDto.ok(changeDetectionService.getChangeDetectionCogUrl(req)); return ApiResponseDto.ok(changeDetectionService.getChangeDetectionCogUrl(req));
} }
@Operation(summary = "변화탐지 년도(차수) 목록", description = "변화탐지 년도(차수) 목록") @Operation(summary = "변화탐지 년도(차수) 목록", description = "변화탐지 년도(차수) 목록")
@GetMapping("/year-list") @GetMapping("/year-list")
public ApiResponseDto<List<ChangeDetectionDto.AnalYearList>> getChangeDetectionYearList(){ public ApiResponseDto<List<ChangeDetectionDto.AnalYearList>> getChangeDetectionYearList() {
return ApiResponseDto.ok(changeDetectionService.getChangeDetectionYearList()); return ApiResponseDto.ok(changeDetectionService.getChangeDetectionYearList());
} }
} }

View File

@@ -8,7 +8,7 @@ import lombok.NoArgsConstructor;
import lombok.Setter; import lombok.Setter;
import org.locationtech.jts.geom.Geometry; import org.locationtech.jts.geom.Geometry;
public class ChangeDetectionDto{ public class ChangeDetectionDto {
@Schema(name = "TestDto", description = "테스트용") @Schema(name = "TestDto", description = "테스트용")
@Getter @Getter
@@ -18,7 +18,8 @@ public class ChangeDetectionDto{
public static class TestDto { public static class TestDto {
private Long id; private Long id;
private Geometry polygon; private Geometry polygon;
private Double centroidX;; private Double centroidX;
;
private Double centroidY; private Double centroidY;
} }
@@ -28,9 +29,9 @@ public class ChangeDetectionDto{
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class CountDto { public static class CountDto {
private String classCd; //영문코드 private String classCd; // 영문코드
private String className; //한글명 private String className; // 한글명
private Long count; //건수 private Long count; // 건수
} }
@Schema(name = "CogUrlReq", description = "COG Url Req") @Schema(name = "CogUrlReq", description = "COG Url Req")
@@ -73,12 +74,12 @@ public class ChangeDetectionDto{
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class PointGeometry{ public static class PointGeometry {
private Long geoUid; private Long geoUid;
private String type; // "Point" private String type; // "Point"
private Geometry coordinates; //Point 값 private Geometry coordinates; // Point 값
private String before_class; //기준 분류 private String before_class; // 기준 분류
private String after_class; //비교 분류 private String after_class; // 비교 분류
} }
@Schema(name = "PolygonGeometry", description = "폴리곤 리턴 객체") @Schema(name = "PolygonGeometry", description = "폴리곤 리턴 객체")
@@ -86,12 +87,12 @@ public class ChangeDetectionDto{
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class PolygonGeometry{ public static class PolygonGeometry {
private Long geoUid; private Long geoUid;
private String type; // "MultiPolygon" private String type; // "MultiPolygon"
private Geometry coordinates; //Polygon 값 private Geometry coordinates; // Polygon 값
private Double center_latitude; //폴리곤 중심 위도 private Double center_latitude; // 폴리곤 중심 위도
private Double center_longitude; //폴리곤 중심 경도 private Double center_longitude; // 폴리곤 중심 경도
} }
@Schema(name = "PolygonProperties", description = "폴리곤 정보") @Schema(name = "PolygonProperties", description = "폴리곤 정보")
@@ -99,13 +100,13 @@ public class ChangeDetectionDto{
@Setter @Setter
@NoArgsConstructor @NoArgsConstructor
@AllArgsConstructor @AllArgsConstructor
public static class PolygonProperties{ public static class PolygonProperties {
private Double area; //면적 private Double area; // 면적
private String before_year; //기준년도 private String before_year; // 기준년도
private Double before_confidence; //기준 신뢰도(확률) private Double before_confidence; // 기준 신뢰도(확률)
private String before_class; //기준 분류 private String before_class; // 기준 분류
private String after_year; //비교년도 private String after_year; // 비교년도
private Double after_confidence; //비교 신뢰도(확률) private Double after_confidence; // 비교 신뢰도(확률)
private String after_class; //비교 분류 private String after_class; // 비교 분류
} }
} }

View File

@@ -3,7 +3,6 @@ package com.kamco.cd.kamcoback.inference.service;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.databind.ObjectMapper;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetLearnDataEntity; 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.MapSheetLearnDataGeomRepository;
import com.kamco.cd.kamcoback.postgres.repository.MapSheetLearnDataRepository; import com.kamco.cd.kamcoback.postgres.repository.MapSheetLearnDataRepository;
import java.io.IOException; import java.io.IOException;
@@ -15,7 +14,6 @@ import java.util.ArrayList;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import org.locationtech.jts.geom.Geometry;
import org.locationtech.jts.io.geojson.GeoJsonReader; import org.locationtech.jts.io.geojson.GeoJsonReader;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -39,9 +37,7 @@ public class LearningModelResultProcessor {
private final ObjectMapper objectMapper = new ObjectMapper(); private final ObjectMapper objectMapper = new ObjectMapper();
private final GeoJsonReader geoJsonReader = new GeoJsonReader(); 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) { public int processLearningModelResultOptimized(Path geoJsonFilePath) {
try { try {
logger.info("Processing learning model result file (optimized): {}", geoJsonFilePath); logger.info("Processing learning model result file (optimized): {}", geoJsonFilePath);
@@ -62,7 +58,8 @@ public class LearningModelResultProcessor {
// Extract metadata from file name and content // Extract metadata from file name and content
String fileName = geoJsonFilePath.getFileName().toString(); 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 // Parse years from filename
String[] parts = mapSheetName.split("_"); String[] parts = mapSheetName.split("_");
@@ -70,44 +67,58 @@ public class LearningModelResultProcessor {
if (parts.length >= 4) { if (parts.length >= 4) {
beforeYear = parts[1]; beforeYear = parts[1];
afterYear = parts[2]; afterYear = parts[2];
mapSheetNum = parts[3]; mapSheetNum = parts[3];
} }
if (beforeYear == null || afterYear == null || mapSheetNum == null) { 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(); int totalFeatures = features.size();
logger.info("Total features to process: {}", totalFeatures); logger.info("Total features to process: {}", totalFeatures);
// Step 1: Create main data record first // 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 // Step 2: Process features in small batches to avoid transaction timeout
int totalProcessed = 0; int totalProcessed = 0;
int batchSize = 25; // Smaller batch size for reliability int batchSize = 25; // Smaller batch size for reliability
for (int i = 0; i < totalFeatures; i += batchSize) { for (int i = 0; i < totalFeatures; i += batchSize) {
int endIndex = Math.min(i + batchSize, totalFeatures); int endIndex = Math.min(i + batchSize, totalFeatures);
logger.info("Processing batch {}-{} of {}", i + 1, endIndex, totalFeatures); logger.info("Processing batch {}-{} of {}", i + 1, endIndex, totalFeatures);
List<JsonNode> batch = new ArrayList<>(); List<JsonNode> batch = new ArrayList<>();
for (int j = i; j < endIndex; j++) { for (int j = i; j < endIndex; j++) {
batch.add(features.get(j)); batch.add(features.get(j));
} }
try { try {
int processed = processBatchSafely(batch, savedMainData.getId(), beforeYear, afterYear, mapSheetNum); int processed =
processBatchSafely(batch, savedMainData.getId(), beforeYear, afterYear, mapSheetNum);
totalProcessed += processed; 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) { } catch (Exception e) {
logger.error("Failed to process batch {}-{}: {}", i + 1, endIndex, e.getMessage()); logger.error("Failed to process batch {}-{}: {}", i + 1, endIndex, e.getMessage());
// Continue with next batch instead of failing completely // 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; return totalProcessed;
} catch (IOException e) { } catch (IOException e) {
@@ -120,32 +131,49 @@ public class LearningModelResultProcessor {
} }
@Transactional @Transactional
private MapSheetLearnDataEntity createMainDataRecord(String geoJsonContent, String fileName, String filePath, String beforeYear, String afterYear, String mapSheetNum) { private MapSheetLearnDataEntity createMainDataRecord(
MapSheetLearnDataEntity mainData = createMainDataEntity(geoJsonContent, fileName, filePath, beforeYear, afterYear, mapSheetNum); 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); MapSheetLearnDataEntity saved = mapSheetLearnDataRepository.save(mainData);
logger.info("Created main data record with ID: {}", saved.getId()); logger.info("Created main data record with ID: {}", saved.getId());
return saved; return saved;
} }
@Transactional @Transactional
private int processBatchSafely(List<JsonNode> features, Long dataUid, String beforeYear, String afterYear, String mapSheetNum) { private int processBatchSafely(
List<JsonNode> features,
Long dataUid,
String beforeYear,
String afterYear,
String mapSheetNum) {
int processed = 0; int processed = 0;
for (JsonNode feature : features) { for (JsonNode feature : features) {
try { try {
long geoUid = System.currentTimeMillis() + (long) (Math.random() * 10000); long geoUid = System.currentTimeMillis() + (long) (Math.random() * 10000);
insertFeatureWithPostGIS(feature, geoUid, dataUid, beforeYear, afterYear, mapSheetNum); insertFeatureWithPostGIS(feature, geoUid, dataUid, beforeYear, afterYear, mapSheetNum);
processed++; processed++;
// Small delay to prevent ID collisions // 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) { } catch (Exception e) {
logger.warn("Failed to process individual feature: {}", e.getMessage()); logger.warn("Failed to process individual feature: {}", e.getMessage());
// Continue processing other features in this batch // Continue processing other features in this batch
} }
} }
return processed; return processed;
} }
@@ -214,32 +242,38 @@ public class LearningModelResultProcessor {
for (int i = 0; i < features.size(); i += batchSize) { for (int i = 0; i < features.size(); i += batchSize) {
int endIndex = Math.min(i + batchSize, features.size()); int endIndex = Math.min(i + batchSize, features.size());
logger.info("Processing batch {}-{} of {} features", i + 1, endIndex, features.size()); logger.info("Processing batch {}-{} of {} features", i + 1, endIndex, features.size());
// Process each feature individually within this logging batch // Process each feature individually within this logging batch
for (int j = i; j < endIndex; j++) { for (int j = i; j < endIndex; j++) {
JsonNode feature = features.get(j); JsonNode feature = features.get(j);
try { try {
// Generate unique ID for this geometry entity // Generate unique ID for this geometry entity
long geoUid = System.currentTimeMillis() + (long) (Math.random() * 10000) + j; long geoUid = System.currentTimeMillis() + (long) (Math.random() * 10000) + j;
// Extract feature data and insert directly with PostGIS // 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++; featureCount++;
// Small delay to prevent issues // Small delay to prevent issues
if (j % 5 == 0) { 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) { } catch (Exception e) {
logger.warn("Failed to process feature {}: {}", j + 1, e.getMessage()); logger.warn("Failed to process feature {}: {}", j + 1, e.getMessage());
} }
} }
// Log progress after each batch // Log progress after each batch
if (featureCount > 0 && endIndex % batchSize == 0) { if (featureCount > 0 && endIndex % batchSize == 0) {
logger.info("Processed {} features so far, success rate: {:.1f}%", logger.info(
featureCount, (featureCount * 100.0) / endIndex); "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 */ /** Insert GeoJSON feature directly using PostGIS functions */
private void insertFeatureWithPostGIS( 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 { throws Exception {
JsonNode properties = feature.get("properties"); JsonNode properties = feature.get("properties");
@@ -306,12 +345,12 @@ public class LearningModelResultProcessor {
// Extract properties // Extract properties
Double cdProb = properties.has("cd_prob") ? properties.get("cd_prob").asDouble() : null; Double cdProb = properties.has("cd_prob") ? properties.get("cd_prob").asDouble() : null;
Double area = properties.has("area") ? properties.get("area").asDouble() : null; Double area = properties.has("area") ? properties.get("area").asDouble() : null;
String classBeforeName = null; String classBeforeName = null;
Double classBeforeProb = null; Double classBeforeProb = null;
String classAfterName = null; String classAfterName = null;
Double classAfterProb = null; Double classAfterProb = null;
// Classification data // Classification data
JsonNode classNode = properties.get("class"); JsonNode classNode = properties.get("class");
if (classNode != null) { if (classNode != null) {
@@ -342,18 +381,26 @@ public class LearningModelResultProcessor {
// Get geometry type // Get geometry type
String geoType = geometry.has("type") ? geometry.get("type").asText() : "Unknown"; String geoType = geometry.has("type") ? geometry.get("type").asText() : "Unknown";
// Convert geometry to JSON string for PostGIS // Convert geometry to JSON string for PostGIS
String geometryJson = geometry.toString(); String geometryJson = geometry.toString();
// Insert using PostGIS functions // Insert using PostGIS functions
mapSheetLearnDataGeomRepository.insertWithPostGISGeometry( mapSheetLearnDataGeomRepository.insertWithPostGISGeometry(
geoUid, cdProb, classBeforeName, classBeforeProb, geoUid,
classAfterName, classAfterProb, Long.parseLong(mapSheetNum), cdProb,
Integer.parseInt(beforeYear), Integer.parseInt(afterYear), classBeforeName,
area, geometryJson, geoType, dataUid 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); logger.debug("Inserted geometry entity with ID: {} using PostGIS", geoUid);
} }

View File

@@ -57,7 +57,8 @@ public class ChangeDetectionCoreService {
public ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req) { public ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req) {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
ChangeDetectionDto.CogUrlDto resultDto = changeDetectionRepository.getChangeDetectionCogUrl(req); ChangeDetectionDto.CogUrlDto resultDto =
changeDetectionRepository.getChangeDetectionCogUrl(req);
try { try {
JsonNode geomNode = mapper.readTree(resultDto.getBbox().toString()); JsonNode geomNode = mapper.readTree(resultDto.getBbox().toString());

View File

@@ -1,21 +1,23 @@
package com.kamco.cd.kamcoback.postgres.entity; package com.kamco.cd.kamcoback.postgres.entity;
import jakarta.persistence.*; import jakarta.persistence.*;
import java.time.ZonedDateTime;
import java.util.UUID;
import lombok.Getter; import lombok.Getter;
import lombok.Setter; import lombok.Setter;
import org.hibernate.annotations.ColumnDefault; import org.hibernate.annotations.ColumnDefault;
import java.time.ZonedDateTime;
import java.util.UUID;
@Getter @Getter
@Setter @Setter
@Table(name = "imagery") @Table(name = "imagery")
@Entity @Entity
public class ImageryEntity { public class ImageryEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.SEQUENCE,generator = "imagery_id_seq_gen") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "imagery_id_seq_gen")
@SequenceGenerator(name = "imagery_id_seq_gen",sequenceName = "imagery_id_seq",allocationSize = 1) @SequenceGenerator(
name = "imagery_id_seq_gen",
sequenceName = "imagery_id_seq",
allocationSize = 1)
@Column(name = "id", nullable = false) @Column(name = "id", nullable = false)
private Long id; private Long id;

View File

@@ -13,9 +13,9 @@ public class MapInkx50kEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_inkx_50k_fid_seq_gen") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_inkx_50k_fid_seq_gen")
@SequenceGenerator( @SequenceGenerator(
name = "tb_map_inkx_50k_fid_seq_gen", name = "tb_map_inkx_50k_fid_seq_gen",
sequenceName = "tb_map_inkx_50k_fid_seq", sequenceName = "tb_map_inkx_50k_fid_seq",
allocationSize = 1) allocationSize = 1)
private Integer fid; private Integer fid;
@Column(name = "mapidcd_no") @Column(name = "mapidcd_no")
@@ -29,5 +29,4 @@ public class MapInkx50kEntity {
@Column(name = "geom") @Column(name = "geom")
private Geometry geom; private Geometry geom;
} }

View File

@@ -13,9 +13,9 @@ public class MapInkx5kEntity {
@Id @Id
@GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_inkx_5k_fid_seq_gen") @GeneratedValue(strategy = GenerationType.SEQUENCE, generator = "tb_map_inkx_5k_fid_seq_gen")
@SequenceGenerator( @SequenceGenerator(
name = "tb_map_inkx_5k_fid_seq_gen", name = "tb_map_inkx_5k_fid_seq_gen",
sequenceName = "tb_map_inkx_5k_fid_seq", sequenceName = "tb_map_inkx_5k_fid_seq",
allocationSize = 1) allocationSize = 1)
private Integer fid; private Integer fid;
@Column(name = "mapidcd_no") @Column(name = "mapidcd_no")

View File

@@ -29,26 +29,26 @@ public interface MapSheetLearnDataGeomRepository
/** 데이터 UID로 기존 지오메트리 데이터 삭제 (재생성 전에 사용) */ /** 데이터 UID로 기존 지오메트리 데이터 삭제 (재생성 전에 사용) */
void deleteByDataUid(Long dataUid); void deleteByDataUid(Long dataUid);
/** /** PostGIS 함수를 사용하여 geometry 데이터를 직접 삽입 ST_SetSRID(ST_GeomFromGeoJSON(...), 5186) 형식으로 저장 */
* PostGIS 함수를 사용하여 geometry 데이터를 직접 삽입
* ST_SetSRID(ST_GeomFromGeoJSON(...), 5186) 형식으로 저장
*/
@Modifying @Modifying
@Transactional @Transactional
@Query(value = """ @Query(
value =
"""
INSERT INTO tb_map_sheet_learn_data_geom ( INSERT INTO tb_map_sheet_learn_data_geom (
geo_uid, cd_prob, class_before_name, class_before_prob, geo_uid, cd_prob, class_before_name, class_before_prob,
class_after_name, class_after_prob, map_sheet_num, class_after_name, class_after_prob, map_sheet_num,
before_yyyy, after_yyyy, area, geom, geo_type, data_uid, before_yyyy, after_yyyy, area, geom, geo_type, data_uid,
created_dttm, updated_dttm created_dttm, updated_dttm
) VALUES ( ) VALUES (
:geoUid, :cdProb, :classBeforeName, :classBeforeProb, :geoUid, :cdProb, :classBeforeName, :classBeforeProb,
:classAfterName, :classAfterProb, :mapSheetNum, :classAfterName, :classAfterProb, :mapSheetNum,
:beforeYyyy, :afterYyyy, :area, :beforeYyyy, :afterYyyy, :area,
ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geometryJson AS TEXT)), 5186), ST_SetSRID(ST_GeomFromGeoJSON(CAST(:geometryJson AS TEXT)), 5186),
:geoType, :dataUid, NOW(), NOW() :geoType, :dataUid, NOW(), NOW()
) ON CONFLICT (geo_uid) DO NOTHING ) ON CONFLICT (geo_uid) DO NOTHING
""", nativeQuery = true) """,
nativeQuery = true)
void insertWithPostGISGeometry( void insertWithPostGISGeometry(
@Param("geoUid") Long geoUid, @Param("geoUid") Long geoUid,
@Param("cdProb") Double cdProb, @Param("cdProb") Double cdProb,
@@ -62,6 +62,5 @@ public interface MapSheetLearnDataGeomRepository
@Param("area") Double area, @Param("area") Double area,
@Param("geometryJson") String geometryJson, @Param("geometryJson") String geometryJson,
@Param("geoType") String geoType, @Param("geoType") String geoType,
@Param("dataUid") Long dataUid @Param("dataUid") Long dataUid);
);
} }

View File

@@ -1,8 +1,6 @@
package com.kamco.cd.kamcoback.postgres.repository.changedetection; package com.kamco.cd.kamcoback.postgres.repository.changedetection;
import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto; import com.kamco.cd.kamcoback.changedetection.dto.ChangeDetectionDto;
import com.querydsl.core.Tuple;
import java.util.List; import java.util.List;
public interface ChangeDetectionRepositoryCustom { public interface ChangeDetectionRepositoryCustom {

View File

@@ -1,5 +1,12 @@
package com.kamco.cd.kamcoback.postgres.repository.changedetection; 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.core.JsonProcessingException;
import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper; 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.Expressions;
import com.querydsl.core.types.dsl.StringExpression; import com.querydsl.core.types.dsl.StringExpression;
import com.querydsl.jpa.impl.JPAQueryFactory; import com.querydsl.jpa.impl.JPAQueryFactory;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import org.springframework.data.jpa.repository.support.QuerydslRepositorySupport;
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 public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
implements ChangeDetectionRepositoryCustom { implements ChangeDetectionRepositoryCustom {
@@ -51,74 +50,77 @@ public class ChangeDetectionRepositoryImpl extends QuerydslRepositorySupport
@Override @Override
public List<ChangeDetectionDto.CountDto> getChangeDetectionClassCount(Long id) { public List<ChangeDetectionDto.CountDto> getChangeDetectionClassCount(Long id) {
return queryFactory return queryFactory
.select(Projections.constructor( .select(
ChangeDetectionDto.CountDto.class, Projections.constructor(
mapSheetAnalSttcEntity.classAfterCd.toUpperCase(), ChangeDetectionDto.CountDto.class,
mapSheetAnalSttcEntity.id.classAfterName, mapSheetAnalSttcEntity.classAfterCd.toUpperCase(),
mapSheetAnalSttcEntity.classAfterCnt.sum() mapSheetAnalSttcEntity.id.classAfterName,
)) mapSheetAnalSttcEntity.classAfterCnt.sum()))
.from(mapSheetAnalEntity) .from(mapSheetAnalEntity)
.innerJoin(mapSheetAnalDataEntity) .innerJoin(mapSheetAnalDataEntity)
.on(mapSheetAnalDataEntity.analUid.eq(mapSheetAnalEntity.id)) .on(mapSheetAnalDataEntity.analUid.eq(mapSheetAnalEntity.id))
.innerJoin(mapSheetAnalSttcEntity) .innerJoin(mapSheetAnalSttcEntity)
.on(mapSheetAnalSttcEntity.dataUid.eq(mapSheetAnalDataEntity.id)) .on(mapSheetAnalSttcEntity.dataUid.eq(mapSheetAnalDataEntity.id))
.where(mapSheetAnalEntity.id.eq(id)) .where(mapSheetAnalEntity.id.eq(id))
.groupBy(mapSheetAnalSttcEntity.classAfterCd, mapSheetAnalSttcEntity.id.classAfterName) .groupBy(mapSheetAnalSttcEntity.classAfterCd, mapSheetAnalSttcEntity.id.classAfterName)
.fetch(); .fetch();
} }
@Override @Override
public ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req) { public ChangeDetectionDto.CogUrlDto getChangeDetectionCogUrl(ChangeDetectionDto.CogUrlReq req) {
Tuple result = queryFactory Tuple result =
.select( queryFactory
makeCogUrl(req.getBeforeYear()).max().as("beforeCogUrl"), .select(
makeCogUrl(req.getAfterYear()).max().as("afterCogUrl"), makeCogUrl(req.getBeforeYear()).max().as("beforeCogUrl"),
Expressions.stringTemplate("ST_AsGeoJSON({0})", mapInkx5kEntity.geom).as("bbox") makeCogUrl(req.getAfterYear()).max().as("afterCogUrl"),
) Expressions.stringTemplate("ST_AsGeoJSON({0})", mapInkx5kEntity.geom).as("bbox"))
.from(imageryEntity) .from(imageryEntity)
.innerJoin(mapInkx5kEntity) .innerJoin(mapInkx5kEntity)
.on(imageryEntity.scene5k.eq(mapInkx5kEntity.mapidcdNo)) .on(imageryEntity.scene5k.eq(mapInkx5kEntity.mapidcdNo))
.where( .where(
imageryEntity.year.eq(req.getBeforeYear()).or(imageryEntity.year.eq(req.getAfterYear())), imageryEntity
imageryEntity.scene5k.eq(req.getMapSheetNum()) .year
) .eq(req.getBeforeYear())
.groupBy(mapInkx5kEntity.geom) .or(imageryEntity.year.eq(req.getAfterYear())),
.fetchOne(); imageryEntity.scene5k.eq(req.getMapSheetNum()))
.groupBy(mapInkx5kEntity.geom)
.fetchOne();
//Polygon -> JsonNode 로 변환 // Polygon -> JsonNode 로 변환
JsonNode geometryJson = changeGeometryJson(String.valueOf(Objects.requireNonNull(result).get(2, StringExpression.class))); 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 @Override
public List<ChangeDetectionDto.AnalYearList> getChangeDetectionYearList() { public List<ChangeDetectionDto.AnalYearList> getChangeDetectionYearList() {
return queryFactory return queryFactory
.select( .select(
Projections.constructor( Projections.constructor(
ChangeDetectionDto.AnalYearList.class, ChangeDetectionDto.AnalYearList.class,
mapSheetAnalEntity.id, mapSheetAnalEntity.id,
mapSheetAnalEntity.analTitle, mapSheetAnalEntity.analTitle,
mapSheetAnalEntity.compareYyyy.as("beforeYear"), mapSheetAnalEntity.compareYyyy.as("beforeYear"),
mapSheetAnalEntity.targetYyyy.as("afterYear"), mapSheetAnalEntity.targetYyyy.as("afterYear"),
mapSheetAnalEntity.baseMapSheetNum mapSheetAnalEntity.baseMapSheetNum))
)
)
.from(mapSheetAnalEntity) .from(mapSheetAnalEntity)
.orderBy(mapSheetAnalEntity.id.asc()) .orderBy(mapSheetAnalEntity.id.asc())
.fetch() .fetch();
;
} }
private StringExpression makeCogUrl(Integer year) { private StringExpression makeCogUrl(Integer year) {
return new CaseBuilder() return new CaseBuilder()
.when(imageryEntity.year.eq(year)) .when(imageryEntity.year.eq(year))
.then(Expressions.stringTemplate("{0} || {1}",imageryEntity.cogMiddlePath, imageryEntity.cogFilename)) .then(
.otherwise("") Expressions.stringTemplate(
; "{0} || {1}", imageryEntity.cogMiddlePath, imageryEntity.cogFilename))
.otherwise("");
} }
private JsonNode changeGeometryJson(String geometry){ private JsonNode changeGeometryJson(String geometry) {
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
try { try {
return mapper.readTree(geometry); return mapper.readTree(geometry);