|
|
@@ -789,6 +789,173 @@ public class SpatialFilesDbServiceImpl implements ISpatialFilesDbService {
|
|
|
}
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
+ * 动态传入表名称,根据表结构生成 SHP 文件
|
|
|
+ * @param tableName 表名
|
|
|
+ * @param filePath 生成路径
|
|
|
+ * @param fileName 文件名
|
|
|
+ * @throws Exception
|
|
|
+ */
|
|
|
+ @Override
|
|
|
+ public void writeShapefileByTable(String tableName, String filePath, String fileName) throws Exception {
|
|
|
+ // 1. 获取表字段信息
|
|
|
+ List<Map<String, Object>> columns = tGeomDbDetailsMapper.selectTableColumns(tableName);
|
|
|
+ if (columns == null || columns.isEmpty()) {
|
|
|
+ throw new RuntimeException("无法获取表字段信息: " + tableName);
|
|
|
+ }
|
|
|
+
|
|
|
+ // 2. 获取数据
|
|
|
+ List<Map<String, Object>> dataList = tGeomDbDetailsMapper.selectTableDataAndGeom(tableName);
|
|
|
+ if (dataList == null || dataList.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ // 3. 定义要素类型 (SimpleFeatureType)
|
|
|
+ SimpleFeatureTypeBuilder typeBuilder = new SimpleFeatureTypeBuilder();
|
|
|
+ typeBuilder.setName(tableName);
|
|
|
+
|
|
|
+ // 设置坐标系 CGCS2000 (EPSG:4490)
|
|
|
+ CoordinateReferenceSystem crs = CRS.decode("EPSG:4490");
|
|
|
+ typeBuilder.setCRS(crs);
|
|
|
+
|
|
|
+ // 存储字段名和对应的类型,用于后续数据读取
|
|
|
+ Map<String, String> columnTypeMap = new LinkedHashMap<>();
|
|
|
+
|
|
|
+ // 添加几何字段 (固定为 the_geom)
|
|
|
+ typeBuilder.add("the_geom", MultiPolygon.class);
|
|
|
+
|
|
|
+ for (Map<String, Object> col : columns) {
|
|
|
+ String colName = (String) col.get("column_name");
|
|
|
+ String dataType = (String) col.get("data_type");
|
|
|
+
|
|
|
+ if ("geom".equalsIgnoreCase(colName)) {
|
|
|
+ continue; // 已经处理过几何字段
|
|
|
+ }
|
|
|
+
|
|
|
+ // SHP 字段名长度限制为 10 字符
|
|
|
+ String shpColName = colName.length() > 10 ? colName.substring(0, 10) : colName;
|
|
|
+ Class<?> javaType = mapDatabaseTypeToJavaClass(dataType);
|
|
|
+ typeBuilder.add(shpColName, javaType);
|
|
|
+ columnTypeMap.put(colName, shpColName);
|
|
|
+ }
|
|
|
+
|
|
|
+ SimpleFeatureType featureType = typeBuilder.buildFeatureType();
|
|
|
+
|
|
|
+ // 4. 创建要素集合
|
|
|
+ List<SimpleFeature> features = new ArrayList<>();
|
|
|
+ SimpleFeatureBuilder featureBuilder = new SimpleFeatureBuilder(featureType);
|
|
|
+ WKTReader wktReader = new WKTReader();
|
|
|
+ WKBReader wkbReader = new WKBReader();
|
|
|
+
|
|
|
+ for (Map<String, Object> data : dataList) {
|
|
|
+ // 获取几何数据,优先从 geom2 (EWKT) 获取
|
|
|
+ Object geomObj = data.get("geom2");
|
|
|
+ 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 {
|
|
|
+ if (geomStr.startsWith("01") || geomStr.startsWith("00")) {
|
|
|
+ geometry = wkbReader.read(WKBReader.hexToBytes(geomStr));
|
|
|
+ } else {
|
|
|
+ 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) {
|
|
|
+ // 强制转为 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);
|
|
|
+
|
|
|
+ // 添加属性字段
|
|
|
+ for (String originColName : columnTypeMap.keySet()) {
|
|
|
+ featureBuilder.add(data.get(originColName));
|
|
|
+ }
|
|
|
+
|
|
|
+ features.add(featureBuilder.buildFeature(null));
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // 5. 写入文件
|
|
|
+ File shpFile = new File(filePath + File.separator + fileName + ".shp");
|
|
|
+ ShapefileDataStoreFactory dataStoreFactory = new ShapefileDataStoreFactory();
|
|
|
+ Map<String, Serializable> params = new HashMap<>();
|
|
|
+ params.put("url", shpFile.toURI().toURL());
|
|
|
+ params.put("create spatial index", Boolean.TRUE);
|
|
|
+
|
|
|
+ ShapefileDataStore dataStore = (ShapefileDataStore) dataStoreFactory.createNewDataStore(params);
|
|
|
+ dataStore.setCharset(Charset.forName("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();
|
|
|
+ }
|
|
|
+ }
|
|
|
+ dataStore.dispose();
|
|
|
+
|
|
|
+ // 6. 生成 .cpg 文件
|
|
|
+ File cpgFile = new File(filePath + File.separator + fileName + ".cpg");
|
|
|
+ try (PrintWriter writer = new PrintWriter(cpgFile)) {
|
|
|
+ writer.print("UTF-8");
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ private Class<?> mapDatabaseTypeToJavaClass(String dataType) {
|
|
|
+ if (dataType == null) return String.class;
|
|
|
+ dataType = dataType.toLowerCase();
|
|
|
+ if (dataType.contains("char") || dataType.contains("text")) {
|
|
|
+ return String.class;
|
|
|
+ } else if (dataType.contains("int")) {
|
|
|
+ return Integer.class;
|
|
|
+ } else if (dataType.contains("numeric") || dataType.contains("decimal") || dataType.contains("double") || dataType.contains("float") || dataType.contains("real")) {
|
|
|
+ return Double.class;
|
|
|
+ } else if (dataType.contains("date") || dataType.contains("time")) {
|
|
|
+ return java.util.Date.class;
|
|
|
+ } else if (dataType.contains("boolean")) {
|
|
|
+ return Boolean.class;
|
|
|
+ }
|
|
|
+ return String.class;
|
|
|
+ }
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
+
|
|
|
|
|
|
|
|
|
}
|