xingjinshuang
2025-02-20 0890b7861feae74bdcfd1851e577db6b9f31d484
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
package com.se.simu.service.Impl;
 
import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.se.simu.service.SemFilesSimuService;
import com.se.simu.utils.ZipUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
 
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardCopyOption;
import java.sql.*;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.HashMap;
 
@Slf4j
@Service
public class SemFilesSimuServiceImpl implements SemFilesSimuService {
 
    @Value("${config.outPath}")
    private String outPath;
 
    /**
     * 获取 INTRODUCE
     *
     * @return {@link Object}
     */
    @Override
    public Object getIntroduce() {
        HashMap<String, Object> introduceMap = new HashMap<>();
        String introduce = "SEM定义城市空间对象(如建筑物、构筑物等)类型和相互关系的存储结构,支持为空间实体对象挂接非结构化的数据,如人工模型、点云模型、视频文件等。SEM可存储城市空间对象的元数据(Metadata)、实体对象(Entity)、链接对象(LinkObject)、属性(Attribute)、材质(Material)、纹理(Texture)、纹理顶点(TextureVertice)、图片数据(Image)、几何模板(Template)以及扩展内容(ExtensionSchema)等";
        introduceMap.put("introduce", introduce);
        introduceMap.put("10张表", "SEM包含元数据表、实体对象表、链接对象表、属性表、材质表、纹理表、纹理顶点表、图片数据表、几何模板表和扩展表");
        introduceMap.put("Metadata(元数据表)", "用于存储 SEM 基础信息");
        introduceMap.put("Entity(实体对象表)", "用于存储空间实体对象的几何数据");
        introduceMap.put("LinkObject(链接对象表)", "用于存储空间实体对象的挂接对象信息");
        introduceMap.put("Attribute(属性表)", "用于存储空间实体对象的属性");
        introduceMap.put("Material(材质表)", "用于存储空间实体对象的材质信息");
        introduceMap.put("Texture(纹理表)", "用于存储空间实体对象的纹理信息");
        introduceMap.put("TextureVertice(纹理顶点表)", "记录空间实体对象的纹理顶点坐标值");
        introduceMap.put("Image(图片数据表)", "存储空间实体对象的纹理或挂接的人工模型所应用的图片数据");
        introduceMap.put("Template(几何模板表)", "存储空间实体对象的几何模板");
        introduceMap.put("ExtensionSchema(扩展表)", "描述领域本体的扩展属性和扩展信息");
        // 新增表
        String dynzamizers = "1、动态数据存储在DYNZAMIZERS表中,其中:" + "url:数据url" + "data:zarr数据,使用的是zarr的压缩存储格式。详见zarr的zipstore。" + "gmlId:与实体对象相关联字段(使用ENTITY表(实体表)中的UUID相关联)" +
                "" + "zarr数据结构示例:" + "Grid相关的zarr:" + "/" + "|——depth (n,height,width) " + "|——time(n)" + "" + "time存储时间序列" + "depth存储水深相关信息,三维数组,第一维为时间 与time相对应" +
                "数组长度n代表时间切片的个数" + "height,width代表栅格的长和宽" + "降雨量相关zarr:" + "/" + "|——rainfall(n)" + "|——time(n)" + "" + "time存储时间序列" + "rainfall 存储降雨量相关信息,一维数组,与time相对应" +
                "数组长度n代表时间切片的个数" + "" + "" + "2、terrain的存储方式:" + "类型为”+Terrain“" +
                "Entity中几何存储地形的外包框,使用纹理贴图存储地形tif转出的png图片。";
        introduceMap.put("DYNZAMIZERS(新增:动态数据存储)", dynzamizers);
        //return dynzamizers.getBytes(StandardCharsets.UTF_8);
        return introduceMap;
    }
 
    @Override
    public Object createSimuBySemFile() {
        return null;
    }
 
