|
|
|
|
@@ -48,13 +48,14 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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 파일을 직접 생성한다.
|
|
|
|
|
*
|
|
|
|
|
* <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)의 데이터 목록
|
|
|
|
|
* @param rows 동일 그룹(stage, mapId, input1, input2)의 데이터 목록
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void writeShp(String shpBasePath, List<InferenceResultShpDto.Basic> rows) {
|
|
|
|
|
@@ -67,13 +68,11 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
// 첫 번째 유효 geometry의 "구체 타입"을 기준으로 스키마를 생성한다.
|
|
|
|
|
Geometry firstGeom = firstNonNullGeometry(rows);
|
|
|
|
|
if (firstGeom == null) {
|
|
|
|
|
throw new IllegalArgumentException(
|
|
|
|
|
"SHP 생성 실패: geometry가 전부 null 입니다. path=" + shpBasePath);
|
|
|
|
|
throw new IllegalArgumentException("SHP 생성 실패: geometry가 전부 null 입니다. path=" + shpBasePath);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@SuppressWarnings("unchecked")
|
|
|
|
|
Class<? extends Geometry> geomType =
|
|
|
|
|
(Class<? extends Geometry>) firstGeom.getClass();
|
|
|
|
|
Class<? extends Geometry> geomType = (Class<? extends Geometry>) firstGeom.getClass();
|
|
|
|
|
|
|
|
|
|
ShapefileDataStore dataStore = null;
|
|
|
|
|
|
|
|
|
|
@@ -91,8 +90,7 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
dataStore = createDataStore(shpFile, schema);
|
|
|
|
|
|
|
|
|
|
// FeatureCollection 생성
|
|
|
|
|
DefaultFeatureCollection collection =
|
|
|
|
|
buildFeatureCollection(schema, rows);
|
|
|
|
|
DefaultFeatureCollection collection = buildFeatureCollection(schema, rows);
|
|
|
|
|
|
|
|
|
|
// 실제 SHP 파일에 feature 쓰기
|
|
|
|
|
writeFeatures(dataStore, collection);
|
|
|
|
|
@@ -100,11 +98,7 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
// .prj 파일 직접 생성 (EPSG:5186)
|
|
|
|
|
writePrjFile(shpBasePath, crs);
|
|
|
|
|
|
|
|
|
|
log.info(
|
|
|
|
|
"SHP 생성 완료: {} ({} features)",
|
|
|
|
|
shpFile.getAbsolutePath(),
|
|
|
|
|
collection.size()
|
|
|
|
|
);
|
|
|
|
|
log.info("SHP 생성 완료: {} ({} features)", shpFile.getAbsolutePath(), collection.size());
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new RuntimeException("SHP 생성 실패: " + shpBasePath, e);
|
|
|
|
|
@@ -120,14 +114,16 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 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": [ ... ] }
|
|
|
|
|
*
|
|
|
|
|
* <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)의 데이터 목록
|
|
|
|
|
* @param rows 동일 그룹(stage, mapId, input1, input2)의 데이터 목록
|
|
|
|
|
*/
|
|
|
|
|
@Override
|
|
|
|
|
public void writeGeoJson(String geoJsonPath, List<InferenceResultShpDto.Basic> rows) {
|
|
|
|
|
@@ -151,13 +147,10 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
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()
|
|
|
|
|
);
|
|
|
|
|
String name =
|
|
|
|
|
String.format(
|
|
|
|
|
"%d_%d_%d_%d",
|
|
|
|
|
first.getStage(), first.getInput1(), first.getInput2(), first.getMapId());
|
|
|
|
|
root.put("name", name);
|
|
|
|
|
|
|
|
|
|
// CRS (EPSG:5186)
|
|
|
|
|
@@ -175,9 +168,9 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
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");
|
|
|
|
|
// groupProps.put("m1", "v1.2222.251223121212");
|
|
|
|
|
// groupProps.put("m2", "v2.211.251223121213");
|
|
|
|
|
// groupProps.put("m3", "v3.233.251223121214");
|
|
|
|
|
root.set("properties", groupProps);
|
|
|
|
|
|
|
|
|
|
// features 배열
|
|
|
|
|
@@ -193,8 +186,7 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
|
|
|
|
|
// feature properties
|
|
|
|
|
ObjectNode p = om.createObjectNode();
|
|
|
|
|
p.put("polygon_id",
|
|
|
|
|
dto.getUuid() != null ? dto.getUuid().toString() : null);
|
|
|
|
|
p.put("polygon_id", dto.getUuid() != null ? dto.getUuid().toString() : null);
|
|
|
|
|
if (dto.getCdProb() != null) {
|
|
|
|
|
p.put("cd_prob", dto.getCdProb());
|
|
|
|
|
}
|
|
|
|
|
@@ -233,18 +225,11 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
|
|
|
|
|
// 파일 쓰기
|
|
|
|
|
try (OutputStreamWriter w =
|
|
|
|
|
new OutputStreamWriter(
|
|
|
|
|
new FileOutputStream(geoJsonFile),
|
|
|
|
|
GEOJSON_CHARSET
|
|
|
|
|
)) {
|
|
|
|
|
new OutputStreamWriter(new FileOutputStream(geoJsonFile), GEOJSON_CHARSET)) {
|
|
|
|
|
om.writerWithDefaultPrettyPrinter().writeValue(w, root);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.info(
|
|
|
|
|
"GeoJSON 생성 완료: {} ({} features)",
|
|
|
|
|
geoJsonFile.getAbsolutePath(),
|
|
|
|
|
features.size()
|
|
|
|
|
);
|
|
|
|
|
log.info("GeoJSON 생성 완료: {} ({} features)", geoJsonFile.getAbsolutePath(), features.size());
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
throw new RuntimeException("GeoJSON 생성 실패: " + geoJsonPath, e);
|
|
|
|
|
@@ -253,8 +238,8 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* rows 목록에서 첫 번째로 발견되는 non-null Geometry를 반환한다.
|
|
|
|
|
* <p>
|
|
|
|
|
* - SHP 스키마 생성 시 geometry 타입 결정을 위해 사용된다.
|
|
|
|
|
*
|
|
|
|
|
* <p>- SHP 스키마 생성 시 geometry 타입 결정을 위해 사용된다.
|
|
|
|
|
*
|
|
|
|
|
* @param rows DTO 목록
|
|
|
|
|
* @return 첫 번째 non-null Geometry, 없으면 null
|
|
|
|
|
@@ -270,17 +255,15 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* SHP 파일에 사용할 SimpleFeatureType(schema)를 생성한다.
|
|
|
|
|
* <p>
|
|
|
|
|
* - geometry 컬럼은 반드시 첫 번째 컬럼이어야 한다. - DBF 컬럼은 SHP 제약(컬럼명 10자, 길이 제한)을 고려한다.
|
|
|
|
|
*
|
|
|
|
|
* <p>- geometry 컬럼은 반드시 첫 번째 컬럼이어야 한다. - DBF 컬럼은 SHP 제약(컬럼명 10자, 길이 제한)을 고려한다.
|
|
|
|
|
*
|
|
|
|
|
* @param geomType geometry의 구체 타입 (Polygon, MultiPolygon 등)
|
|
|
|
|
* @param crs 좌표계(EPSG:5186)
|
|
|
|
|
* @param crs 좌표계(EPSG:5186)
|
|
|
|
|
* @return SimpleFeatureType
|
|
|
|
|
*/
|
|
|
|
|
private SimpleFeatureType createSchema(
|
|
|
|
|
Class<? extends Geometry> geomType,
|
|
|
|
|
CoordinateReferenceSystem crs
|
|
|
|
|
) {
|
|
|
|
|
Class<? extends Geometry> geomType, CoordinateReferenceSystem crs) {
|
|
|
|
|
SimpleFeatureTypeBuilder b = new SimpleFeatureTypeBuilder();
|
|
|
|
|
b.setName("inference_result");
|
|
|
|
|
b.setCRS(crs);
|
|
|
|
|
@@ -305,25 +288,22 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* ShapefileDataStore를 생성하고 스키마를 등록한다.
|
|
|
|
|
* <p>
|
|
|
|
|
* - DBF 파일 인코딩은 EUC-KR로 설정한다. - spatial index(.qix)를 생성한다.
|
|
|
|
|
*
|
|
|
|
|
* <p>- DBF 파일 인코딩은 EUC-KR로 설정한다. - spatial index(.qix)를 생성한다.
|
|
|
|
|
*
|
|
|
|
|
* @param shpFile SHP 파일 객체
|
|
|
|
|
* @param schema SimpleFeatureType
|
|
|
|
|
* @param schema SimpleFeatureType
|
|
|
|
|
* @return 생성된 ShapefileDataStore
|
|
|
|
|
*/
|
|
|
|
|
private ShapefileDataStore createDataStore(
|
|
|
|
|
File shpFile,
|
|
|
|
|
SimpleFeatureType schema
|
|
|
|
|
) throws Exception {
|
|
|
|
|
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);
|
|
|
|
|
ShapefileDataStore dataStore = (ShapefileDataStore) factory.createNewDataStore(params);
|
|
|
|
|
|
|
|
|
|
dataStore.setCharset(DBF_CHARSET);
|
|
|
|
|
dataStore.createSchema(schema);
|
|
|
|
|
@@ -333,17 +313,15 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* DTO 목록을 SimpleFeatureCollection으로 변환한다.
|
|
|
|
|
* <p>
|
|
|
|
|
* - DTO 1건당 Feature 1개 생성 - geometry가 null인 데이터는 제외한다.
|
|
|
|
|
*
|
|
|
|
|
* <p>- DTO 1건당 Feature 1개 생성 - geometry가 null인 데이터는 제외한다.
|
|
|
|
|
*
|
|
|
|
|
* @param schema FeatureType
|
|
|
|
|
* @param rows DTO 목록
|
|
|
|
|
* @param rows DTO 목록
|
|
|
|
|
* @return DefaultFeatureCollection
|
|
|
|
|
*/
|
|
|
|
|
private DefaultFeatureCollection buildFeatureCollection(
|
|
|
|
|
SimpleFeatureType schema,
|
|
|
|
|
List<InferenceResultShpDto.Basic> rows
|
|
|
|
|
) {
|
|
|
|
|
SimpleFeatureType schema, List<InferenceResultShpDto.Basic> rows) {
|
|
|
|
|
DefaultFeatureCollection collection = new DefaultFeatureCollection();
|
|
|
|
|
SimpleFeatureBuilder builder = new SimpleFeatureBuilder(schema);
|
|
|
|
|
|
|
|
|
|
@@ -360,13 +338,11 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
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.getBeforeProbability() != null ? dto.getBeforeProbability().doubleValue() : null);
|
|
|
|
|
builder.add(dto.getAfterClass());
|
|
|
|
|
builder.add(dto.getAfterProbability() != null
|
|
|
|
|
? dto.getAfterProbability().doubleValue()
|
|
|
|
|
: null);
|
|
|
|
|
builder.add(
|
|
|
|
|
dto.getAfterProbability() != null ? dto.getAfterProbability().doubleValue() : null);
|
|
|
|
|
|
|
|
|
|
SimpleFeature feature = builder.buildFeature(null);
|
|
|
|
|
collection.add(feature);
|
|
|
|
|
@@ -379,17 +355,14 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
/**
|
|
|
|
|
* FeatureCollection을 SHP 파일에 실제로 기록한다.
|
|
|
|
|
*
|
|
|
|
|
* @param dataStore ShapefileDataStore
|
|
|
|
|
* @param dataStore ShapefileDataStore
|
|
|
|
|
* @param collection FeatureCollection
|
|
|
|
|
*/
|
|
|
|
|
private void writeFeatures(
|
|
|
|
|
ShapefileDataStore dataStore,
|
|
|
|
|
DefaultFeatureCollection collection
|
|
|
|
|
) throws Exception {
|
|
|
|
|
private void writeFeatures(ShapefileDataStore dataStore, DefaultFeatureCollection collection)
|
|
|
|
|
throws Exception {
|
|
|
|
|
|
|
|
|
|
String typeName = dataStore.getTypeNames()[0];
|
|
|
|
|
SimpleFeatureSource featureSource =
|
|
|
|
|
dataStore.getFeatureSource(typeName);
|
|
|
|
|
SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);
|
|
|
|
|
|
|
|
|
|
if (!(featureSource instanceof SimpleFeatureStore store)) {
|
|
|
|
|
throw new IllegalStateException("FeatureStore 생성 실패");
|
|
|
|
|
@@ -404,21 +377,14 @@ public class GeoToolsShpWriter implements ShpWriter {
|
|
|
|
|
* SHP 좌표계 정보를 담은 .prj 파일을 생성한다.
|
|
|
|
|
*
|
|
|
|
|
* @param shpBasePath SHP 기본 경로 (확장자 제외)
|
|
|
|
|
* @param crs 좌표계(EPSG:5186)
|
|
|
|
|
* @param crs 좌표계(EPSG:5186)
|
|
|
|
|
*/
|
|
|
|
|
private void writePrjFile(
|
|
|
|
|
String shpBasePath,
|
|
|
|
|
CoordinateReferenceSystem crs
|
|
|
|
|
) throws Exception {
|
|
|
|
|
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
|
|
|
|
|
);
|
|
|
|
|
Files.writeString(prjFile.toPath(), crs.toWKT(), StandardCharsets.UTF_8);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|