Merge pull request '[KC-168] 라벨링 툴 - 목록, 상세 API 나누기' (#180) from feat/infer_dev_260107 into develop

Reviewed-on: https://kamco.gitea.gs.dabeeo.com/dabeeo/kamco-dabeeo-backoffice/pulls/180
This commit is contained in:
2026-01-12 10:56:10 +09:00
6 changed files with 126 additions and 55 deletions

View File

@@ -1,6 +1,7 @@
package com.kamco.cd.kamcoback.postgres.core; package com.kamco.cd.kamcoback.postgres.core;
import com.kamco.cd.kamcoback.postgres.repository.trainingdata.TrainingDataLabelRepository; import com.kamco.cd.kamcoback.postgres.repository.trainingdata.TrainingDataLabelRepository;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingGeometryInfo;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingListDto; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingListDto;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.searchReq; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.searchReq;
import lombok.RequiredArgsConstructor; import lombok.RequiredArgsConstructor;
@@ -17,4 +18,8 @@ public class TrainingDataLabelCoreService {
searchReq searchReq, String userId, String status) { searchReq searchReq, String userId, String status) {
return trainingDataLabelRepository.findLabelingAssignedList(searchReq, userId, status); return trainingDataLabelRepository.findLabelingAssignedList(searchReq, userId, status);
} }
public LabelingGeometryInfo findLabelingAssignedGeom(String assignmentUid) {
return trainingDataLabelRepository.findLabelingAssignedGeom(assignmentUid);
}
} }

View File

@@ -1,5 +1,6 @@
package com.kamco.cd.kamcoback.postgres.repository.trainingdata; package com.kamco.cd.kamcoback.postgres.repository.trainingdata;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingGeometryInfo;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingListDto; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingListDto;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.searchReq; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.searchReq;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -7,4 +8,6 @@ import org.springframework.data.domain.Page;
public interface TrainingDataLabelRepositoryCustom { public interface TrainingDataLabelRepositoryCustom {
Page<LabelingListDto> findLabelingAssignedList(searchReq searchReq, String userId, String status); Page<LabelingListDto> findLabelingAssignedList(searchReq searchReq, String userId, String status);
LabelingGeometryInfo findLabelingAssignedGeom(String assignmentUid);
} }

View File