    @Override
    public Object readSemFile(String filePath) {
        // 文件地址
        log.info("filePath:{}", filePath);
        // 处理文件
        JSONObject result = new JSONObject();
        try {
            // 测试连接SQLite数据库
            Connection connection = connectToSQLiteWithCopy(filePath);
            System.out.println("SQLite数据库连接成功!");
            Statement stmt = connection.createStatement();
            // 查询ENTITY表的数据并返回JSON格式的结果
//            result = queryEntityTable(stmt);
            result = queryDynamizersTable(stmt);
            // 关闭连接
            connection.close();
            return result;
        } catch (SQLException | IOException e) {
            System.err.println("操作失败: " + e.getMessage());
            return e.getMessage();
        }
    }
 
 
    /**
     * 根据传入的文件路径,添加后缀 `.db` 并连接 SQLite 数据库。
     *
     * @param filePath 文件路径
     * @return SQLite数据库的连接
     * @throws SQLException 如果SQLite连接失败
     */
    public static Connection connectToSQLite(String filePath) throws SQLException {
        // 检查文件路径是否为空
        if (filePath == null || filePath.trim().isEmpty()) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
 
        // 检查文件是否已经有.db后缀,没有则添加
        if (!filePath.endsWith(".db")) {
            filePath = filePath + ".db";
        }
        log.info("Connecting to SQLite database: " + filePath);
 
        // 创建文件对象
        File dbFile = new File(filePath);
 
        // 如果文件不存在,则创建一个新文件
        if (!dbFile.exists()) {
            log.info("文件不存在,请检查文件位置是否正确...");
            return null;
            // try {
            //     // 通过调用 createNewFile() 方法创建新文件
            //     boolean created = dbFile.createNewFile();
            //     if (!created) {
            //         throw new SQLException("无法创建数据库文件");
            //     }
            // } catch (Exception e) {
            //     throw new SQLException("创建数据库文件时出错: " + e.getMessage(), e);
            // }
        }
 
        // 使用 SQLite JDBC 连接字符串连接数据库
        String url = "jdbc:sqlite:" + dbFile.getAbsolutePath();
        log.info("连接到SQLite数据库: {}", url);
 
        // 创建并返回数据库连接
        return DriverManager.getConnection(url);
    }
 
