|
|
|
|
@@ -27,43 +27,57 @@ public class GeoJsonDataService {
|
|
|
|
|
private final GeoJsonReader geoJsonReader = new GeoJsonReader();
|
|
|
|
|
|
|
|
|
|
/** GeoJSON 파일들을 데이터베이스에 저장 */
|
|
|
|
|
@Transactional
|
|
|
|
|
public List<Long> processGeoJsonFiles(
|
|
|
|
|
Map<String, String> geoJsonContents, String archiveFileName) {
|
|
|
|
|
Map<String, String> geoJsonContents, String archiveFileName) {
|
|
|
|
|
List<Long> savedIds = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
log.info("GeoJSON 파일 처리 시작: {} ({}개 파일)", archiveFileName, geoJsonContents.size());
|
|
|
|
|
|
|
|
|
|
// 개별 파일별로 독립적인 트랜잭션으로 처리
|
|
|
|
|
for (Map.Entry<String, String> entry : geoJsonContents.entrySet()) {
|
|
|
|
|
String fileName = entry.getKey();
|
|
|
|
|
String geoJsonContent = entry.getValue();
|
|
|
|
|
|
|
|
|
|
try {
|
|
|
|
|
Long savedId = processGeoJsonFile(fileName, geoJsonContent, archiveFileName);
|
|
|
|
|
Long savedId = processGeoJsonFileWithTransaction(fileName, geoJsonContent, archiveFileName);
|
|
|
|
|
if (savedId != null) {
|
|
|
|
|
savedIds.add(savedId);
|
|
|
|
|
log.debug("GeoJSON 파일 저장 성공: {} (ID: {})", fileName, savedId);
|
|
|
|
|
|
|
|
|
|
// 학습 모델 결과 파일인지 확인하여 geometry 데이터 처리
|
|
|
|
|
if (isLearningModelResult(fileName, geoJsonContent)) {
|
|
|
|
|
processLearningModelGeometry(savedId, geoJsonContent, fileName);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("GeoJSON 파일 처리 실패: {}", fileName, e);
|
|
|
|
|
// 개별 파일 처리 실패는 전체 처리를 중단시키지 않음
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 메모리 정리
|
|
|
|
|
System.gc();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
log.info(
|
|
|
|
|
"GeoJSON 파일 처리 완료: {} (성공: {}개, 전체: {}개)",
|
|
|
|
|
archiveFileName,
|
|
|
|
|
savedIds.size(),
|
|
|
|
|
geoJsonContents.size());
|
|
|
|
|
"GeoJSON 파일 처리 완료: {} (성공: {}개, 전체: {}개)",
|
|
|
|
|
archiveFileName,
|
|
|
|
|
savedIds.size(),
|
|
|
|
|
geoJsonContents.size());
|
|
|
|
|
|
|
|
|
|
return savedIds;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 개별 파일을 별도 트랜잭션으로 처리 */
|
|
|
|
|
@Transactional
|
|
|
|
|
public Long processGeoJsonFileWithTransaction(
|
|
|
|
|
String fileName, String geoJsonContent, String archiveFileName) {
|
|
|
|
|
try {
|
|
|
|
|
Long savedId = processGeoJsonFile(fileName, geoJsonContent, archiveFileName);
|
|
|
|
|
if (savedId != null && isLearningModelResult(fileName, geoJsonContent)) {
|
|
|
|
|
processLearningModelGeometryOptimized(savedId, geoJsonContent, fileName);
|
|
|
|
|
}
|
|
|
|
|
return savedId;
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("파일 처리 중 트랜잭션 에러: {}", fileName, e);
|
|
|
|
|
throw e; // 트랜잭션 롤백을 위해 재throw
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 개별 GeoJSON 파일을 MapSheetLearnDataEntity로 변환하여 저장 */
|
|
|
|
|
private Long processGeoJsonFile(String fileName, String geoJsonContent, String archiveFileName) {
|
|
|
|
|
try {
|
|
|
|
|
@@ -74,7 +88,7 @@ public class GeoJsonDataService {
|
|
|
|
|
// 파일이 이미 처리되었는지 확인
|
|
|
|
|
String dataPath = generateDataPath(archiveFileName, fileName);
|
|
|
|
|
Optional<MapSheetLearnDataEntity> existingData =
|
|
|
|
|
mapSheetLearnDataRepository.findByDataPath(dataPath);
|
|
|
|
|
mapSheetLearnDataRepository.findByDataPath(dataPath);
|
|
|
|
|
|
|
|
|
|
if (existingData.isPresent()) {
|
|
|
|
|
log.warn("이미 처리된 파일입니다: {}", dataPath);
|
|
|
|
|
@@ -83,7 +97,7 @@ public class GeoJsonDataService {
|
|
|
|
|
|
|
|
|
|
// 새 엔티티 생성 및 저장
|
|
|
|
|
MapSheetLearnDataEntity entity =
|
|
|
|
|
createMapSheetLearnDataEntity(fileName, geoJsonContent, archiveFileName, geoJsonNode);
|
|
|
|
|
createMapSheetLearnDataEntity(fileName, geoJsonContent, archiveFileName, geoJsonNode);
|
|
|
|
|
MapSheetLearnDataEntity savedEntity = mapSheetLearnDataRepository.save(entity);
|
|
|
|
|
|
|
|
|
|
return savedEntity.getId();
|
|
|
|
|
@@ -108,7 +122,7 @@ public class GeoJsonDataService {
|
|
|
|
|
|
|
|
|
|
/** MapSheetLearnDataEntity 생성 */
|
|
|
|
|
private MapSheetLearnDataEntity createMapSheetLearnDataEntity(
|
|
|
|
|
String fileName, String geoJsonContent, String archiveFileName, JsonNode geoJsonNode) {
|
|
|
|
|
String fileName, String geoJsonContent, String archiveFileName, JsonNode geoJsonNode) {
|
|
|
|
|
|
|
|
|
|
MapSheetLearnDataEntity entity = new MapSheetLearnDataEntity();
|
|
|
|
|
|
|
|
|
|
@@ -273,8 +287,8 @@ public class GeoJsonDataService {
|
|
|
|
|
JsonNode properties = firstFeature.get("properties");
|
|
|
|
|
// 학습 모델 특화 필드 확인
|
|
|
|
|
return properties.has("cd_prob")
|
|
|
|
|
|| properties.has("class")
|
|
|
|
|
|| (properties.has("before") && properties.has("after"));
|
|
|
|
|
|| properties.has("class")
|
|
|
|
|
|| (properties.has("before") && properties.has("after"));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
@@ -285,9 +299,9 @@ public class GeoJsonDataService {
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 학습 모델 결과의 geometry 데이터 처리 */
|
|
|
|
|
@Transactional
|
|
|
|
|
public void processLearningModelGeometry(Long dataUid, String geoJsonContent, String fileName) {
|
|
|
|
|
/** 학습 모델 결과의 geometry 데이터 처리 - 최적화된 배치 처리 */
|
|
|
|
|
public void processLearningModelGeometryOptimized(
|
|
|
|
|
Long dataUid, String geoJsonContent, String fileName) {
|
|
|
|
|
try {
|
|
|
|
|
log.info("학습 모델 geometry 데이터 처리 시작: {} (dataUid: {})", fileName, dataUid);
|
|
|
|
|
|
|
|
|
|
@@ -295,7 +309,7 @@ public class GeoJsonDataService {
|
|
|
|
|
|
|
|
|
|
// 메타데이터 추출
|
|
|
|
|
String mapSheetName =
|
|
|
|
|
rootNode.has("name") ? rootNode.get("name").asText() : fileName.replace(".geojson", "");
|
|
|
|
|
rootNode.has("name") ? rootNode.get("name").asText() : fileName.replace(".geojson", "");
|
|
|
|
|
|
|
|
|
|
// 파일명에서 연도 및 지도번호 추출 (캠코_2021_2022_35813023)
|
|
|
|
|
String[] parts = mapSheetName.split("_");
|
|
|
|
|
@@ -318,36 +332,75 @@ public class GeoJsonDataService {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
List<MapSheetLearnDataGeomEntity> geomEntities = new ArrayList<>();
|
|
|
|
|
// 소규모 배치로 나누어 처리
|
|
|
|
|
int totalFeatures = features.size();
|
|
|
|
|
int batchSize = 10; // 작은 배치 크기
|
|
|
|
|
int processedCount = 0;
|
|
|
|
|
|
|
|
|
|
for (JsonNode feature : features) {
|
|
|
|
|
try {
|
|
|
|
|
MapSheetLearnDataGeomEntity geomEntity =
|
|
|
|
|
log.info("총 {}개 feature를 {}개씩 배치로 나누어 처리", totalFeatures, batchSize);
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < totalFeatures; i += batchSize) {
|
|
|
|
|
int endIndex = Math.min(i + batchSize, totalFeatures);
|
|
|
|
|
List<MapSheetLearnDataGeomEntity> batch = new ArrayList<>();
|
|
|
|
|
|
|
|
|
|
for (int j = i; j < endIndex; j++) {
|
|
|
|
|
try {
|
|
|
|
|
JsonNode feature = features.get(j);
|
|
|
|
|
MapSheetLearnDataGeomEntity geomEntity =
|
|
|
|
|
createGeometryEntity(feature, dataUid, beforeYear, afterYear, mapSheetNum);
|
|
|
|
|
if (geomEntity != null) {
|
|
|
|
|
geomEntities.add(geomEntity);
|
|
|
|
|
processedCount++;
|
|
|
|
|
if (geomEntity != null) {
|
|
|
|
|
batch.add(geomEntity);
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.warn("Feature geometry 처리 실패 (feature {}): {}", j + 1, e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.warn("Feature geometry 처리 실패 (feature {}): {}", processedCount, e.getMessage());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 배치별 저장
|
|
|
|
|
if (!batch.isEmpty()) {
|
|
|
|
|
saveBatchGeometry(batch);
|
|
|
|
|
processedCount += batch.size();
|
|
|
|
|
log.debug("배치 {}-{} 처리 완료 ({}개)", i + 1, endIndex, batch.size());
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 메모리 정리
|
|
|
|
|
batch.clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 배치 저장
|
|
|
|
|
if (!geomEntities.isEmpty()) {
|
|
|
|
|
mapSheetLearnDataGeomRepository.saveAll(geomEntities);
|
|
|
|
|
log.info("학습 모델 geometry 데이터 저장 완료: {} ({}개 feature)", fileName, geomEntities.size());
|
|
|
|
|
}
|
|
|
|
|
log.info("학습 모델 geometry 데이터 저장 완료: {} ({}개 feature)", fileName, processedCount);
|
|
|
|
|
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("학습 모델 geometry 데이터 처리 실패: {}", fileName, e);
|
|
|
|
|
throw new RuntimeException("Geometry 처리 실패: " + fileName, e);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 배치별 geometry 저장 - 별도 트랜잭션 */
|
|
|
|
|
@Transactional(propagation = org.springframework.transaction.annotation.Propagation.REQUIRES_NEW)
|
|
|
|
|
public void saveBatchGeometry(List<MapSheetLearnDataGeomEntity> batch) {
|
|
|
|
|
try {
|
|
|
|
|
if (batch == null || batch.isEmpty()) {
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
mapSheetLearnDataGeomRepository.saveAll(batch);
|
|
|
|
|
} catch (Exception e) {
|
|
|
|
|
log.error("배치 geometry 저장 실패: {}", e.getMessage());
|
|
|
|
|
// 개별 저장 시도
|
|
|
|
|
for (MapSheetLearnDataGeomEntity entity : batch) {
|
|
|
|
|
try {
|
|
|
|
|
if (entity != null) {
|
|
|
|
|
mapSheetLearnDataGeomRepository.save(entity);
|
|
|
|
|
}
|
|
|
|
|
} catch (Exception individualError) {
|
|
|
|
|
log.warn("개별 geometry 저장 실패: {}", individualError.getMessage());
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/** 개별 feature에서 geometry entity 생성 */
|
|
|
|
|
private MapSheetLearnDataGeomEntity createGeometryEntity(
|
|
|
|
|
JsonNode feature, Long dataUid, String beforeYear, String afterYear, String mapSheetNum) {
|
|
|
|
|
JsonNode feature, Long dataUid, String beforeYear, String afterYear, String mapSheetNum) {
|
|
|
|
|
|
|
|
|
|
JsonNode properties = feature.get("properties");
|
|
|
|
|
JsonNode geometry = feature.get("geometry");
|
|
|
|
|
|