|
|
@@ -10,15 +10,26 @@ import com.siwei.spatial.service.file.ISpatialFilesDbService;
|
|
|
import com.siwei.spatial.service.file.ITGeomDbDetailsService;
|
|
|
import com.siwei.spatial.service.file.ITGeomDbService;
|
|
|
import com.siwei.spatial.utils.FileUtils;
|
|
|
+import org.geotools.data.DefaultTransaction;
|
|
|
import org.geotools.data.FeatureSource;
|
|
|
+import org.geotools.data.Transaction;
|
|
|
+import org.geotools.data.collection.ListFeatureCollection;
|
|
|
import org.geotools.data.shapefile.ShapefileDataStore;
|
|
|
+import org.geotools.data.shapefile.ShapefileDataStoreFactory;
|
|
|
import org.geotools.data.simple.SimpleFeatureCollection;
|
|
|
import org.geotools.data.simple.SimpleFeatureIterator;
|
|
|
+import org.geotools.data.simple.SimpleFeatureSource;
|
|
|
+import org.geotools.data.simple.SimpleFeatureStore;
|
|
|
+import org.geotools.feature.simple.SimpleFeatureBuilder;
|
|
|
+import org.geotools.feature.simple.SimpleFeatureTypeBuilder;
|
|
|
import org.geotools.referencing.CRS;
|
|
|
import org.locationtech.jts.geom.Geometry;
|
|
|
+import org.locationtech.jts.geom.MultiPolygon;
|
|
|
import org.locationtech.jts.io.ParseException;
|
|
|
+import org.locationtech.jts.io.WKBReader;
|
|
|
import org.locationtech.jts.io.WKTReader;
|
|
|
import org.opengis.feature.simple.SimpleFeature;
|
|
|
+import org.opengis.feature.simple.SimpleFeatureType;
|
|
|
import org.opengis.feature.type.AttributeDescriptor;
|
|
|
import org.opengis.referencing.crs.CoordinateReferenceSystem;
|
|
|
import org.slf4j.Logger;
|
|
|
@@ -28,9 +39,12 @@ import org.springframework.beans.factory.annotation.Value;
|
|
|
import org.springframework.stereotype.Service;
|
|
|
import org.springframework.transaction.annotation.Transactional;
|
|
|
|
|
|
+import javax.servlet.http.HttpServletResponse;
|
|
|
import java.io.File;
|
|
|
import java.io.FileInputStream;
|
|
|
import java.io.IOException;
|
|
|
+import java.io.PrintWriter;
|
|
|
+import java.io.Serializable;
|
|
|
import java.math.BigDecimal;
|
|
|
import java.nio.charset.Charset;
|
|
|
import java.nio.charset.StandardCharsets;
|
|
|
@@ -589,17 +603,192 @@ public class SpatialFilesDbServiceImpl implements ISpatialFilesDbService {
|
|
|
|
|
|
|
|
|
|
|
|
+ @Override
|
|
|
+ public List<Map<String,Object>> testGetShpData(String tabeName) {
|
|
|
+ List<Map<String,Object>> geoms = tGeomDbDetailsMapper.selectTableDataAndGeom(tabeName);
|
|
|
+ return geoms;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+ /**
|
|
|
+ * 从表中读取带有geom的数据,并生成一个zip文件,文件包含当前表的shp文件
|
|
|
+ * @param response
|
|
|
+ * @param ysdm
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void dealShpFileDownLoad(HttpServletResponse response, String ysdm) {
|
|
|
+ try {
|
|
|
+ List<Map<String, Object>> djzqAndGeomList = tGeomDbDetailsMapper.selectTableDjzq(ysdm, 0);
|
|
|
+ if (djzqAndGeomList == null || djzqAndGeomList.isEmpty()) {
|
|
|
+ throw new RuntimeException("没有找到相关数据");
|
|
|
+ }
|
|
|
|
|
|
+ // 1. 创建临时目录
|
|
|
+ String tempDir = System.getProperty("java.io.tmpdir") + File.separator + IdUtils.fastSimpleUUID();
|
|
|
+ File dir = new File(tempDir);
|
|
|
+ if (!dir.exists()) dir.mkdirs();
|
|
|
|
|
|
+ // 2. 生成 SHP 文件
|
|
|
+ String shpName = "djzq";
|
|
|
+ File shpFile = new File(tempDir + File.separator + shpName + ".shp");
|
|
|
+ writeShapefile(shpFile, djzqAndGeomList);
|
|
|
|
|
|
+ // 生成 .cpg 文件 (解决中文乱码,明确指定编码)
|
|
|
+ File cpgFile = new File(tempDir + File.separator + shpName + ".cpg");
|
|
|
+ try (PrintWriter writer = new PrintWriter(cpgFile)) {
|
|
|
+ writer.print("UTF-8");
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 压缩文件
|
|
|
+ File zipFile = new File(tempDir + File.separator + shpName + ".zip");
|
|
|
+ List<File> filesToZip = Arrays.asList(
|
|
|
+ new File(tempDir + File.separator + shpName + ".shp"),
|
|
|
+ new File(tempDir + File.separator + shpName + ".shx"),
|
|
|
+ new File(tempDir + File.separator + shpName + ".dbf"),
|
|
|
+ new File(tempDir + File.separator + shpName + ".prj"),
|
|
|
+ new File(tempDir + File.separator + shpName + ".cpg")
|
|
|
+ );
|
|
|
+ FileUtils.zipFiles(filesToZip, zipFile);
|
|
|
+
|
|
|
+ // 4. 设置响应头并下载
|
|
|
+ response.setContentType("application/zip");
|
|
|
+ response.setCharacterEncoding("utf-8");
|
|
|
+ String fileName = shpName + ".zip";
|
|
|
+ response.setHeader("Content-Disposition", "attachment;filename=" + fileName);
|
|
|
+
|
|
|
+ try (FileInputStream fis = new FileInputStream(zipFile)) {
|
|
|
+ byte[] buffer = new byte[4096];
|
|
|
+ int len;
|
|
|
+ while ((len = fis.read(buffer)) > 0) {
|
|
|
+ response.getOutputStream().write(buffer, 0, len);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ response.getOutputStream().flush();
|
|
|
|
|
|
+ // 5. 清理临时文件 (异步清理或在方法结束时清理)
|
|
|
+ // 这里简单处理,生产环境建议使用定时任务或更健壮的清理机制
|
|
|
+ // FileUtils.deleteDirectory(dir);
|
|
|
|
|
|
- @Override
|
|
|
- public List<Map<String,Object>> testGetShpData(String tabeName) {
|
|
|
- List<Map<String,Object>> geoms = tGeomDbDetailsMapper.selectTableDataAndGeom(tabeName);
|
|
|
- return geoms;
|
|
|
+ } catch (Exception e) {
|
|
|
+ log.error("导出SHP文件失败", e);
|
|
|
+ throw new RuntimeException("导出SHP文件失败: " + e.getMessage());
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
+ private void writeShapefile(File file, List<Map<String, Object>> dataList) throws Exception {
|
|
|
+ // 1. 定义要素类型 (SimpleFeatureType)
|
|
|
+ SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
|
|
|
+ typeBuilder.setName("djzq");
|
|
|
+
|
|
|
+ // 设置坐标系 CGCS2000 (EPSG:4490)
|
|
|
+ CoordinateReferenceSystem crs = CRS.decode("EPSG:4490");
|
|
|
+ typeBuilder.setCRS(crs);
|
|
|
+
|
|
|
+ // 添加字段 (根据表结构)
|
|
|
+ typeBuilder.add("the_geom", MultiPolygon.class);
|
|
|
+ typeBuilder.add("bsm", Integer.class);
|
|
|
+ typeBuilder.add("ysdm", String.class);
|
|
|
+ typeBuilder.add("djzqdm", String.class);
|
|
|
+ typeBuilder.add("djzqmc", String.class);
|
|
|
+ typeBuilder.add("bz", String.class);
|
|
|
+ typeBuilder.add("valid_flag", Integer.class);
|
|
|
+
|
|
|
+ SimpleFeatureType featureType = typeBuilder.buildFeatureType();
|
|
|
+
|
|
|
+ // 2. 创建要素集合
|
|
|
+ List<SimpleFeature> features = new ArrayList<>();
|
|
|
+ SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
|
|
|
+ WKTReader wktReader = new WKTReader();
|
|
|
+ WKBReader wkbReader = new WKBReader();
|
|
|
+
|
|
|
+ for (Map<String, Object> data : dataList) {
|
|
|
+ Object geomObj = data.get("wkt_geom");
|
|
|
+ if (geomObj == null) {
|
|
|
+ geomObj = data.get("geom"); // 备选方案,兼容原有字段名
|
|
|
+ }
|
|
|
+ if (geomObj == null) continue;
|
|
|
+
|
|
|
+ Geometry geometry = null;
|
|
|
+ if (geomObj instanceof Geometry) {
|
|
|
+ geometry = (Geometry) geomObj;
|
|
|
+ } else {
|
|
|
+ String geomStr = geomObj.toString();
|
|
|
+ if (StringUtils.isEmpty(geomStr)) {
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ try {
|
|
|
+ // 尝试判断是否为 Hex EWKB (以 010 或 000 开头)
|
|
|
+ if (geomStr.startsWith("01") || geomStr.startsWith("00")) {
|
|
|
+ // 如果是 EWKB,需要注意 JTS WKBReader 可能不直接支持 PostGIS 的 EWKB 标志位
|
|
|
+ // 优先保证数据库层 ST_AsText 转换
|
|
|
+ geometry = wkbReader.read(WKBReader.hexToBytes(geomStr));
|
|
|
+ } else {
|
|
|
+ // 尝试作为 WKT 解析
|
|
|
+ String wkt = geomStr;
|
|
|
+ if (wkt.contains(";")) {
|
|
|
+ wkt = wkt.split(";")[1];
|
|
|
+ }
|
|
|
+ geometry = wktReader.read(wkt);
|
|
|
+ }
|
|
|
+ } catch (ParseException e) {
|
|
|
+ log.warn("无法解析几何数据: {}, 错误: {}", geomStr, e.getMessage());
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (geometry != null) {
|
|
|
+ // Shapefile 强制要求几何类型一致,这里统一转为 MultiPolygon
|
|
|
+ if (geometry instanceof org.locationtech.jts.geom.Polygon) {
|
|
|
+ geometry = geometry.getFactory().createMultiPolygon(new org.locationtech.jts.geom.Polygon[]{(org.locationtech.jts.geom.Polygon) geometry});
|
|
|
+ }
|
|
|
+
|
|
|
+ featureBuilder.add(geometry);
|
|
|
+ featureBuilder.add(data.get("bsm"));
|
|
|
+ featureBuilder.add(data.get("ysdm"));
|
|
|
+ featureBuilder.add(data.get("djzqdm"));
|
|
|
+ featureBuilder.add(data.get("djzqmc"));
|
|
|
+ featureBuilder.add(data.get("bz"));
|
|
|
+ featureBuilder.add(data.get("valid_flag"));
|
|
|
+
|
|
|
+ features.add(featureBuilder.buildFeature(null));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 写入文件
|
|
|
+ ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
|
|
|
+ Map<String, Serializable> params = new HashMap<>();
|
|
|
+ params.put("url", file.toURI().toURL());
|
|
|
+ params.put("create spatial index", Boolean.TRUE);
|
|
|
+
|
|
|
+ ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
|
|
|
+ dataStore.setCharset(Charset.forName("UTF-8")); // 统一使用 UTF-8
|
|
|
+ dataStore.createSchema(featureType);
|
|
|
+
|
|
|
+ Transaction transaction = new DefaultTransaction("create");
|
|
|
+ String typeName = dataStore.getTypeNames()[0];
|
|
|
+ SimpleFeatureSource featureSource = dataStore.getFeatureSource(typeName);
|
|
|
+
|
|
|
+ if (featureSource instanceof SimpleFeatureStore) {
|
|
|
+ SimpleFeatureStore featureStore = (SimpleFeatureStore) featureSource;
|
|
|
+ SimpleFeatureCollection collection = new ListFeatureCollection(featureType, features);
|
|
|
+ featureStore.setTransaction(transaction);
|
|
|
+ try {
|
|
|
+ featureStore.addFeatures(collection);
|
|
|
+ transaction.commit();
|
|
|
+ } catch (Exception e) {
|
|
|
+ transaction.rollback();
|
|
|
+ throw e;
|
|
|
+ } finally {
|
|
|
+ transaction.close();
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ throw new IOException(typeName + " does not support read/write access");
|
|
|
+ }
|
|
|
+ dataStore.dispose();
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
}
|