    /**
     * 根据传入的文件路径,复制文件并给复制的文件添加后缀 `.db`,然后连接 SQLite 数据库。
     *
     * @param filePath 原始文件路径
     * @return SQLite数据库的连接
     * @throws SQLException 如果SQLite连接失败
     * @throws IOException  如果文件复制失败
     */
    public static Connection connectToSQLiteWithCopy(String filePath) throws SQLException, IOException {
        // 检查文件路径是否为空
        if (filePath == null || filePath.trim().isEmpty()) {
            throw new IllegalArgumentException("文件路径不能为空");
        }
        // 创建原始文件对象
        File originalFile = new File(filePath);
        // 检查原文件是否存在
        if (!originalFile.exists()) {
            throw new FileNotFoundException("原始文件不存在:" + filePath);
        }
        // 获取当前时间戳作为文件名的一部分
        String timestamp = String.valueOf(System.currentTimeMillis());
        // 创建一个新的文件名,添加一个随机后缀以避免文件名冲突
        String newFilePath = filePath + "." + timestamp + ".db";
        // 复制文件到新的路径
        copyFile(originalFile, new File(newFilePath));
        // 使用 SQLite JDBC 连接字符串连接新的数据库文件
        String url = "jdbc:sqlite:" + newFilePath;
        log.info("连接到SQLite数据库: {}", url);
        // 返回SQLite数据库连接
        return DriverManager.getConnection(url);
    }
 
 
    /**
     * 复制文件到新的位置
     *
     * @param sourceFile 原始文件
     * @param destFile   目标文件
     * @throws IOException 如果复制过程中发生错误
     */
    private static void copyFile(File sourceFile, File destFile) throws IOException {
        // 使用NIO的Files.copy方法进行高效的文件复制
        Path sourcePath = sourceFile.toPath();
        Path destinationPath = destFile.toPath();
 
        // 复制文件并覆盖目标文件
        Files.copy(sourcePath, destinationPath, StandardCopyOption.REPLACE_EXISTING);
        log.info("文件已复制到:" + destinationPath.toString());
    }
 
 
    /**
     * 查询ENTITY表的数据并返回JSON格式的数据
     *
     * @param stmt SQLite数据库连接
     * @return JSON格式的查询结果
     * @throws SQLException 如果查询过程中发生错误
     */
    public static JSONObject queryEntityTable(Statement stmt) throws SQLException {
        // 构建SQL查询语句
        String querySql = "SELECT ID,DATA,SCHEMA,UUID FROM ENTITY";
        // 执行查询并返回结果
        try (ResultSet rs = stmt.executeQuery(querySql)) {
            // 创建一个 JSON 数组用于存储多条记录
            JSONArray jsonArray = new JSONArray();
            // 将查询结果转化为JSON数组
            JSONObject resJsonObject = new JSONObject();
            // 遍历查询结果
            while (rs.next()) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("ID", rs.getInt("ID"));
                String data = rs.getString("DATA");
                // 将DATA字段进行反序列化
                jsonObject.put("DATA", JSON.parse(data));
                jsonObject.put("SCHEMA", rs.getInt("SCHEMA"));
                jsonObject.put("UUID", rs.getString("UUID"));
                // 将每一行的结果添加到JSON数组
                jsonArray.add(jsonObject);
            }
            resJsonObject.put("entity", jsonArray);
            return resJsonObject;
        }
    }
 
    /**
     * 查询DYNAMIZERS表的数据并返回JSON格式的数据
     * 对于BLOB字段,转换为Base64字符串
     *
     * @param stmt SQL语句执行对象
     * @return JSON格式的查询结果
     * @throws SQLException 如果查询过程中发生错误
     */
    public JSONObject queryDynamizersTable(Statement stmt) throws SQLException {
        // 构建SQL查询语句
        String querySql = "SELECT URL, GMLID, DATA FROM DYNAMIZERS";
        // 创建一个 JSON 对象用于存储结果
        JSONObject resultJson = new JSONObject();
        // 创建一个 JSON 数组用于存储多条记录
        JSONArray jsonArray = new JSONArray();
        // 执行查询并返回结果
        try (ResultSet rs = stmt.executeQuery(querySql)) {
            // 遍历查询结果
            while (rs.next()) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("URL", rs.getString("URL"));
                jsonObject.put("GMLID", rs.getString("GMLID"));
                // 获取 BLOB 数据并将其转换为 Base64 编码字符串
                byte[] blobData = rs.getBytes("DATA");
                DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyyMMddHHmmss");
                String dirpath = outPath + "\\" + formatter.format(LocalDateTime.now());
                String filepath = outPath + "\\" + formatter.format(LocalDateTime.now()) + "\\" + rs.getString("URL");
                try {
                    File file = new File(dirpath);
                    if (!file.exists()) {
                        file.mkdirs();
                    }
                    File resultFile = new File(filepath);
                    resultFile.createNewFile();
                    try (FileOutputStream fos = new FileOutputStream(filepath)) {
                        fos.write(blobData);
                        ZipUtils.unzip(filepath,dirpath);
                        System.out.println("Bytes written to file successfully.");
                        resultFile.delete();
                    } catch (IOException e) {
                        e.printStackTrace();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                }
                jsonObject.put("DATA", filepath);
                jsonArray.add(jsonObject);
            }
            // 将查询结果放入最终的 JSON 对象中
            resultJson.put("dynamizers", jsonArray);
        }
        return resultJson;
    }
 
    /**
     * 使用分页 Query Entity Table
     * // 假设每页查询1000条数据,查询第2页的数据
     * // JSONArray result = queryEntityTableWithPagination(stmt, 1000, 2);
     *
     * @param stmt       STMT
     * @param pageSize
     * @param pageNumber 页码
     * @return {@link JSONArray}
     * @throws SQLException sql异常
     */
    public static JSONArray queryEntityTableWithPagination(Statement stmt, int pageSize, int pageNumber) throws SQLException {
        // 计算查询的偏移量
        int offset = (pageNumber - 1) * pageSize;
 
        // 构建SQL查询语句,使用LIMIT和OFFSET进行分页
        String querySql = "SELECT * FROM ENTITY LIMIT " + pageSize + " OFFSET " + offset;
 
        try (ResultSet rs = stmt.executeQuery(querySql)) {
            JSONArray jsonArray = new JSONArray();
 
            // 遍历查询结果
            while (rs.next()) {
                JSONObject jsonObject = new JSONObject();
                jsonObject.put("ID", rs.getInt("ID"));
                jsonObject.put("DATA", rs.getString("DATA"));
                jsonObject.put("SCHEMA", rs.getString("SCHEMA"));
                jsonObject.put("UUID", rs.getString("UUID"));
 
                jsonArray.add(jsonObject);
            }
 
            return jsonArray;
        }
    }
 
 
//    public static void main(String[] args) {
//        try {
//            // 测试连接SQLite数据库
//            Connection connection = connectToSQLiteWithCopy("D:\\0a_project\\simulation\\other\\1211SEM样例\\管点.sem");
//            System.out.println("SQLite数据库连接成功!");
//            // 关闭连接
//            connection.close();
//        } catch (SQLException | IOException e) {
//            System.err.println("操作失败: " + e.getMessage());
//        }
//    }
 
    /**
     * 创建数据库连接
     */
    private static Connection getConnection(String sqliteDbPath) throws SQLException {
        // 创建SQLite数据库连接
        Connection conn = DriverManager.getConnection("jdbc:sqlite:" + sqliteDbPath);
        Statement stmt = conn.createStatement();
 
        // 关闭数据库连接
        //conn.close();
        return conn;
    }
 
 
}