shp 파일 생성 수정

This commit is contained in:
2025-12-26 16:39:31 +09:00
parent 2a60ddfd36
commit 38aa998aea
8 changed files with 312 additions and 231 deletions

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
import com.kamco.cd.kamcoback.postgres.entity.MapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.repository.Inference.InferenceResultRepository;
import java.util.List;
@@ -13,22 +14,32 @@ public class InferenceResultShpCoreService {
private final InferenceResultRepository repo;
/** inference_results -> (inference, geom) upsert */
/**
* inference_results 기준으로 - tb_map_sheet_anal_data_inference -
* tb_map_sheet_anal_data_inference_geom 테이블을 최신 상태로 구성한다.
*/
@Transactional
public void buildInferenceData() {
repo.upsertGroupsFromInferenceResults();
repo.upsertGeomsFromInferenceResults();
public InferenceResultShpDto.InferenceCntDto buildInferenceData() {
int inferenceCnt = repo.upsertGroupsFromInferenceResults();
int inferenceGeomCnt = repo.upsertGeomsFromInferenceResults();
InferenceResultShpDto.InferenceCntDto cntDto = new InferenceResultShpDto.InferenceCntDto();
cntDto.setInferenceCnt(inferenceCnt);
cntDto.setInferenceGeomCnt(inferenceGeomCnt);
return cntDto;
}
/** file_created_yn = false/null 인 data_uid 목록 */
/** 파일 생성이 완료되지 않은 분석 데이터(data_uid) 목록을 조회한다. */
@Transactional(readOnly = true)
public List<Long> findPendingDataUids(int limit) {
return repo.findPendingDataUids(limit);
}
/**
* 재생성 시작: 덮어쓰기 기준(무조건 처음부터) - inference.file_created_yn = false, file_created_dttm = null -
* geom(file_created_yn) 전부 false 리셋
* 분석 데이터 재생성을 위해 기존 파일 생성 상태를 초기화한다.
*
* <p>- 분석 데이터(file_created_yn)를 미생성 상태로 변경 - 해당 분석 데이터에 속한 모든 도형의 생성 상태를 미생성으로 변경
*/
@Transactional
public void resetForRegenerate(Long dataUid) {
@@ -36,13 +47,25 @@ public class InferenceResultShpCoreService {
repo.resetGeomCreatedByDataUid(dataUid);
}
/** 생성 대상 geom 엔티티 로드 (file_created_yn=false/null + geom not null) */
/**
* 지정된 분석 데이터에 속한 도형 정보를 조회한다.
*
* <p>- 파일 미생성 상태의 도형만 대상 - geometry가 존재하는 도형만 조회
*/
@Transactional(readOnly = true)
public List<MapSheetAnalDataInferenceGeomEntity> loadGeomEntities(Long dataUid, int limit) {
return repo.findGeomEntitiesByDataUid(dataUid, limit);
public List<InferenceResultShpDto.Basic> loadGeomDtos(Long dataUid, int limit) {
List<MapSheetAnalDataInferenceGeomEntity> entities =
repo.findGeomEntitiesByDataUid(dataUid, limit);
return entities.stream().map(InferenceResultShpDto.Basic::from).toList();
}
/** 성공 마킹: - 성공 geo_uid만 geom.file_created_yn=true - inference.file_created_yn=true */
/**
* 파일 생성이 성공한 도형 및 분석 데이터에 대해 생성 완료 상태로 갱신한다.
*
* @param dataUid 분석 데이터 UID
* @param geoUids 파일 생성이 완료된 도형 UID 목록
*/
@Transactional
public void markSuccess(Long dataUid, List<Long> geoUids) {
repo.markGeomCreatedByGeoUids(geoUids);

View File

@@ -19,114 +19,131 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
@PersistenceContext private final EntityManager em;
private final QMapSheetAnalDataInferenceEntity i =
/** tb_map_sheet_anal_data_inference */
private final QMapSheetAnalDataInferenceEntity inferenceEntity =
QMapSheetAnalDataInferenceEntity.mapSheetAnalDataInferenceEntity;
private final QMapSheetAnalDataInferenceGeomEntity g =
/** tb_map_sheet_anal_data_inference_geom */
private final QMapSheetAnalDataInferenceGeomEntity inferenceGeomEntity =
QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity;
// ===============================
// Upsert (Native only)
// ===============================
/**
* inference_results 테이블을 기준으로 분석 데이터 단위(stage, compare_yyyy, target_yyyy, map_sheet_num)를
* 생성/갱신한다.
*
* <p>- 최초 생성 시 file_created_yn = false - detecting_cnt는 inference_results 건수 기준
*
* @return 반영된 행 수
*/
@Override
public int upsertGroupsFromInferenceResults() {
String sql =
"""
INSERT INTO tb_map_sheet_anal_data_inference (
stage,
compare_yyyy,
target_yyyy,
map_sheet_num,
created_dttm,
updated_dttm,
file_created_yn,
detecting_cnt
)
SELECT
r.stage,
r.input1 AS compare_yyyy,
r.input2 AS target_yyyy,
r.map_id AS map_sheet_num,
now() AS created_dttm,
now() AS updated_dttm,
false AS file_created_yn,
count(*) AS detecting_cnt
FROM inference_results r
GROUP BY r.stage, r.input1, r.input2, r.map_id
ON CONFLICT (stage, compare_yyyy, target_yyyy, map_sheet_num)
DO UPDATE SET
updated_dttm = now(),
detecting_cnt = EXCLUDED.detecting_cnt
""";
INSERT INTO tb_map_sheet_anal_data_inference (
stage,
compare_yyyy,
target_yyyy,
map_sheet_num,
created_dttm,
updated_dttm,
file_created_yn,
detecting_cnt
)
SELECT
r.stage,
r.input1 AS compare_yyyy,
r.input2 AS target_yyyy,
r.map_id AS map_sheet_num,
now() AS created_dttm,
now() AS updated_dttm,
false AS file_created_yn,
count(*) AS detecting_cnt
FROM inference_results r
GROUP BY r.stage, r.input1, r.input2, r.map_id
ON CONFLICT (stage, compare_yyyy, target_yyyy, map_sheet_num)
DO UPDATE SET
updated_dttm = now(),
detecting_cnt = EXCLUDED.detecting_cnt
""";
return em.createNativeQuery(sql).executeUpdate();
}
/**
* inference_results 테이블을 기준으로 도형 단위(uuid) 분석 결과를 생성/갱신한다.
*
* <p>- uuid 기준 중복 제거(DISTINCT ON) - 최신 updated_dttm 우선 - geometry는 WKB / WKT 모두 처리 - 최초 생성 시
* file_created_yn = false
*
* @return 반영된 행 수
*/
@Override
public int upsertGeomsFromInferenceResults() {
// class_after_prob 컬럼 매핑 오타 주의(여기 제대로 넣음)
String sql =
"""
INSERT INTO tb_map_sheet_anal_data_inference_geom (
uuid, stage, cd_prob, compare_yyyy, target_yyyy, map_sheet_num,
class_before_cd, class_before_prob, class_after_cd, class_after_prob,
geom, area, data_uid, created_dttm, updated_dttm,
file_created_yn
)
SELECT
x.uuid, x.stage, x.cd_prob, x.compare_yyyy, x.target_yyyy, x.map_sheet_num,
x.class_before_cd, x.class_before_prob, x.class_after_cd, x.class_after_prob,
x.geom, x.area, x.data_uid, x.created_dttm, x.updated_dttm,
false AS file_created_yn
FROM (
SELECT DISTINCT ON (r.uuid)
r.uuid,
r.stage,
r.cd_prob,
r.input1 AS compare_yyyy,
r.input2 AS target_yyyy,
r.map_id AS map_sheet_num,
r.before_class AS class_before_cd,
r.before_probability AS class_before_prob,
r.after_class AS class_after_cd,
r.after_probability AS class_after_prob,
CASE
WHEN r.geometry IS NULL THEN NULL
WHEN left(r.geometry, 2) = '01'
THEN ST_SetSRID(ST_GeomFromWKB(decode(r.geometry, 'hex')), 5186)
ELSE ST_GeomFromText(r.geometry, 5186)
END AS geom,
r.area,
di.data_uid,
r.created_dttm,
r.updated_dttm
FROM inference_results r
JOIN tb_map_sheet_anal_data_inference di
ON di.stage = r.stage
AND di.compare_yyyy = r.input1
AND di.target_yyyy = r.input2
AND di.map_sheet_num = r.map_id
ORDER BY r.uuid, r.updated_dttm DESC NULLS LAST, r.uid DESC
) x
ON CONFLICT (uuid)
DO UPDATE SET
stage = EXCLUDED.stage,
cd_prob = EXCLUDED.cd_prob,
compare_yyyy = EXCLUDED.compare_yyyy,
target_yyyy = EXCLUDED.target_yyyy,
map_sheet_num = EXCLUDED.map_sheet_num,
class_before_cd = EXCLUDED.class_before_cd,
class_before_prob = EXCLUDED.class_before_prob,
class_after_cd = EXCLUDED.class_after_cd,
class_after_prob = EXCLUDED.class_after_prob,
geom = EXCLUDED.geom,
area = EXCLUDED.area,
data_uid = EXCLUDED.data_uid,
updated_dttm = now()
""";
INSERT INTO tb_map_sheet_anal_data_inference_geom (
uuid, stage, cd_prob, compare_yyyy, target_yyyy, map_sheet_num,
class_before_cd, class_before_prob, class_after_cd, class_after_prob,
geom, area, data_uid, created_dttm, updated_dttm,
file_created_yn
)
SELECT
x.uuid, x.stage, x.cd_prob, x.compare_yyyy, x.target_yyyy, x.map_sheet_num,
x.class_before_cd, x.class_before_prob, x.class_after_cd, x.class_after_prob,
x.geom, x.area, x.data_uid, x.created_dttm, x.updated_dttm,
false AS file_created_yn
FROM (
SELECT DISTINCT ON (r.uuid)
r.uuid,
r.stage,
r.cd_prob,
r.input1 AS compare_yyyy,
r.input2 AS target_yyyy,
r.map_id AS map_sheet_num,
r.before_class AS class_before_cd,
r.before_probability AS class_before_prob,
r.after_class AS class_after_cd,
r.after_probability AS class_after_prob,
CASE
WHEN r.geometry IS NULL THEN NULL
WHEN left(r.geometry, 2) = '01'
THEN ST_SetSRID(ST_GeomFromWKB(decode(r.geometry, 'hex')), 5186)
ELSE ST_GeomFromText(r.geometry, 5186)
END AS geom,
r.area,
di.data_uid,
r.created_dttm,
r.updated_dttm
FROM inference_results r
JOIN tb_map_sheet_anal_data_inference di
ON di.stage = r.stage
AND di.compare_yyyy = r.input1
AND di.target_yyyy = r.input2
AND di.map_sheet_num = r.map_id
ORDER BY r.uuid, r.updated_dttm DESC NULLS LAST, r.uid DESC
) x
ON CONFLICT (uuid)
DO UPDATE SET
stage = EXCLUDED.stage,
cd_prob = EXCLUDED.cd_prob,
compare_yyyy = EXCLUDED.compare_yyyy,
target_yyyy = EXCLUDED.target_yyyy,
map_sheet_num = EXCLUDED.map_sheet_num,
class_before_cd = EXCLUDED.class_before_cd,
class_before_prob = EXCLUDED.class_before_prob,
class_after_cd = EXCLUDED.class_after_cd,
class_after_prob = EXCLUDED.class_after_prob,
geom = EXCLUDED.geom,
area = EXCLUDED.area,
data_uid = EXCLUDED.data_uid,
updated_dttm = now()
""";
return em.createNativeQuery(sql).executeUpdate();
}
@@ -135,63 +152,92 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
// Jobs
// ===============================
/**
* 파일 생성이 완료되지 않은 분석 데이터(data_uid) 목록을 조회한다.
*
* @param limit 최대 조회 건수
* @return data_uid 목록
*/
@Override
public List<Long> findPendingDataUids(int limit) {
return queryFactory
.select(i.id) // data_uid
.from(i)
.where(i.fileCreatedYn.isFalse().or(i.fileCreatedYn.isNull()))
.orderBy(i.id.asc())
.select(inferenceEntity.id)
.from(inferenceEntity)
.where(inferenceEntity.fileCreatedYn.isFalse().or(inferenceEntity.fileCreatedYn.isNull()))
.orderBy(inferenceEntity.id.asc())
.limit(limit)
.fetch();
}
// ===============================
// Reset / Mark (전부 ZonedDateTime)
// Reset / Mark
// ===============================
/**
* 분석 데이터의 파일 생성 상태를 재생성 가능 상태로 초기화한다.
*
* <p>- file_created_yn = false - file_created_dttm = null
*
* @return 갱신된 행 수
*/
@Override
public int resetInferenceCreated(Long dataUid) {
ZonedDateTime now = ZonedDateTime.now();
return (int)
queryFactory
.update(i)
.set(i.fileCreatedYn, false)
.set(i.fileCreatedDttm, (ZonedDateTime) null)
.set(i.updatedDttm, now)
.where(i.id.eq(dataUid))
.update(inferenceEntity)
.set(inferenceEntity.fileCreatedYn, false)
.set(inferenceEntity.fileCreatedDttm, (ZonedDateTime) null)
.set(inferenceEntity.updatedDttm, now)
.where(inferenceEntity.id.eq(dataUid))
.execute();
}
/**
* 분석 데이터의 파일 생성 완료 상태를 반영한다.
*
* @return 갱신된 행 수
*/
@Override
public int markInferenceCreated(Long dataUid) {
ZonedDateTime now = ZonedDateTime.now();
return (int)
queryFactory
.update(i)
.set(i.fileCreatedYn, true)
.set(i.fileCreatedDttm, now)
.set(i.updatedDttm, now)
.where(i.id.eq(dataUid))
.update(inferenceEntity)
.set(inferenceEntity.fileCreatedYn, true)
.set(inferenceEntity.fileCreatedDttm, now)
.set(inferenceEntity.updatedDttm, now)
.where(inferenceEntity.id.eq(dataUid))
.execute();
}
/**
* 분석 데이터에 속한 모든 도형의 파일 생성 상태를 초기화한다.
*
* @return 갱신된 행 수
*/
@Override
public int resetGeomCreatedByDataUid(Long dataUid) {
ZonedDateTime now = ZonedDateTime.now();
return (int)
queryFactory
.update(g)
.set(g.fileCreatedYn, false)
.set(g.fileCreatedDttm, (ZonedDateTime) null)
.set(g.updatedDttm, now) // ✅ 엔티티/Q가 ZonedDateTime이면 정상
.where(g.dataUid.eq(dataUid))
.update(inferenceGeomEntity)
.set(inferenceGeomEntity.fileCreatedYn, false)
.set(inferenceGeomEntity.fileCreatedDttm, (ZonedDateTime) null)
.set(inferenceGeomEntity.updatedDttm, now)
.where(inferenceGeomEntity.dataUid.eq(dataUid))
.execute();
}
/**
* 파일 생성이 완료된 도형(geo_uid)을 생성 완료 상태로 반영한다.
*
* @param geoUids 생성 완료된 도형 UID 목록
* @return 갱신된 행 수
*/
@Override
public int markGeomCreatedByGeoUids(List<Long> geoUids) {
if (geoUids == null || geoUids.isEmpty()) {
@@ -202,11 +248,11 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
return (int)
queryFactory
.update(g)
.set(g.fileCreatedYn, true)
.set(g.fileCreatedDttm, now)
.set(g.updatedDttm, now)
.where(g.geoUid.in(geoUids))
.update(inferenceGeomEntity)
.set(inferenceGeomEntity.fileCreatedYn, true)
.set(inferenceGeomEntity.fileCreatedDttm, now)
.set(inferenceGeomEntity.updatedDttm, now)
.where(inferenceGeomEntity.geoUid.in(geoUids))
.execute();
}
@@ -214,16 +260,24 @@ public class InferenceResultRepositoryImpl implements InferenceResultRepositoryC
// Export source (Entity only)
// ===============================
/**
* SHP / GeoJSON 파일 생성을 위한 도형 데이터 조회
*
* <p>- 특정 분석 데이터(data_uid)에 속한 도형 - geometry 존재 - 파일 미생성 상태만 대상
*/
@Override
public List<MapSheetAnalDataInferenceGeomEntity> findGeomEntitiesByDataUid(
Long dataUid, int limit) {
return queryFactory
.selectFrom(g)
.selectFrom(inferenceGeomEntity)
.where(
g.dataUid.eq(dataUid),
g.geom.isNotNull(),
g.fileCreatedYn.isFalse().or(g.fileCreatedYn.isNull()))
.orderBy(g.geoUid.asc())
inferenceGeomEntity.dataUid.eq(dataUid),
inferenceGeomEntity.geom.isNotNull(),
inferenceGeomEntity
.fileCreatedYn
.isFalse()
.or(inferenceGeomEntity.fileCreatedYn.isNull()))
.orderBy(inferenceGeomEntity.geoUid.asc())
.limit(limit)
.fetch();
}