Merge pull request ':spotlessApply 실행' (#19) from feat/demo-20251205 into develop
Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/19
This commit is contained in:
@@ -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<List<JsonNode>> getPolygonToJson(){
|
||||
public ApiResponseDto<List<JsonNode>> getPolygonToJson() {
|
||||
return ApiResponseDto.ok(changeDetectionService.getPolygonToJson());
|
||||
}
|
||||
|
||||
@Operation(summary = "변화탐지 분류별 건수", description = "변화탐지 분류별 건수")
|
||||
@GetMapping("/class-count/{id}")
|
||||
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));
|
||||
}
|
||||
|
||||
@Operation(summary = "변화탐지 COG Url", description = "변화탐지 COG Url")
|
||||
@GetMapping("/cog-url")
|
||||
public ApiResponseDto<ChangeDetectionDto.CogUrlDto> 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<List<ChangeDetectionDto.AnalYearList>> getChangeDetectionYearList(){
|
||||
public ApiResponseDto<List<ChangeDetectionDto.AnalYearList>> getChangeDetectionYearList() {
|
||||
return ApiResponseDto.ok(changeDetectionService.getChangeDetectionYearList());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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; // 비교 분류
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<JsonNode> 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<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;
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
}
|
||||
|
||||
@@ -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")
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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<ChangeDetectionDto.CountDto> 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<ChangeDetectionDto.AnalYearList> 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);
|
||||
|
||||
Reference in New Issue
Block a user