@@ -6,6 +6,7 @@ import static com.kamco.cd.kamcoback.postgres.entity.QMapInkx5kEntity.mapInkx5kE
import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity; import static com.kamco.cd.kamcoback.postgres.entity.QMapSheetAnalDataInferenceGeomEntity.mapSheetAnalDataInferenceGeomEntity;
import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity; import com.kamco.cd.kamcoback.postgres.entity.LabelingAssignmentEntity;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingGeometryInfo;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingListDto; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingListDto;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.searchReq; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.searchReq;
import com.querydsl.core.types.Projections; import com.querydsl.core.types.Projections;
@@ -18,6 +19,7 @@ import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
import java.util.UUID;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl; import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.PageRequest;
@@ -49,15 +51,7 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
labelingAssignmentEntity.workState, labelingAssignmentEntity.workState,
labelingAssignmentEntity.assignGroupId, labelingAssignmentEntity.assignGroupId,
mapInkx5kEntity.mapidNm, mapInkx5kEntity.mapidNm,
mapSheetAnalDataInferenceGeomEntity.pnu, mapSheetAnalDataInferenceGeomEntity.pnu))
Expressions.stringTemplate(
"ST_AsGeoJSON({0})", mapSheetAnalDataInferenceGeomEntity.geom),
makeCogUrl(mapSheetAnalDataInferenceGeomEntity.compareYyyy)
.max()
.as("beforeCogUrl"),
makeCogUrl(mapSheetAnalDataInferenceGeomEntity.targetYyyy)
.max()
.as("afterCogUrl")))
.from(labelingAssignmentEntity) .from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalDataInferenceGeomEntity) .innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on( .on(
@@ -65,28 +59,7 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
mapSheetAnalDataInferenceGeomEntity.geoUid)) mapSheetAnalDataInferenceGeomEntity.geoUid))
.innerJoin(mapInkx5kEntity) .innerJoin(mapInkx5kEntity)
.on(labelingAssignmentEntity.assignGroupId.eq(mapInkx5kEntity.mapidcdNo)) .on(labelingAssignmentEntity.assignGroupId.eq(mapInkx5kEntity.mapidcdNo))
.leftJoin(imageryEntity)
.on(
imageryEntity
.scene5k
.eq(labelingAssignmentEntity.assignGroupId)
.and(
imageryEntity
.year
.eq(mapSheetAnalDataInferenceGeomEntity.compareYyyy)
.or(
imageryEntity.year.eq(
mapSheetAnalDataInferenceGeomEntity.targetYyyy))))
.where(labelingAssignmentEntity.workerUid.eq(userId), statusInLabelState(status)) .where(labelingAssignmentEntity.workerUid.eq(userId), statusInLabelState(status))
.groupBy(
labelingAssignmentEntity.assignmentUid,
labelingAssignmentEntity.inferenceGeomUid,
labelingAssignmentEntity.workerUid,
labelingAssignmentEntity.workState,
labelingAssignmentEntity.assignGroupId,
mapInkx5kEntity.mapidNm,
mapSheetAnalDataInferenceGeomEntity.pnu,
mapSheetAnalDataInferenceGeomEntity.geom)
.offset(pageable.getOffset()) .offset(pageable.getOffset())
.limit(pageable.getPageSize()) .limit(pageable.getPageSize())
.orderBy( .orderBy(
@@ -105,6 +78,36 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
mapSheetAnalDataInferenceGeomEntity.geoUid)) mapSheetAnalDataInferenceGeomEntity.geoUid))
.innerJoin(mapInkx5kEntity) .innerJoin(mapInkx5kEntity)
.on(labelingAssignmentEntity.assignGroupId.eq(mapInkx5kEntity.mapidcdNo)) .on(labelingAssignmentEntity.assignGroupId.eq(mapInkx5kEntity.mapidcdNo))
.where(
labelingAssignmentEntity.workerUid.eq(userId), statusInLabelState(status))
.fetchOne())
.orElse(0L);
return new PageImpl<>(list, pageable, count);
}
@Override
public LabelingGeometryInfo findLabelingAssignedGeom(String assignmentUid) {
return queryFactory
.select(
Projections.constructor(
LabelingGeometryInfo.class,
labelingAssignmentEntity.assignmentUid,
labelingAssignmentEntity.inferenceGeomUid,
Expressions.stringTemplate(
"ST_AsGeoJSON({0})", mapSheetAnalDataInferenceGeomEntity.geom),
makeCogUrl(mapSheetAnalDataInferenceGeomEntity.compareYyyy)
.max()
.as("beforeCogUrl"),
makeCogUrl(mapSheetAnalDataInferenceGeomEntity.targetYyyy).max().as("afterCogUrl"),
Expressions.stringTemplate("ST_AsGeoJSON({0})", mapInkx5kEntity.geom)))
.from(labelingAssignmentEntity)
.innerJoin(mapSheetAnalDataInferenceGeomEntity)
.on(
labelingAssignmentEntity.inferenceGeomUid.eq(
mapSheetAnalDataInferenceGeomEntity.geoUid))
.innerJoin(mapInkx5kEntity)
.on(labelingAssignmentEntity.assignGroupId.eq(mapInkx5kEntity.mapidcdNo))
.leftJoin(imageryEntity) .leftJoin(imageryEntity)
.on( .on(
imageryEntity imageryEntity
@@ -114,16 +117,19 @@ public class TrainingDataLabelRepositoryImpl extends QuerydslRepositorySupport
imageryEntity imageryEntity
.year .year
.eq(mapSheetAnalDataInferenceGeomEntity.compareYyyy) .eq(mapSheetAnalDataInferenceGeomEntity.compareYyyy)
.or( .or(imageryEntity.year.eq(mapSheetAnalDataInferenceGeomEntity.targetYyyy))))
imageryEntity.year.eq( .where(labelingAssignmentEntity.assignmentUid.eq(UUID.fromString(assignmentUid)))
mapSheetAnalDataInferenceGeomEntity.targetYyyy)))) .groupBy(
.where( labelingAssignmentEntity.assignmentUid,
labelingAssignmentEntity.workerUid.eq(userId), statusInLabelState(status)) labelingAssignmentEntity.inferenceGeomUid,
.groupBy(labelingAssignmentEntity.assignmentUid) labelingAssignmentEntity.workerUid,
.fetchOne()) labelingAssignmentEntity.workState,
.orElse(0L); labelingAssignmentEntity.assignGroupId,
mapInkx5kEntity.mapidNm,
return new PageImpl<>(list, pageable, count); mapSheetAnalDataInferenceGeomEntity.pnu,
mapSheetAnalDataInferenceGeomEntity.geom,
mapInkx5kEntity.geom)
.fetchOne();
} }
private StringExpression makeCogUrl(NumberPath<Integer> year) { private StringExpression makeCogUrl(NumberPath<Integer> year) {

View File

@@ -3,6 +3,7 @@ package com.kamco.cd.kamcoback.trainingdata;
import com.kamco.cd.kamcoback.code.dto.CommonCodeDto; import com.kamco.cd.kamcoback.code.dto.CommonCodeDto;
import com.kamco.cd.kamcoback.config.api.ApiResponseDto; import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingGeometryInfo;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingListDto; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingListDto;
import com.kamco.cd.kamcoback.trainingdata.service.TrainingDataLabelService; import com.kamco.cd.kamcoback.trainingdata.service.TrainingDataLabelService;
import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Operation;
@@ -49,4 +50,23 @@ public class TrainingDataLabelApiController {
return ApiResponseDto.ok( return ApiResponseDto.ok(
trainingDataLabelService.findLabelingAssignedList(searchReq, userId, status)); trainingDataLabelService.findLabelingAssignedList(searchReq, userId, status));
} }
@Operation(summary = "상세 Geometry 조회", description = "라벨 할당 상세 Geometry 조회")
@ApiResponses(
value = {
@ApiResponse(
responseCode = "200",
description = "조회 성공",
content =
@Content(
mediaType = "application/json",
schema = @Schema(implementation = CommonCodeDto.Basic.class))),
@ApiResponse(responseCode = "404", description = "코드를 찾을 수 없음", content = @Content),
@ApiResponse(responseCode = "500", description = "서버 오류", content = @Content)
})
@GetMapping("/geom-info")
public ApiResponseDto<LabelingGeometryInfo> findLabelingAssignedGeom(
@RequestParam(defaultValue = "4f9ebc8b-6635-4177-b42f-7efc9c7b4c02") String assignmentUid) {
return ApiResponseDto.ok(trainingDataLabelService.findLabelingAssignedGeom(assignmentUid));
}
} }

