파일생성 추가
This commit is contained in:
@@ -0,0 +1,27 @@
|
||||
package com.kamco.cd.kamcoback.inference;
|
||||
|
||||
import com.kamco.cd.kamcoback.config.api.ApiResponseDto;
|
||||
import com.kamco.cd.kamcoback.inference.service.InferenceResultShpService;
|
||||
import io.swagger.v3.oas.annotations.Operation;
|
||||
import io.swagger.v3.oas.annotations.tags.Tag;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.web.bind.annotation.PostMapping;
|
||||
import org.springframework.web.bind.annotation.RequestMapping;
|
||||
import org.springframework.web.bind.annotation.RestController;
|
||||
|
||||
@Tag(name = "추론결과 데이터 생성", description = "추론결과 데이터 생성 API")
|
||||
@RestController
|
||||
@RequiredArgsConstructor
|
||||
@RequestMapping("/api/inference/shp")
|
||||
public class InferenceResultShpApiController {
|
||||
|
||||
private final InferenceResultShpService inferenceResultShpService;
|
||||
|
||||
|
||||
@Operation(summary = "추론결과 데이터 저장", description = "추론결과 데이터 저장")
|
||||
@PostMapping
|
||||
public ApiResponseDto<Void> saveInferenceResultData() {
|
||||
inferenceResultShpService.saveInferenceResultData();
|
||||
return ApiResponseDto.createOK(null);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
package com.kamco.cd.kamcoback.inference;
|
||||
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
|
||||
import java.util.List;
|
||||
|
||||
public interface ShpWriter {
|
||||
|
||||
// SHP (.shp/.shx/.dbf)
|
||||
void writeShp(String shpBasePath, List<InferenceResultShpDto.Basic> rows);
|
||||
|
||||
// GeoJSON (.geojson)
|
||||
void writeGeoJson(String geoJsonPath, List<InferenceResultShpDto.Basic> rows);
|
||||
}
|
||||
@@ -0,0 +1,86 @@
|
||||
package com.kamco.cd.kamcoback.inference.dto;
|
||||
|
||||
import com.kamco.cd.kamcoback.postgres.entity.InferenceResultEntity;
|
||||
import java.time.ZonedDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
|
||||
public class InferenceResultShpDto {
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public static class Basic {
|
||||
|
||||
private Long id;
|
||||
private UUID uuid;
|
||||
private Integer stage;
|
||||
private Float cdProb;
|
||||
private Integer input1;
|
||||
private Integer input2;
|
||||
private Long mapId;
|
||||
private String beforeClass;
|
||||
private Float beforeProbability;
|
||||
private String afterClass;
|
||||
private Float afterProbability;
|
||||
private Float area;
|
||||
private ZonedDateTime createdDttm;
|
||||
private ZonedDateTime updatedDttm;
|
||||
private Geometry geometry;
|
||||
|
||||
public Basic(
|
||||
Long id,
|
||||
UUID uuid,
|
||||
Integer stage,
|
||||
Float cdProb,
|
||||
Integer input1,
|
||||
Integer input2,
|
||||
Long mapId,
|
||||
String beforeClass,
|
||||
Float beforeProbability,
|
||||
String afterClass,
|
||||
Float afterProbability,
|
||||
Float area,
|
||||
ZonedDateTime createdDttm,
|
||||
ZonedDateTime updatedDttm,
|
||||
Geometry geometry
|
||||
) {
|
||||
this.id = id;
|
||||
this.uuid = uuid;
|
||||
this.stage = stage;
|
||||
this.cdProb = cdProb;
|
||||
this.input1 = input1;
|
||||
this.input2 = input2;
|
||||
this.mapId = mapId;
|
||||
this.beforeClass = beforeClass;
|
||||
this.beforeProbability = beforeProbability;
|
||||
this.afterClass = afterClass;
|
||||
this.afterProbability = afterProbability;
|
||||
this.area = area;
|
||||
this.createdDttm = createdDttm;
|
||||
this.updatedDttm = updatedDttm;
|
||||
this.geometry = geometry;
|
||||
}
|
||||
|
||||
public static Basic from(InferenceResultEntity e) {
|
||||
return new Basic(
|
||||
e.getId(),
|
||||
e.getUuid(),
|
||||
e.getStage(),
|
||||
e.getCdProb(),
|
||||
e.getInput1(),
|
||||
e.getInput2(),
|
||||
e.getMapId(),
|
||||
e.getBeforeClass(),
|
||||
e.getBeforeProbability(),
|
||||
e.getAfterClass(),
|
||||
e.getAfterProbability(),
|
||||
e.getArea(),
|
||||
e.getCreatedDttm(),
|
||||
e.getUpdatedDttm(),
|
||||
e.getGeometry()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
package com.kamco.cd.kamcoback.inference.service;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
import com.kamco.cd.kamcoback.inference.ShpWriter;
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
|
||||
import java.io.File;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
import java.io.Serializable;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.nio.file.Files;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.geotools.api.data.SimpleFeatureSource;
|
||||
import org.geotools.api.data.SimpleFeatureStore;
|
||||
import org.geotools.api.data.Transaction;
|
||||
import org.geotools.api.feature.simple.SimpleFeature;
|
||||
import org.geotools.api.feature.simple.SimpleFeatureType;
|
||||
import org.geotools.api.referencing.crs.CoordinateReferenceSystem;
|
||||
import org.geotools.data.shapefile.ShapefileDataStore;
|
||||
import org.geotools.data.shapefile.ShapefileDataStoreFactory;
|
||||
import org.geotools.feature.DefaultFeatureCollection;
|
||||
import org.geotools.feature.simple.SimpleFeatureBuilder;
|
||||
import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
|
||||
import org.geotools.geojson.geom.GeometryJSON;
|
||||
import org.geotools.referencing.CRS;
|
||||
import org.locationtech.jts.geom.Geometry;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class GeoToolsShpWriter implements ShpWriter {
|
||||
|
||||
// DBF 파일 한글 깨짐 방지를 위해 EUC-KR 사용
|
||||
private static final Charset DBF_CHARSET = Charset.forName("EUC-KR");
|
||||
|
||||
// GeoJSON 출력은 UTF-8
|
||||
private static final Charset GEOJSON_CHARSET = StandardCharsets.UTF_8;
|
||||
|
||||
// 좌표계: Korea 2000 / Central Belt 2010
|
||||
private static final String EPSG_5186 = "EPSG:5186";
|
||||
|
||||
/**
|
||||
* SHP 파일(.shp/.shx/.dbf/.prj)을 생성한다.
|
||||
* <p>
|
||||
* - shpBasePath를 기준으로 파일을 생성한다. 예) /Users/kim/export/shp/1_map_2021_2022 → 1_map_2021_2022.shp → 1_map_2021_2022.shx → 1_map_2021_2022.dbf → 1_map_2021_2022.prj
|
||||
* <p>
|
||||
* - geometry 타입은 첫 번째 유효 geometry 기준으로 스키마를 생성한다. - 좌표계는 EPSG:5186으로 설정하며, .prj 파일을 직접 생성한다.
|
||||
*
|
||||
* @param shpBasePath 확장자를 제외한 SHP 파일 기본 경로
|
||||
* @param rows 동일 그룹(stage, mapId, input1, input2)의 데이터 목록
|
||||
*/
|
||||
@Override
|
||||
public void writeShp(String shpBasePath, List<InferenceResultShpDto.Basic> rows) {
|
||||
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// SHP는 Geometry.class를 허용하지 않으므로
|
||||
// 첫 번째 유효 geometry의 "구체 타입"을 기준으로 스키마를 생성한다.
|
||||
Geometry firstGeom = firstNonNullGeometry(rows);
|
||||
if (firstGeom == null) {
|
||||
throw new IllegalArgumentException(
|
||||
"SHP 생성 실패: geometry가 전부 null 입니다. path=" + shpBasePath);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
Class<? extends Geometry> geomType =
|
||||
(Class<? extends Geometry>) firstGeom.getClass();
|
||||
|
||||
ShapefileDataStore dataStore = null;
|
||||
|
||||
try {
|
||||
File shpFile = new File(shpBasePath + ".shp");
|
||||
createDirectories(shpFile);
|
||||
|
||||
// EPSG:5186 CRS 로딩
|
||||
CoordinateReferenceSystem crs = CRS.decode(EPSG_5186, false);
|
||||
|
||||
// FeatureType(schema) 생성
|
||||
SimpleFeatureType schema = createSchema(geomType, crs);
|
||||
|
||||
// ShapefileDataStore 생성
|
||||
dataStore = createDataStore(shpFile, schema);
|
||||
|
||||
// FeatureCollection 생성
|
||||
DefaultFeatureCollection collection =
|
||||
buildFeatureCollection(schema, rows);
|
||||
|
||||
// 실제 SHP 파일에 feature 쓰기
|
||||
writeFeatures(dataStore, collection);
|
||||
|
||||
// .prj 파일 직접 생성 (EPSG:5186)
|
||||
writePrjFile(shpBasePath, crs);
|
||||
|
||||
log.info(
|
||||
"SHP 생성 완료: {} ({} features)",
|
||||
shpFile.getAbsolutePath(),
|
||||
collection.size()
|
||||
);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("SHP 생성 실패: " + shpBasePath, e);
|
||||
} finally {
|
||||
if (dataStore != null) {
|
||||
try {
|
||||
dataStore.dispose();
|
||||
} catch (Exception ignore) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* GeoJSON 파일(.geojson)을 생성한다.
|
||||
* <p>
|
||||
* - FeatureCollection 형태로 출력한다. - 최상단에 name / crs / properties를 포함한다. - 각 Feature는 polygon 단위로 생성된다. - geometry는 GeoTools GeometryJSON을 사용하여 직렬화한다.
|
||||
* <p>
|
||||
* GeoJSON 구조 예: { "type": "FeatureCollection", "name": "stage_input1_input2_mapId", "crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::5186" } },
|
||||
* "properties": { ... }, "features": [ ... ] }
|
||||
*
|
||||
* @param geoJsonPath 생성할 GeoJSON 파일의 전체 경로 (.geojson 포함)
|
||||
* @param rows 동일 그룹(stage, mapId, input1, input2)의 데이터 목록
|
||||
*/
|
||||
@Override
|
||||
public void writeGeoJson(String geoJsonPath, List<InferenceResultShpDto.Basic> rows) {
|
||||
|
||||
if (rows == null || rows.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
File geoJsonFile = new File(geoJsonPath);
|
||||
createDirectories(geoJsonFile);
|
||||
|
||||
// 그룹 공통 메타 정보는 첫 row 기준
|
||||
InferenceResultShpDto.Basic first = rows.get(0);
|
||||
|
||||
ObjectMapper om = new ObjectMapper();
|
||||
GeometryJSON gj = new GeometryJSON(15);
|
||||
|
||||
// FeatureCollection 루트
|
||||
ObjectNode root = om.createObjectNode();
|
||||
root.put("type", "FeatureCollection");
|
||||
|
||||
// name: stage_input1_input2_mapId
|
||||
String name = String.format(
|
||||
"%d_%d_%d_%d",
|
||||
first.getStage(),
|
||||
first.getInput1(),
|
||||
first.getInput2(),
|
||||
first.getMapId()
|
||||
);
|
||||
root.put("name", name);
|
||||
|
||||
// CRS (EPSG:5186)
|
||||
ObjectNode crs = om.createObjectNode();
|
||||
crs.put("type", "name");
|
||||
ObjectNode crsProps = om.createObjectNode();
|
||||
crsProps.put("name", "urn:ogc:def:crs:EPSG::5186");
|
||||
crs.set("properties", crsProps);
|
||||
root.set("crs", crs);
|
||||
|
||||
// 그룹 공통 properties
|
||||
ObjectNode groupProps = om.createObjectNode();
|
||||
groupProps.put("stage", first.getStage());
|
||||
groupProps.put("input1", first.getInput1());
|
||||
groupProps.put("input2", first.getInput2());
|
||||
groupProps.put("map_id", first.getMapId());
|
||||
// 학습서버 버전은 추후 추가
|
||||
// groupProps.put("m1", "v1.2222.251223121212");
|
||||
// groupProps.put("m2", "v2.211.251223121213");
|
||||
// groupProps.put("m3", "v3.233.251223121214");
|
||||
root.set("properties", groupProps);
|
||||
|
||||
// features 배열
|
||||
ArrayNode features = om.createArrayNode();
|
||||
|
||||
for (InferenceResultShpDto.Basic dto : rows) {
|
||||
if (dto.getGeometry() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
ObjectNode feature = om.createObjectNode();
|
||||
feature.put("type", "Feature");
|
||||
|
||||
// feature properties
|
||||
ObjectNode p = om.createObjectNode();
|
||||
p.put("polygon_id",
|
||||
dto.getUuid() != null ? dto.getUuid().toString() : null);
|
||||
if (dto.getCdProb() != null) {
|
||||
p.put("cd_prob", dto.getCdProb());
|
||||
}
|
||||
if (dto.getInput1() != null) {
|
||||
p.put("input1", dto.getInput1());
|
||||
}
|
||||
if (dto.getInput2() != null) {
|
||||
p.put("input2", dto.getInput2());
|
||||
}
|
||||
if (dto.getMapId() != null) {
|
||||
p.put("map_id", dto.getMapId());
|
||||
}
|
||||
if (dto.getArea() != null) {
|
||||
p.put("area", dto.getArea());
|
||||
}
|
||||
p.put("before_c", dto.getBeforeClass());
|
||||
if (dto.getBeforeProbability() != null) {
|
||||
p.put("before_p", dto.getBeforeProbability());
|
||||
}
|
||||
p.put("after_c", dto.getAfterClass());
|
||||
if (dto.getAfterProbability() != null) {
|
||||
p.put("after_p", dto.getAfterProbability());
|
||||
}
|
||||
|
||||
feature.set("properties", p);
|
||||
|
||||
// geometry
|
||||
String geomJson = gj.toString(dto.getGeometry());
|
||||
JsonNode geomNode = om.readTree(geomJson);
|
||||
feature.set("geometry", geomNode);
|
||||
|
||||
features.add(feature);
|
||||
}
|
||||
|
||||
root.set("features", features);
|
||||
|
||||
// 파일 쓰기
|
||||
try (OutputStreamWriter w =
|
||||
new OutputStreamWriter(
|
||||
new FileOutputStream(geoJsonFile),
|
||||
GEOJSON_CHARSET
|
||||
)) {
|
||||
om.writerWithDefaultPrettyPrinter().writeValue(w, root);
|
||||
}
|
||||
|
||||
log.info(
|
||||
"GeoJSON 생성 완료: {} ({} features)",
|
||||
geoJsonFile.getAbsolutePath(),
|
||||
features.size()
|
||||
);
|
||||
|
||||
} catch (Exception e) {
|
||||
throw new RuntimeException("GeoJSON 생성 실패: " + geoJsonPath, e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rows 목록에서 첫 번째로 발견되는 non-null Geometry를 반환한다.
|
||||
* <p>
|
||||
* - SHP 스키마 생성 시 geometry 타입 결정을 위해 사용된다.
|
||||
*
|
||||
* @param rows DTO 목록
|
||||
* @return 첫 번째 non-null Geometry, 없으면 null
|
||||
*/
|
||||
private Geometry firstNonNullGeometry(List<InferenceResultShpDto.Basic> rows) {
|
||||
for (InferenceResultShpDto.Basic r : rows) {
|
||||
if (r != null && r.getGeometry() != null) {
|
||||
return r.getGeometry();
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* SHP 파일에 사용할 SimpleFeatureType(schema)를 생성한다.
|
||||
* <p>
|
||||
* - geometry 컬럼은 반드시 첫 번째 컬럼이어야 한다. - DBF 컬럼은 SHP 제약(컬럼명 10자, 길이 제한)을 고려한다.
|
||||
*
|
||||
* @param geomType geometry의 구체 타입 (Polygon, MultiPolygon 등)
|
||||
* @param crs 좌표계(EPSG:5186)
|
||||
* @return SimpleFeatureType
|
||||
*/
|
||||
private SimpleFeatureType createSchema(
|
||||
Class<? extends Geometry> geomType,
|
||||
CoordinateReferenceSystem crs
|
||||
) {
|
||||
SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder();
|
||||
b.setName("inference_result");
|
||||
b.setCRS(crs);
|
||||
|
||||
// geometry는 반드시 첫 컬럼
|
||||
b.add("the_geom", geomType);
|
||||
|
||||
// DBF 컬럼 정의 (10자 제한 고려)
|
||||
b.length(36).add("poly_id", String.class);
|
||||
b.add("cd_prob", Double.class);
|
||||
b.add("input1", Integer.class);
|
||||
b.add("input2", Integer.class);
|
||||
b.add("map_id", Long.class);
|
||||
b.add("area", Double.class);
|
||||
b.length(20).add("before_c", String.class);
|
||||
b.add("before_p", Double.class);
|
||||
b.length(20).add("after_c", String.class);
|
||||
b.add("after_p", Double.class);
|
||||
|
||||
return b.buildFeatureType();
|
||||
}
|
||||
|
||||
/**
|
||||
* ShapefileDataStore를 생성하고 스키마를 등록한다.
|
||||
* <p>
|
||||
* - DBF 파일 인코딩은 EUC-KR로 설정한다. - spatial index(.qix)를 생성한다.
|
||||
*
|
||||
* @param shpFile SHP 파일 객체
|
||||
* @param schema SimpleFeatureType
|
||||
* @return 생성된 ShapefileDataStore
|
||||
*/
|
||||
private ShapefileDataStore createDataStore(
|
||||
File shpFile,
|
||||
SimpleFeatureType schema
|
||||
) throws Exception {
|
||||
|
||||
Map<String, Serializable> params = new HashMap<>();
|
||||
params.put("url", shpFile.toURI().toURL());
|
||||
params.put("create spatial index", Boolean.TRUE);
|
||||
|
||||
ShapefileDataStoreFactory factory = new ShapefileDataStoreFactory();
|
||||
ShapefileDataStore dataStore =
|
||||
(ShapefileDataStore) factory.createNewDataStore(params);
|
||||
|
||||
dataStore.setCharset(DBF_CHARSET);
|
||||
dataStore.createSchema(schema);
|
||||
|
||||
return dataStore;
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO 목록을 SimpleFeatureCollection으로 변환한다.
|
||||
* <p>
|
||||
* - DTO 1건당 Feature 1개 생성 - geometry가 null인 데이터는 제외한다.
|
||||
*
|
||||
* @param schema FeatureType
|
||||
* @param rows DTO 목록
|
||||
* @return DefaultFeatureCollection
|
||||
*/
|
||||
private DefaultFeatureCollection buildFeatureCollection(
|
||||
SimpleFeatureType schema,
|
||||
List<InferenceResultShpDto.Basic> rows
|
||||
) {
|
||||
DefaultFeatureCollection collection = new DefaultFeatureCollection();
|
||||
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(schema);
|
||||
|
||||
for (InferenceResultShpDto.Basic dto : rows) {
|
||||
if (dto == null || dto.getGeometry() == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
builder.add(dto.getGeometry());
|
||||
builder.add(dto.getUuid() != null ? dto.getUuid().toString() : null);
|
||||
builder.add(dto.getCdProb() != null ? dto.getCdProb().doubleValue() : null);
|
||||
builder.add(dto.getInput1());
|
||||
builder.add(dto.getInput2());
|
||||
builder.add(dto.getMapId());
|
||||
builder.add(dto.getArea() != null ? dto.getArea().doubleValue() : null);
|
||||
builder.add(dto.getBeforeClass());
|
||||
builder.add(dto.getBeforeProbability() != null
|
||||
? dto.getBeforeProbability().doubleValue()
|
||||
: null);
|
||||
builder.add(dto.getAfterClass());
|
||||
builder.add(dto.getAfterProbability() != null
|
||||
? dto.getAfterProbability().doubleValue()
|
||||
: null);
|
||||
|
||||
SimpleFeature feature = builder.buildFeature(null);
|
||||
collection.add(feature);
|
||||
builder.reset();
|
||||
}
|
||||
|
||||
return collection;
|
||||
}
|
||||
|
||||
/**
|
||||
* FeatureCollection을 SHP 파일에 실제로 기록한다.
|
||||
*
|
||||
* @param dataStore ShapefileDataStore
|
||||
* @param collection FeatureCollection
|
||||
*/
|
||||
private void writeFeatures(
|
||||
ShapefileDataStore dataStore,
|
||||
DefaultFeatureCollection collection
|
||||
) throws Exception {
|
||||
|
||||
String typeName = dataStore.getTypeNames()[0];
|
||||
SimpleFeatureSource featureSource =
|
||||
dataStore.getFeatureSource(typeName);
|
||||
|
||||
if (!(featureSource instanceof SimpleFeatureStore store)) {
|
||||
throw new IllegalStateException("FeatureStore 생성 실패");
|
||||
}
|
||||
|
||||
store.setTransaction(Transaction.AUTO_COMMIT);
|
||||
store.addFeatures(collection);
|
||||
store.getTransaction().commit();
|
||||
}
|
||||
|
||||
/**
|
||||
* SHP 좌표계 정보를 담은 .prj 파일을 생성한다.
|
||||
*
|
||||
* @param shpBasePath SHP 기본 경로 (확장자 제외)
|
||||
* @param crs 좌표계(EPSG:5186)
|
||||
*/
|
||||
private void writePrjFile(
|
||||
String shpBasePath,
|
||||
CoordinateReferenceSystem crs
|
||||
) throws Exception {
|
||||
|
||||
File prjFile = new File(shpBasePath + ".prj");
|
||||
createDirectories(prjFile);
|
||||
|
||||
Files.writeString(
|
||||
prjFile.toPath(),
|
||||
crs.toWKT(),
|
||||
StandardCharsets.UTF_8
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일이 생성될 디렉토리가 없으면 생성한다.
|
||||
*
|
||||
* @param file 생성 대상 파일
|
||||
*/
|
||||
private void createDirectories(File file) throws Exception {
|
||||
File parent = file.getParentFile();
|
||||
if (parent != null) {
|
||||
Files.createDirectories(parent.toPath());
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
package com.kamco.cd.kamcoback.inference.service;
|
||||
|
||||
import com.kamco.cd.kamcoback.inference.ShpWriter;
|
||||
import com.kamco.cd.kamcoback.inference.dto.InferenceResultShpDto;
|
||||
import com.kamco.cd.kamcoback.postgres.core.InferenceResultShpCoreService;
|
||||
import com.kamco.cd.kamcoback.postgres.core.InferenceResultShpCoreService.ShpKey;
|
||||
import java.util.List;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
@Transactional(readOnly = true)
|
||||
public class InferenceResultShpService {
|
||||
|
||||
private final InferenceResultShpCoreService coreService;
|
||||
private final ShpWriter shpWriter;
|
||||
|
||||
public void saveInferenceResultData() {
|
||||
|
||||
coreService.streamGrouped(1000, (key, entities) -> {
|
||||
|
||||
// Entity -> DTO
|
||||
List<InferenceResultShpDto.Basic> dtoList =
|
||||
entities.stream().map(InferenceResultShpDto.Basic::from).toList();
|
||||
|
||||
flushGroup(key, dtoList);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 파일명 및 파일 경로
|
||||
*
|
||||
* @param key
|
||||
* @param dtoList
|
||||
*/
|
||||
private void flushGroup(ShpKey key, List<InferenceResultShpDto.Basic> dtoList) {
|
||||
|
||||
// TODO 경로 정해지면 수정해야함
|
||||
String baseDir = System.getProperty("user.home") + "/export";
|
||||
|
||||
// 파일명 stage_input1_input2_mapId
|
||||
String baseName = String.format(
|
||||
"%d_%d_%d_%d",
|
||||
key.stage(), key.mapId(), key.input1(), key.input2()
|
||||
);
|
||||
|
||||
String shpBasePath = baseDir + "/shp/" + baseName; // 확장자 없이
|
||||
String geoJsonPath = baseDir + "/geojson/" + baseName + ".geojson";
|
||||
|
||||
// shp: .shp/.shx/.dbf 생성
|
||||
shpWriter.writeShp(shpBasePath, dtoList);
|
||||
|
||||
// geojson: .geojson 생성
|
||||
shpWriter.writeGeoJson(geoJsonPath, dtoList);
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user