반응형
__스프링에서 shp 파일을 테이블로 생성 by geotools __
shp to postgis(postgres) table by spring__
전자정부 + 자바 1.8 + 메이븐 기준
원하는 로직
shp
파일들어있는 zip 파일들을 받는다- 해당
shp
파일을 읽고 postgres (postgis) 테이블로 발행한다
pom.xml (egov 기준)
- geotools 가져오기 위해서는
pom.xml
에서 repositories 주소를 추가해줘야함
<properties>
<spring.maven.artifact.version>4.2.4.RELEASE</spring.maven.artifact.version>
<egovframework.rte.version>3.7.0</egovframework.rte.version>
<geotools.version>20.5</geotools.version>
</properties>
<repositories>
<repository>
<id>GeoSolutions</id>
<url>https://maven.geo-solutions.it/</url>
</repository>
<repository>
<id>GeoSolutions2</id>
<url>https://repo.osgeo.org/repository/geotools-releases/</url>
</repository>
<repository>
<id>mvn2</id>
<url>http://repo1.maven.org/maven2/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>true</enabled>
</snapshots>
</repository>
<repository>
<id>egovframe</id>
<url>http://www.egovframe.go.kr/maven/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
<repository>
<id>egovframe2</id>
<url>http://maven.egovframe.kr:8080/maven/</url>
<releases>
<enabled>true</enabled>
</releases>
<snapshots>
<enabled>false</enabled>
</snapshots>
</repository>
</repositories>
- geotools 가져오기
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-main</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-shapefile</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.geotools.jdbc</groupId>
<artifactId>gt-jdbc-postgis</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
<dependency>
<groupId>org.geotools</groupId>
<artifactId>gt-epsg-hsql</artifactId>
<version>${geotools.version}</version>
</dependency>
<dependency>
<groupId>org.bgee.log4jdbc-log4j2</groupId>
<artifactId>log4jdbc-log4j2-jdbc4.1</artifactId>
<version>1.16</version>
</dependency>
주의
- jts 버전이 다르면 테이블만 생성되고 값이 안들어감
<dependency>
<groupId>org.locationtech.jts</groupId>
<artifactId>jts-core</artifactId>
<version>1.16.0</version> <!-- 버전 바꾸면 안댐 -->
</dependency>
- 자바 8 이하는 아래꺼 꼭 추가해줘야함
(이거 없으면 작동안함 이거때매 개 삽질함, 자바 8 이상을 인식못하는 그 문제가 있었음)
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
<version>3.12.0</version>
</dependency>
@Controller
@Resource(name = "Geoservice")
private GeoService service;
@Description("SHP to DB ,DB to GeoLyr")
@PostMapping("/uploadShp.do")
@ResponseBody
public List<String> uploadShp(MultipartHttpServletRequest multiRequest) throws IOException {
return service.insertShpToGeo(multiRequest);
}
@Service
파일 리스트들을 받고, 해당 zip 파일명으로 하나씩 테이블을 생성하고
성공, 실패 목록을 리턴
@Override
public List<String> insertShpToGeo(MultipartHttpServletRequest multiRequest) throws IOException {
List<MultipartFile> fileList = multiRequest.getFiles("file");
List<String> fail = new ArrayList<>();
for (MultipartFile multipartFile : fileList) {
String fileNm = FilenameUtils.removeExtension(multipartFile.getOriginalFilename());
String successNm = createTableByShp(multipartFile, fileNm);
if (successNm == null) {
fail.add(multipartFile.getOriginalFilename());
}
}
if (fail.isEmpty()) fail.add("전부성공" + cnt);
return fail;
}
1. shp 로 테이블 생성
- 좌표계는 5186 으로 변환해서 생성
- 테이블의 좌표계가 있고 원하는 좌표계가 아닐경우에만 5186으로 변환
- 테이블의 좌표계가 없을경우 그냥 5186 으로 생성
private String createTableByShp(MultipartFile zipFile, String fileNm) throws IOException {
String result = fileNm;
DataStore dataStore = null;
ShapefileDataStore fileDataStore = null;
Path tempDir = null;
String epsg = "EPSG:5186"; // 목표좌표계
Map<String, Object> params = getPostgisInfo(globalProperties);
DefaultTransaction transaction = new DefaultTransaction("createTableTransaction");
try {
Map<String, Object> fileData = unZipShp(zipFile);
tempDir = (Path) fileData.get("tempDir");
Path shpFilePath = (Path) fileData.get("shpFilePath");
dataStore = DataStoreFinder.getDataStore(params);
if (dataStore == null) {
throw new IOException("dataStore is null");
}
// 파일을 통해 데이터 스토어 만들고 인코딩 설정후 스키마 가져옴
fileDataStore = new ShapefileDataStore(shpFilePath.toFile().toURI().toURL());
fileDataStore.setCharset(detectCharset(shpFilePath));
SimpleFeatureType featureSchema = fileDataStore.getFeatureSource().getSchema();
// geomType 재반환
// 가져온 스키마 기반으로 피쳐타입 생성하기
SimpleFeatureTypeBuilder builder = new SimpleFeatureTypeBuilder();
builder.setName(fileNm);
builder.setCRS(CRS.decode(epsg)); // 목표 좌표계 설정
// 기존 속성 추가
for (AttributeDescriptor attribute : featureSchema.getAttributeDescriptors()) {
if (attribute instanceof GeometryDescriptor) {
GeometryDescriptor geomDesc = (GeometryDescriptor) attribute;
builder.add(geomDesc.getLocalName(), geomDesc.getType().getBinding());
} else {
builder.add(attribute.getName().getLocalPart(), attribute.getType().getBinding());
}
}
// 추가한 속성들로 새로운 스키마 생성하고 테이블 만들기
SimpleFeatureType newSchema = builder.buildFeatureType();
dataStore.createSchema(newSchema);
// 만든 테이블의 피쳐 스토어 얻어오기
SimpleFeatureStore featureStore = (SimpleFeatureStore) dataStore.getFeatureSource(fileNm);
featureStore.setTransaction(transaction);
// 좌표계 변환 및 피쳐 스토어에 값 insert
CoordinateReferenceSystem targetCRS = CRS.decode(epsg, true);
CoordinateReferenceSystem sourceCRS = featureSchema.getCoordinateReferenceSystem();
String srs = CRS.toSRS(sourceCRS);
boolean needTrans = false;
if (sourceCRS != null) {
needTrans = !srs.equals(epsg);
}
if (needTrans) {
MathTransform transform = CRS.findMathTransform(sourceCRS, targetCRS, true);
List<SimpleFeature> newFeatures = new ArrayList<>();
try (SimpleFeatureIterator iter = fileDataStore.getFeatureSource().getFeatures().features()) {
while (iter.hasNext()) {
SimpleFeature feature = iter.next();
SimpleFeatureBuilder newBuilder = new SimpleFeatureBuilder(newSchema);
for (AttributeDescriptor descriptor : featureSchema.getAttributeDescriptors()) {
Object value = feature.getAttribute(descriptor.getName());
if (value instanceof Geometry) {
value = JTS.transform((Geometry) value, transform);
}
newBuilder.set(descriptor.getName().getLocalPart(), value);
}
SimpleFeature newFeature = newBuilder.buildFeature(null);
newFeatures.add(newFeature);
}
}
SimpleFeatureCollection collection = new ListFeatureCollection(newSchema, newFeatures);
featureStore.addFeatures(collection);
} else {
featureStore.addFeatures(fileDataStore.getFeatureSource().getFeatures());
}
transaction.commit();
} catch (FactoryException | TransformException | RuntimeException | IOException e) {
System.out.println(e);
result = null;
transaction.rollback();
} finally {
transaction.close();
dataStore.dispose();
fileDataStore.dispose();
deleteTemp(tempDir);
}
return result;
}
2. postgis 연결 설정
private Map<String, Object> getPostgisInfo(Properties properties) {
// 아래는 본인의 상황에 맞게 설정하세요
Map<String, Object> postgisInfo = new HashMap<>();
postgisInfo.put("dbtype", "postgis");
postgisInfo.put("host", "localhost");
postgisInfo.put("port", 5432);
postgisInfo.put("schema", "public");
postgisInfo.put("database", "postgres");
postgisInfo.put("user", "postgres");
postgisInfo.put("passwd", "1234");
return postgisInfo;
}
3. 유틸
- 인코딩
UTF-8
private Charset detectCharset(Path shpFilePath) throws IOException {
Path cpgFilePath = Paths.get(shpFilePath.toString().replace(".shp", ".cpg"));
if (Files.exists(cpgFilePath)) {
String charsetName = new String(Files.readAllBytes(cpgFilePath)).trim();
return Charset.forName(charsetName);
}
// Default to UTF-8 if no .cpg file is found
return Charset.forName("UTF-8");
}
- zip 파일 압축해제 및 임시파일 삭제
private Map<String, Object> unZipShp(MultipartFile zipFile) throws IOException {
Path tempPath = null;
File convFile = null;
try {
tempPath = Files.createTempDirectory("shp-upload");
convFile = File.createTempFile("temp", null);
zipFile.transferTo(convFile);
String shpFileName = null;
Path shpFilePath = null;
try (InputStream is = new FileInputStream(convFile);
//UTF-8
ArchiveInputStream ais = new ZipArchiveInputStream(is, "EUC-KR")) {
ArchiveEntry entry;
while ((entry = ais.getNextEntry()) != null) {
String entryName = entry.getName();
Path filePath = tempPath.resolve(entryName);
if (entry.isDirectory()) {
Files.createDirectories(filePath);
} else {
try (OutputStream os = Files.newOutputStream(filePath)) {
IOUtils.copy(ais, os);
}
}
if (entryName.toLowerCase().endsWith(".shp")) {
shpFileName = filePath.getFileName().toString();
shpFilePath = tempPath.resolve(shpFileName);
}
}
}
Map<String, Object> result = new HashMap<>();
result.put("tempDir", tempPath);
result.put("shpFilePath", shpFilePath);
result.put("shpFileName", shpFileName);
return result;
} catch (IOException | RuntimeException e) {
deleteTemp(tempPath);
throw e;
} finally {
if (convFile != null) {
convFile.delete();
}
}
}
private void deleteTemp(Path tempDir) throws IOException {
if (tempDir == null) {
return;
}
// 디렉토리가 빈 디렉토리인지 확인
try (Stream<Path> stream = Files.list(tempDir)) {
if (stream.findFirst().isPresent()) {
Files.walk(tempDir)
.sorted(Comparator.reverseOrder())
.map(Path::toFile)
.forEach(File::delete);
}
}
Files.deleteIfExists(tempDir);
}
참고
https://velog.io/@dailylifecoding/playing-around-with-geotools
https://spatiumwdev.tistory.com/16
반응형
'GIS (공간정보)' 카테고리의 다른 글
JPA Entity 에서 Geom 타입 컬럼 사용하기 (0) | 2024.11.25 |
---|