View File

@@ -29,10 +29,15 @@ public class TrainingDataLabelDto {
private String mapSheetNum; private String mapSheetNum;
private String mapIdNm; private String mapIdNm;
private Long pnu; private Long pnu;
@JsonIgnore private String geom_data; // json string
private JsonNode geom; // @JsonIgnore
private String beforeCogUrl; // private String geomData; // json string
private String afterCogUrl; // private JsonNode geom;
// private String beforeCogUrl;
// private String afterCogUrl;
// @JsonIgnore
// private String mapBboxString; //json string
// private JsonNode mapBbox;
public LabelingListDto( public LabelingListDto(
UUID assignmentUid, UUID assignmentUid,
@@ -41,10 +46,7 @@ public class TrainingDataLabelDto {
String workState, String workState,
String mapSheetNum, String mapSheetNum,
String mapIdNm, String mapIdNm,
Long pnu, Long pnu) {
String geom_data,
String beforeCogUrl,
String afterCogUrl) {
this.assignmentUid = assignmentUid; this.assignmentUid = assignmentUid;
this.inferenceGeomUid = inferenceGeomUid; this.inferenceGeomUid = inferenceGeomUid;
this.workerUid = workerUid; this.workerUid = workerUid;
@@ -52,18 +54,48 @@ public class TrainingDataLabelDto {
this.mapSheetNum = mapSheetNum; this.mapSheetNum = mapSheetNum;
this.mapIdNm = mapIdNm; this.mapIdNm = mapIdNm;
this.pnu = pnu; this.pnu = pnu;
}
}
@Schema(name = "LabelingGeometryInfo", description = "LabelingGeometryInfo")
@Getter
@Setter
@NoArgsConstructor
public static class LabelingGeometryInfo {
private UUID assignmentUid;
private Long inferenceGeomUid;
@JsonIgnore private String geomData; // json string
private JsonNode geom;
private String beforeCogUrl;
private String afterCogUrl;
@JsonIgnore private String mapBboxString; // json string
private JsonNode mapBbox;
public LabelingGeometryInfo(
UUID assignmentUid,
Long inferenceGeomUid,
String geomData,
String beforeCogUrl,
String afterCogUrl,
String mapBboxString) {
this.assignmentUid = assignmentUid;
this.inferenceGeomUid = inferenceGeomUid;
this.beforeCogUrl = beforeCogUrl;
this.afterCogUrl = afterCogUrl;
ObjectMapper mapper = new ObjectMapper(); ObjectMapper mapper = new ObjectMapper();
JsonNode geomJson; JsonNode geomJson;
JsonNode mapBboxJson;
try { try {
geomJson = mapper.readTree(geom_data); geomJson = mapper.readTree(geomData);
mapBboxJson = mapper.readTree(mapBboxString);
} catch (JsonProcessingException e) { } catch (JsonProcessingException e) {
throw new RuntimeException(e); throw new RuntimeException(e);
} }
this.geom = geomJson;
this.beforeCogUrl = beforeCogUrl; this.geom = geomJson;
this.afterCogUrl = afterCogUrl; this.mapBbox = mapBboxJson;
} }
} }

View File

@@ -1,6 +1,7 @@
package com.kamco.cd.kamcoback.trainingdata.service; package com.kamco.cd.kamcoback.trainingdata.service;
import com.kamco.cd.kamcoback.postgres.core.TrainingDataLabelCoreService; import com.kamco.cd.kamcoback.postgres.core.TrainingDataLabelCoreService;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingGeometryInfo;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingListDto; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.LabelingListDto;
import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.searchReq; import com.kamco.cd.kamcoback.trainingdata.dto.TrainingDataLabelDto.searchReq;
import org.springframework.data.domain.Page; import org.springframework.data.domain.Page;
@@ -19,4 +20,8 @@ public class TrainingDataLabelService {
searchReq searchReq, String userId, String status) { searchReq searchReq, String userId, String status) {
return trainingDataLabelCoreService.findLabelingAssignedList(searchReq, userId, status); return trainingDataLabelCoreService.findLabelingAssignedList(searchReq, userId, status);
} }
public LabelingGeometryInfo findLabelingAssignedGeom(String assignmentUid) {
return trainingDataLabelCoreService.findLabelingAssignedGeom(assignmentUid);
}
} }