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 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; } }