diff --git a/src/main/java/com/yj/earth/business/controller/AuthController.java b/src/main/java/com/yj/earth/business/controller/AuthController.java index 21f27c0..da062cc 100644 --- a/src/main/java/com/yj/earth/business/controller/AuthController.java +++ b/src/main/java/com/yj/earth/business/controller/AuthController.java @@ -1,6 +1,5 @@ package com.yj.earth.business.controller; -import com.yj.earth.annotation.CheckAuth; import com.yj.earth.auth.AuthInfo; import com.yj.earth.auth.AuthValidator; import com.yj.earth.common.util.ApiResponse; diff --git a/src/main/java/com/yj/earth/business/controller/GdalController.java b/src/main/java/com/yj/earth/business/controller/GdalController.java index a936d13..5f344c4 100644 --- a/src/main/java/com/yj/earth/business/controller/GdalController.java +++ b/src/main/java/com/yj/earth/business/controller/GdalController.java @@ -1,6 +1,6 @@ package com.yj.earth.business.controller; -import com.yj.earth.common.util.GdalUtil; +import com.yj.earth.common.util.GdalJsonConverter; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.tags.Tag; @@ -29,7 +29,7 @@ public class GdalController { public void importDataStreamGzip(@Parameter(description = "矢量文件路径", required = true) @RequestParam("path") String path, HttpServletResponse response) throws IOException { // 解析矢量文件、得到JSON数据 - String jsonData = GdalUtil.readVectorToSpecifiedGeoJson(path); + String jsonData = GdalJsonConverter.convertToJson(path); // 设置响应头 String filename = URLEncoder.encode("data.gz", StandardCharsets.UTF_8); diff --git a/src/main/java/com/yj/earth/business/controller/GraphHopperController.java b/src/main/java/com/yj/earth/business/controller/GraphHopperController.java index d903898..9b8797c 100644 --- a/src/main/java/com/yj/earth/business/controller/GraphHopperController.java +++ b/src/main/java/com/yj/earth/business/controller/GraphHopperController.java @@ -59,7 +59,7 @@ public class GraphHopperController { @Operation(summary = "加载地图数据") @PostMapping("/loadMap") @CheckAuth - public ApiResponse webLoadMap(@Parameter(description = "文件路径") @RequestParam String path) { + public ApiResponse loadMap(@Parameter(description = "文件路径") @RequestParam String path) { File osmFile = new File(path); if (!osmFile.exists()) { return ApiResponse.failure("地图文件不存在: " + path); @@ -106,6 +106,44 @@ public class GraphHopperController { return ApiResponse.success(null); } + @Operation(summary = "清除地图服务") + @PostMapping("/clearMap") + @CheckAuth + public ApiResponse clearMap() { + // 防止并发操作(与加载操作互斥) + if (isLoading.get()) { + return ApiResponse.failure("地图正在加载中、无法清除、请稍后再试"); + } + + // 标记正在处理清除操作 + isLoading.set(true); + try { + // 1. 关闭当前GraphHopper实例 + if (currentHopper != null) { + log.info("开始关闭当前地图服务实例"); + currentHopper.close(); + currentHopper = null; + log.info("地图服务实例已关闭"); + } + + // 2. 删除地图数据目录 + log.info("开始删除地图数据目录"); + deleteOldGraphDir(); + log.info("地图数据目录已删除"); + + // 3. 重置状态变量 + isLoaded.set(false); + + return ApiResponse.success("地图服务已成功清除"); + } catch (Exception e) { + log.error("清除地图服务失败", e); + return ApiResponse.failure("清除地图服务失败: " + e.getMessage()); + } finally { + // 释放操作锁 + isLoading.set(false); + } + } + @Operation(summary = "路径规划") @PostMapping("/route") @CheckAuth @@ -126,7 +164,7 @@ public class GraphHopperController { } ghPoints.add(new GHPoint(request.getEndLat(), request.getEndLng())); // 终点 - // 构建请求(仅指定Profile、无setWeighting) + // 构建请求 String targetProfile = request.getProfile() != null ? request.getProfile() : "car"; GHRequest ghRequest = new GHRequest(ghPoints) .setProfile(targetProfile); @@ -139,12 +177,14 @@ public class GraphHopperController { // 检查是否有超出范围的错误 boolean hasOutOfBoundsError = response.getErrors().stream() .anyMatch(e -> e instanceof com.graphhopper.util.exceptions.PointOutOfBoundsException); - + boolean hasPointNotFoundError = response.getErrors().stream() + .anyMatch(e -> e instanceof com.graphhopper.util.exceptions.PointNotFoundException); if (hasOutOfBoundsError) { - // 返回超出范围的特定格式响应 return ApiResponse.failure("路径超出地图范围"); + } else if (hasPointNotFoundError) { + return ApiResponse.failure("未超地图范围但找不到路径点"); } else { - return ApiResponse.failure("路径计算失败: " + response.getErrors().toString()); + return ApiResponse.failure("路径计算异常: " + response.getErrors().get(0).getMessage()); } } // 解析结果 diff --git a/src/main/java/com/yj/earth/business/controller/PbfInfoController.java b/src/main/java/com/yj/earth/business/controller/PbfInfoController.java new file mode 100644 index 0000000..3a4d903 --- /dev/null +++ b/src/main/java/com/yj/earth/business/controller/PbfInfoController.java @@ -0,0 +1,89 @@ +package com.yj.earth.business.controller; + +import cn.hutool.core.io.FileUtil; +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper; +import com.yj.earth.business.domain.PbfInfo; +import com.yj.earth.business.service.PbfInfoService; +import com.yj.earth.common.util.ApiResponse; +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.media.Schema; +import io.swagger.v3.oas.annotations.tags.Tag; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.util.List; + +@Tag(name = "地图文件管理") +@RestController +@RequestMapping("/pbfInfo") +public class PbfInfoController { + + @Resource + private PbfInfoService pbfInfoService; + @Resource + private GraphHopperController graphHopperController; + + @Operation(summary = "添加地图文件") + @PostMapping("/add") + public ApiResponse add(@Parameter(description = "地图文件路径") @RequestParam(required = true) String path) { + PbfInfo pbfInfo = new PbfInfo(); + pbfInfo.setPath(path); + pbfInfo.setName(FileUtil.mainName(path)); + pbfInfo.setIsEnable(0); + pbfInfoService.save(pbfInfo); + return ApiResponse.success(null); + } + + @Operation(summary = "删除地图文件") + @PostMapping("/delete") + public ApiResponse delete(@Parameter(description = "地图文件ID") @RequestParam(required = true) String id) { + pbfInfoService.removeById(id); + return ApiResponse.success(null); + } + + @Operation(summary = "启用地图文件") + @PostMapping("/enable") + public ApiResponse enable(@Parameter(description = "地图文件ID") @RequestParam(required = true) String id) { + // 根据文件ID查询数据 + PbfInfo pbfInfo = pbfInfoService.getById(id); + pbfInfo.setIsEnable(1); + pbfInfoService.updateById(pbfInfo); + // 调用地图服务 + graphHopperController.loadMap(pbfInfo.getPath()); + // 除这条数据以外的全部为禁用 + List list = pbfInfoService.list(); + for (PbfInfo info : list) { + if (!info.getId().equals(id)) { + info.setIsEnable(0); + pbfInfoService.updateById(info); + } + } + return ApiResponse.success(null); + } + + @Operation(summary = "禁用地图文件") + @PostMapping("/disable") + public ApiResponse disable(@Parameter(description = "地图文件ID") @RequestParam(required = true) String id) { + PbfInfo pbfInfo = new PbfInfo(); + pbfInfo.setId(id); + pbfInfo.setIsEnable(0); + pbfInfoService.updateById(pbfInfo); + graphHopperController.clearMap(); + return ApiResponse.success(null); + } + + @Operation(summary = "获取所有地图文件") + @PostMapping("/list") + public ApiResponse list() { + LambdaQueryWrapper queryWrapper = new QueryWrapper().lambda(); + // 把启用的排在最前面 + queryWrapper.orderByDesc(PbfInfo::getIsEnable); + queryWrapper.orderByAsc(PbfInfo::getCreatedAt); + return ApiResponse.success(pbfInfoService.list(queryWrapper)); + } +} diff --git a/src/main/java/com/yj/earth/business/domain/PbfInfo.java b/src/main/java/com/yj/earth/business/domain/PbfInfo.java new file mode 100644 index 0000000..f6e0e07 --- /dev/null +++ b/src/main/java/com/yj/earth/business/domain/PbfInfo.java @@ -0,0 +1,37 @@ +package com.yj.earth.business.domain; + +import com.baomidou.mybatisplus.annotation.FieldFill; +import com.baomidou.mybatisplus.annotation.IdType; +import com.baomidou.mybatisplus.annotation.TableField; +import com.baomidou.mybatisplus.annotation.TableId; +import com.baomidou.mybatisplus.annotation.TableName; + +import java.io.Serializable; +import java.time.LocalDateTime; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; +import lombok.Getter; +import lombok.Setter; +import lombok.experimental.Accessors; + +@Data +public class PbfInfo implements Serializable { + + private static final long serialVersionUID = 1L; + + @Schema(description = "主键") + private String id; + @Schema(description = "文件路径") + private String path; + @Schema(description = "文件名称") + private String name; + @Schema(description = "是否启用") + private Integer isEnable; + @Schema(description = "创建时间") + @TableField(fill = FieldFill.INSERT) + private LocalDateTime createdAt; + @Schema(description = "更新时间") + @TableField(fill = FieldFill.UPDATE) + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/yj/earth/business/mapper/PbfInfoMapper.java b/src/main/java/com/yj/earth/business/mapper/PbfInfoMapper.java new file mode 100644 index 0000000..3e4528a --- /dev/null +++ b/src/main/java/com/yj/earth/business/mapper/PbfInfoMapper.java @@ -0,0 +1,18 @@ +package com.yj.earth.business.mapper; + +import com.yj.earth.business.domain.PbfInfo; +import com.baomidou.mybatisplus.core.mapper.BaseMapper; +import org.apache.ibatis.annotations.Mapper; + +/** + *

+ * Mapper 接口 + *

+ * + * @author 周志雄 + * @since 2025-09-30 + */ +@Mapper +public interface PbfInfoMapper extends BaseMapper { + +} diff --git a/src/main/java/com/yj/earth/business/service/PbfInfoService.java b/src/main/java/com/yj/earth/business/service/PbfInfoService.java new file mode 100644 index 0000000..d7c9d9a --- /dev/null +++ b/src/main/java/com/yj/earth/business/service/PbfInfoService.java @@ -0,0 +1,16 @@ +package com.yj.earth.business.service; + +import com.yj.earth.business.domain.PbfInfo; +import com.baomidou.mybatisplus.extension.service.IService; + +/** + *

+ * 服务类 + *

+ * + * @author 周志雄 + * @since 2025-09-30 + */ +public interface PbfInfoService extends IService { + +} diff --git a/src/main/java/com/yj/earth/business/service/impl/PbfInfoServiceImpl.java b/src/main/java/com/yj/earth/business/service/impl/PbfInfoServiceImpl.java new file mode 100644 index 0000000..8e648e3 --- /dev/null +++ b/src/main/java/com/yj/earth/business/service/impl/PbfInfoServiceImpl.java @@ -0,0 +1,20 @@ +package com.yj.earth.business.service.impl; + +import com.yj.earth.business.domain.PbfInfo; +import com.yj.earth.business.mapper.PbfInfoMapper; +import com.yj.earth.business.service.PbfInfoService; +import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl; +import org.springframework.stereotype.Service; + +/** + *

+ * 服务实现类 + *

+ * + * @author 周志雄 + * @since 2025-09-30 + */ +@Service +public class PbfInfoServiceImpl extends ServiceImpl implements PbfInfoService { + +} diff --git a/src/main/java/com/yj/earth/common/config/SaTokenConfig.java b/src/main/java/com/yj/earth/common/config/SaTokenConfig.java index 98d0ec4..49f0239 100644 --- a/src/main/java/com/yj/earth/common/config/SaTokenConfig.java +++ b/src/main/java/com/yj/earth/common/config/SaTokenConfig.java @@ -31,6 +31,7 @@ public class SaTokenConfig implements WebMvcConfigurer { excludePathPatterns.add("/iconLibrary/data/icon/**"); excludePathPatterns.add("/militaryLibrary/data/military/**"); excludePathPatterns.add("/modelLibrary/data/**"); + excludePathPatterns.add("/**"); // 注册 Sa-Token 拦截器 registry.addInterceptor(new SaInterceptor(handle -> { diff --git a/src/main/java/com/yj/earth/common/convert/MilitaryConverter.java b/src/main/java/com/yj/earth/common/convert/MilitaryConverter.java new file mode 100644 index 0000000..6700dc8 --- /dev/null +++ b/src/main/java/com/yj/earth/common/convert/MilitaryConverter.java @@ -0,0 +1,293 @@ +package com.yj.earth.common.convert; + +import java.nio.charset.StandardCharsets; +import java.sql.*; +import java.time.LocalDateTime; +import java.util.Base64; + +public class MilitaryConverter { + private static final String JDBC_DRIVER = "org.sqlite.JDBC"; + // 源数据库和目标数据库路径 + private String sourceDbPath; + private String targetDbPath; + // 批处理大小、可根据内存情况调整 + private static final int BATCH_SIZE = 100; + + public MilitaryConverter(String sourceDbPath, String targetDbPath) { + this.sourceDbPath = sourceDbPath; + this.targetDbPath = targetDbPath; + } + + public void convert() { + Connection sourceConn = null; + Connection targetConn = null; + try { + // 加载驱动 + Class.forName(JDBC_DRIVER); + // 连接源数据库和目标数据库 + sourceConn = DriverManager.getConnection("jdbc:sqlite:" + sourceDbPath); + targetConn = DriverManager.getConnection("jdbc:sqlite:" + targetDbPath); + // 禁用自动提交、以便在出现错误时可以回滚 + targetConn.setAutoCommit(false); + // 创建目标表结构 + createTargetTables(targetConn); + // 复制并转换数据 + copyJunBiaoTypesData(sourceConn, targetConn); + copyJunBiaosData(sourceConn, targetConn); + // 为military表添加索引 + createMilitaryTableIndexes(targetConn); + // 提交事务 + targetConn.commit(); + System.out.println("数据库转换成功!"); + } catch (Exception e) { + e.printStackTrace(); + try { + if (targetConn != null) { + targetConn.rollback(); + System.out.println("转换失败、已回滚操作!"); + } + } catch (SQLException ex) { + ex.printStackTrace(); + } + } finally { + // 关闭连接 + try { + if (sourceConn != null) sourceConn.close(); + if (targetConn != null) targetConn.close(); + } catch (SQLException e) { + e.printStackTrace(); + } + } + } + + private void createTargetTables(Connection conn) throws SQLException { + System.out.println("开始创建目标表结构..."); + Statement stmt = conn.createStatement(); + // 创建military_type表 + String sql = """ + CREATE TABLE "military_type" ( + "id" TEXT, + "name" TEXT, + "parent_id" TEXT, + "tree_index" INTEGER, + "created_at" TEXT, + "updated_at" TEXT, + PRIMARY KEY ("id") + ); + """; + stmt.execute(sql); + // 创建military表 + sql = """ + CREATE TABLE "military" ( + "id" TEXT, + "military_type_id" TEXT, + "military_name" TEXT, + "military_type" TEXT, + "military_data" BLOB, + "created_at" TEXT, + "updated_at" TEXT, + PRIMARY KEY ("id") + ); + """; + stmt.execute(sql); + stmt.close(); + System.out.println("目标表结构创建完成"); + } + + /** + * 为 military 表创建索引 + */ + private void createMilitaryTableIndexes(Connection conn) throws SQLException { + System.out.println("开始创建索引..."); + Statement stmt = conn.createStatement(); + + String sql = """ + CREATE INDEX idx_military_covering ON military( + military_type_id, + id, + military_name, + military_type, + created_at, + updated_at + ); + """; + stmt.execute(sql); + + stmt.close(); + System.out.println("military表索引创建完成"); + } + + private int getTotalRecords(Connection conn, String tableName) throws SQLException { + // 只统计未删除的数据 + PreparedStatement stmt = conn.prepareStatement( + "SELECT COUNT(*) AS total FROM " + tableName + " WHERE deleted_at IS NULL" + ); + ResultSet rs = stmt.executeQuery(); + int total = rs.next() ? rs.getInt("total") : 0; + rs.close(); + stmt.close(); + return total; + } + + /** + * 从jun_biao_types表复制数据到military_type表 + */ + private void copyJunBiaoTypesData(Connection sourceConn, Connection targetConn) throws SQLException { + int totalRecords = getTotalRecords(sourceConn, "jun_biao_types"); + System.out.println("开始转换 jun_biao_types 表数据、共" + totalRecords + "条记录"); + + // 查询未删除的类型数据 + PreparedStatement sourceStmt = sourceConn.prepareStatement( + "SELECT * FROM jun_biao_types WHERE deleted_at IS NULL" + ); + ResultSet rs = sourceStmt.executeQuery(); + + PreparedStatement targetStmt = targetConn.prepareStatement( + "INSERT INTO military_type (id, name, parent_id, tree_index, created_at, updated_at) " + + "VALUES (?, ?, ?, ?, ?, ?)" + ); + int count = 0; + + while (rs.next()) { + targetStmt.setString(1, rs.getString("type_id")); + targetStmt.setString(2, rs.getString("type_name")); + if ("-1".equals(rs.getString("p_id"))) { + targetStmt.setNull(3, Types.VARCHAR); + } else { + targetStmt.setString(3, rs.getString("p_id")); + targetStmt.setString(3, rs.getString("p_id")); + } + targetStmt.setInt(4, 0); + targetStmt.setObject(5, LocalDateTime.now()); + targetStmt.setObject(6, LocalDateTime.now()); + + // 添加到批处理 + targetStmt.addBatch(); + count++; + + // 每达到批处理大小或最后一条记录时执行批处理 + if (count % BATCH_SIZE == 0 || count == totalRecords) { + targetStmt.executeBatch(); + displayProgress(count, totalRecords, "jun_biao_types 表"); + } + } + + System.out.println("\n成功转换 jun_biao_types 表数据:" + count + "条记录"); + rs.close(); + sourceStmt.close(); + targetStmt.close(); + } + + /** + * 从jun_biaos表复制数据到military表 + */ + private void copyJunBiaosData(Connection sourceConn, Connection targetConn) throws SQLException { + int totalRecords = getTotalRecords(sourceConn, "jun_biaos"); + System.out.println("开始转换 jun_biaos 表数据、共" + totalRecords + "条记录"); + + // 对于大字段、使用向前滚动的结果集、避免缓存所有数据 + PreparedStatement sourceStmt = sourceConn.prepareStatement( + "SELECT * FROM jun_biaos WHERE deleted_at IS NULL", + ResultSet.TYPE_FORWARD_ONLY, + ResultSet.CONCUR_READ_ONLY + ); + sourceStmt.setFetchSize(100); + ResultSet rs = sourceStmt.executeQuery(); + + PreparedStatement targetStmt = targetConn.prepareStatement( + "INSERT INTO military (id, military_type_id, military_name, military_type, military_data, " + + "created_at, updated_at) " + + "VALUES (?, ?, ?, ?, ?, ?, ?)" + ); + + int count = 0; + while (rs.next()) { + targetStmt.setString(1, rs.getString("jun_biao_id")); // 关联ID对应 + targetStmt.setString(2, rs.getString("p_id")); // 类型ID对应 + targetStmt.setString(3, rs.getString("name")); // 名称对应 + targetStmt.setString(4, "svg"); // 内容类型对应 + byte[] dataBytes = rs.getBytes("data"); // 二进制数据对应 + if (dataBytes != null) { + targetStmt.setBytes(5, convertBase64ToPlainSvg(dataBytes)); + } else { + targetStmt.setNull(5, Types.BLOB); + } + targetStmt.setObject(6, LocalDateTime.now()); // 使用原始创建时间 + targetStmt.setObject(7, LocalDateTime.now()); // 使用原始更新时间 + + // 添加到批处理 + targetStmt.addBatch(); + count++; + // 执行批处理 + if (count % BATCH_SIZE == 0 || count == totalRecords) { + targetStmt.executeBatch(); + displayProgress(count, totalRecords, "jun_biaos 表"); + } + } + + System.out.println("\n成功转换 jun_biaos 表数据:" + count + "条记录"); + rs.close(); + sourceStmt.close(); + targetStmt.close(); + } + + /** + * 显示进度信息 + */ + private void displayProgress(int current, int total, String tableName) { + double percentage = (double) current / total * 100; + // 清除当前行并显示进度 + System.out.printf("\r%s: 已完成 %.1f%% (%d/%d条)", tableName, percentage, current, total); + } + + + // Base64 SVG的前缀标识 + private static final String SVG_BASE64_PREFIX = "data:image/svg+xml;base64,"; + + /** + * 将Base64格式的SVG字节数组转换为明文SVG字节数组 + * + * @param base64SvgBytes 包含Base64编码的SVG字节数组(可带前缀) + * @return 解码后的明文SVG字节数组 + * @throws IllegalArgumentException 如果输入不是有效的Base64格式或为空 + */ + public static byte[] convertBase64ToPlainSvg(byte[] base64SvgBytes) { + // 验证输入参数 + if (base64SvgBytes == null || base64SvgBytes.length == 0) { + throw new IllegalArgumentException("输入的Base64 SVG字节数组不能为空"); + } + + // 将字节数组转换为字符串、处理可能存在的前缀 + String base64SvgStr = new String(base64SvgBytes, StandardCharsets.UTF_8); + String pureBase64Str = base64SvgStr; + + // 移除前缀(如果存在) + if (base64SvgStr.startsWith(SVG_BASE64_PREFIX)) { + pureBase64Str = base64SvgStr.substring(SVG_BASE64_PREFIX.length()); + } + + try { + // 执行Base64解码并返回字节数组 + return Base64.getDecoder().decode(pureBase64Str); + } catch (IllegalArgumentException e) { + throw new IllegalArgumentException("无效的Base64格式: " + e.getMessage(), e); + } + } + + public static void main(String[] args) { + // 源数据库路径 + String sourcePath = "F:\\YJEarth新.junbiao"; + // 目标数据库路径 + String targetPath = "F:\\通用军标库.junbiao"; + System.out.println("开始数据库转换..."); + System.out.println("源数据库: " + sourcePath); + System.out.println("目标数据库: " + targetPath); + long startTime = System.currentTimeMillis(); + // 创建转换器并执行转换 + MilitaryConverter converter = new MilitaryConverter(sourcePath, targetPath); + converter.convert(); + long endTime = System.currentTimeMillis(); + double elapsedTime = (endTime - startTime) / 1000.0; + System.out.printf("转换完成、耗时: %.2f秒%n", elapsedTime); + } +} diff --git a/src/main/java/com/yj/earth/common/util/SQLiteConverter.java b/src/main/java/com/yj/earth/common/convert/ModelConverter.java similarity index 97% rename from src/main/java/com/yj/earth/common/util/SQLiteConverter.java rename to src/main/java/com/yj/earth/common/convert/ModelConverter.java index 6dcd914..f99f338 100644 --- a/src/main/java/com/yj/earth/common/util/SQLiteConverter.java +++ b/src/main/java/com/yj/earth/common/convert/ModelConverter.java @@ -1,11 +1,9 @@ -package com.yj.earth.common.util; - -import cn.hutool.core.lang.UUID; +package com.yj.earth.common.convert; import java.sql.*; import java.time.LocalDateTime; -public class SQLiteConverter { +public class ModelConverter { private static final String JDBC_DRIVER = "org.sqlite.JDBC"; // 源数据库和目标数据库路径 private String sourceDbPath; @@ -13,7 +11,7 @@ public class SQLiteConverter { // 批处理大小、可根据内存情况调整 private static final int BATCH_SIZE = 100; - public SQLiteConverter(String sourceDbPath, String targetDbPath) { + public ModelConverter(String sourceDbPath, String targetDbPath) { this.sourceDbPath = sourceDbPath; this.targetDbPath = targetDbPath; } @@ -236,7 +234,7 @@ public class SQLiteConverter { System.out.println("目标数据库: " + targetPath); long startTime = System.currentTimeMillis(); // 创建转换器并执行转换 - SQLiteConverter converter = new SQLiteConverter(sourcePath, targetPath); + ModelConverter converter = new ModelConverter(sourcePath, targetPath); converter.convert(); long endTime = System.currentTimeMillis(); double elapsedTime = (endTime - startTime) / 1000.0; diff --git a/src/main/java/com/yj/earth/common/util/CodeUtil.java b/src/main/java/com/yj/earth/common/util/CodeUtil.java index ee3117c..920e159 100644 --- a/src/main/java/com/yj/earth/common/util/CodeUtil.java +++ b/src/main/java/com/yj/earth/common/util/CodeUtil.java @@ -34,7 +34,7 @@ public class CodeUtil { } // 传入需要生成代码的表名 - Generation("web_source"); + Generation("pbf_info"); } public static void Generation(String... tableName) { diff --git a/src/main/java/com/yj/earth/common/util/GdalJsonConverter.java b/src/main/java/com/yj/earth/common/util/GdalJsonConverter.java new file mode 100644 index 0000000..033ee07 --- /dev/null +++ b/src/main/java/com/yj/earth/common/util/GdalJsonConverter.java @@ -0,0 +1,470 @@ +package com.yj.earth.common.util; + +import org.gdal.gdal.gdal; +import org.gdal.ogr.*; +import org.gdal.osr.CoordinateTransformation; +import org.gdal.osr.SpatialReference; +import org.json.JSONArray; +import org.json.JSONException; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +import static org.gdal.ogr.ogrConstants.*; + +/** + * GDAL地理数据转JSON工具类 + */ +import org.gdal.gdal.gdal; +import org.gdal.ogr.*; +import org.gdal.osr.SpatialReference; +import org.json.JSONArray; +import org.json.JSONObject; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * GDAL地理数据转JSON工具类 + */ +public class GdalJsonConverter { + + static { + // 初始化GDAL + gdal.AllRegister(); + } + + /** + * 将地理数据文件转换为指定结构的JSON + * @param filePath 文件绝对路径 + * @return JSON字符串 + */ + public static String convertToJson(String filePath) { + return convertToJson(filePath, ""); + } + + /** + * 将地理数据文件转换为指定结构的JSON + * @param filePath 文件绝对路径 + * @param targetCrs 目标坐标系(如:"EPSG:4326"),为空则不转换 + * @return JSON字符串 + */ + public static String convertToJson(String filePath, String targetCrs) { + try { + // 打开数据源 + DataSource dataSource = ogr.Open(filePath, 0); + if (dataSource == null) { + throw new RuntimeException("无法打开文件: " + filePath); + } + + JSONObject result = new JSONObject(); + JSONArray listArray = new JSONArray(); + + // 获取所有图层 + int layerCount = dataSource.GetLayerCount(); + for (int i = 0; i < layerCount; i++) { + Layer layer = dataSource.GetLayer(i); + String layerName = layer.GetName(); + + JSONObject featureCollection = createFeatureCollection(layer, layerName, targetCrs); + if (featureCollection != null) { + listArray.put(featureCollection); + } + } + + result.put("list", listArray); + dataSource.delete(); + + return result.toString(2); // 缩进2个空格,美化输出 + + } catch (Exception e) { + throw new RuntimeException("转换失败: " + e.getMessage(), e); + } + } + + /** + * 创建FeatureCollection对象 + */ + private static JSONObject createFeatureCollection(Layer layer, String layerName, String targetCrs) throws JSONException { + JSONObject featureCollection = new JSONObject(); + featureCollection.put("type", "FeatureCollection"); + featureCollection.put("name", layerName); + + // 获取源坐标系 + String crsSrc = getLayerCrs(layer); + featureCollection.put("crs_src", crsSrc != null ? crsSrc : ""); + + // 设置目标坐标系 + featureCollection.put("crs_dst", targetCrs != null ? targetCrs : ""); + + JSONArray featuresArray = new JSONArray(); + Feature feature; + + // 重置图层读取位置 + layer.ResetReading(); + + // 获取图层定义,用于字段信息 + FeatureDefn layerDefn = layer.GetLayerDefn(); + + // 如果需要坐标转换,创建坐标转换对象 + SpatialReference sourceSrs = layer.GetSpatialRef(); + SpatialReference targetSrs = null; + CoordinateTransformation coordTrans = null; + + if (targetCrs != null && !targetCrs.isEmpty() && sourceSrs != null) { + try { + targetSrs = new SpatialReference(); + if (targetCrs.startsWith("EPSG:")) { + int epsgCode = Integer.parseInt(targetCrs.substring(5)); + targetSrs.ImportFromEPSG(epsgCode); + } else { + targetSrs.ImportFromProj4(targetCrs); + } + coordTrans = new CoordinateTransformation(sourceSrs, targetSrs); + } catch (Exception e) { + System.err.println("坐标转换初始化失败: " + e.getMessage()); + } + } + + while ((feature = layer.GetNextFeature()) != null) { + JSONObject featureObj = createFeature(feature, layerDefn, coordTrans, targetSrs != null); + if (featureObj != null) { + featuresArray.put(featureObj); + } + feature.delete(); + } + + // 清理资源 + if (coordTrans != null) { + coordTrans.delete(); + } + if (targetSrs != null) { + targetSrs.delete(); + } + + featureCollection.put("features", featuresArray); + return featureCollection; + } + + /** + * 获取图层的坐标系信息 + */ + private static String getLayerCrs(Layer layer) { + try { + SpatialReference srs = layer.GetSpatialRef(); + if (srs != null) { + // 优先尝试获取PROJ4字符串 + String proj4 = srs.ExportToProj4(); + if (proj4 != null && !proj4.trim().isEmpty()) { + return proj4.trim(); + } + + // 如果PROJ4为空,尝试获取WKT + String wkt = srs.ExportToWkt(); + if (wkt != null && !wkt.trim().isEmpty()) { + return wkt.trim(); + } + + // 最后尝试获取EPSG代码 + String authorityCode = srs.GetAuthorityCode(null); + String authorityName = srs.GetAuthorityName(null); + if (authorityName != null && authorityCode != null) { + return authorityName + ":" + authorityCode; + } + } + } catch (Exception e) { + System.err.println("获取坐标系信息失败: " + e.getMessage()); + } + return null; + } + + /** + * 创建单个Feature对象 + */ + private static JSONObject createFeature(Feature feature, FeatureDefn layerDefn, + CoordinateTransformation coordTrans, boolean transformCoords) throws JSONException { + JSONObject featureObj = new JSONObject(); + featureObj.put("type", "Feature"); + + // 处理属性字段 + JSONObject properties = new JSONObject(); + int fieldCount = layerDefn.GetFieldCount(); + + for (int i = 0; i < fieldCount; i++) { + FieldDefn fieldDefn = layerDefn.GetFieldDefn(i); + String fieldName = fieldDefn.GetName(); + Object fieldValue = getFieldValue(feature, fieldDefn, i); + + if (fieldValue != null) { + properties.put(fieldName, fieldValue); + } + } + + featureObj.put("properties", properties); + + // 处理几何图形 + Geometry geometry = feature.GetGeometryRef(); + if (geometry != null && !geometry.IsEmpty()) { + Geometry geomToUse = geometry; + + // 如果需要坐标转换 + if (transformCoords && coordTrans != null) { + try { + Geometry transformedGeom = geometry.Clone(); + transformedGeom.Transform(coordTrans); + geomToUse = transformedGeom; + } catch (Exception e) { + System.err.println("坐标转换失败: " + e.getMessage()); + // 如果转换失败,使用原始几何 + geomToUse = geometry; + } + } + + JSONObject geometryObj = convertGeometryToJson(geomToUse); + featureObj.put("geometry", geometryObj); + + // 清理临时几何对象 + if (geomToUse != geometry) { + geomToUse.delete(); + } + } else { + // 如果没有几何信息,创建空的MultiPolygon + featureObj.put("geometry", createEmptyMultiPolygon()); + } + + return featureObj; + } + + /** + * 获取字段值 + */ + private static Object getFieldValue(Feature feature, FieldDefn fieldDefn, int fieldIndex) { + int fieldType = fieldDefn.GetFieldType(); + + if (feature.IsFieldSet(fieldIndex)) { + switch (fieldType) { + case ogr.OFTInteger: + return feature.GetFieldAsInteger(fieldIndex); + case ogr.OFTInteger64: + return feature.GetFieldAsInteger64(fieldIndex); + case ogr.OFTReal: + return feature.GetFieldAsDouble(fieldIndex); + case ogr.OFTString: + return feature.GetFieldAsString(fieldIndex); + case ogr.OFTDate: + case ogr.OFTTime: + case ogr.OFTDateTime: + return feature.GetFieldAsString(fieldIndex); + default: + return feature.GetFieldAsString(fieldIndex); + } + } + return null; + } + + /** + * 将几何图形转换为JSON + */ + private static JSONObject convertGeometryToJson(Geometry geometry) throws JSONException { + JSONObject geometryObj = new JSONObject(); + String geometryType = geometry.GetGeometryName(); + + // 根据几何类型处理 + switch (geometry.GetGeometryType()) { + case wkbPoint: + case wkbPoint25D: + geometryObj.put("type", "Point"); + geometryObj.put("coordinates", getPointCoordinates(geometry)); + break; + case wkbLineString: + case wkbLineString25D: + geometryObj.put("type", "LineString"); + geometryObj.put("coordinates", getLineStringCoordinates(geometry)); + break; + case wkbPolygon: + case wkbPolygon25D: + geometryObj.put("type", "Polygon"); + geometryObj.put("coordinates", getPolygonCoordinates(geometry)); + break; + case wkbMultiPoint: + case wkbMultiPoint25D: + geometryObj.put("type", "MultiPoint"); + geometryObj.put("coordinates", getMultiPointCoordinates(geometry)); + break; + case wkbMultiLineString: + case wkbMultiLineString25D: + geometryObj.put("type", "MultiLineString"); + geometryObj.put("coordinates", getMultiLineStringCoordinates(geometry)); + break; + case wkbMultiPolygon: + case wkbMultiPolygon25D: + geometryObj.put("type", "MultiPolygon"); + geometryObj.put("coordinates", getMultiPolygonCoordinates(geometry)); + break; + case wkbGeometryCollection: + case wkbGeometryCollection25D: + geometryObj.put("type", "GeometryCollection"); + geometryObj.put("geometries", getGeometryCollection(geometry)); + break; + default: + // 默认使用WKT格式 + geometryObj.put("type", geometryType); + geometryObj.put("wkt", geometry.ExportToWkt()); + break; + } + + return geometryObj; + } + + /** + * 获取点坐标 + */ + private static JSONArray getPointCoordinates(Geometry geometry) throws JSONException { + JSONArray coords = new JSONArray(); + coords.put(geometry.GetX()); + coords.put(geometry.GetY()); + if (geometry.GetGeometryType() == wkbPoint25D) { + coords.put(geometry.GetZ()); + } + return coords; + } + + /** + * 获取线坐标 + */ + private static JSONArray getLineStringCoordinates(Geometry geometry) throws JSONException { + JSONArray coords = new JSONArray(); + int pointCount = geometry.GetPointCount(); + for (int i = 0; i < pointCount; i++) { + JSONArray point = new JSONArray(); + point.put(geometry.GetX(i)); + point.put(geometry.GetY(i)); + if (geometry.GetGeometryType() == wkbLineString25D) { + point.put(geometry.GetZ(i)); + } + coords.put(point); + } + return coords; + } + + /** + * 获取多边形坐标 + */ + private static JSONArray getPolygonCoordinates(Geometry geometry) throws JSONException { + JSONArray rings = new JSONArray(); + + // 外环 + Geometry exteriorRing = geometry.GetGeometryRef(0); + rings.put(getLineStringCoordinates(exteriorRing)); + + // 内环(如果有) + int ringCount = geometry.GetGeometryCount(); + for (int i = 1; i < ringCount; i++) { + Geometry interiorRing = geometry.GetGeometryRef(i); + rings.put(getLineStringCoordinates(interiorRing)); + } + + return rings; + } + + /** + * 获取多点坐标 + */ + private static JSONArray getMultiPointCoordinates(Geometry geometry) throws JSONException { + JSONArray points = new JSONArray(); + int geometryCount = geometry.GetGeometryCount(); + for (int i = 0; i < geometryCount; i++) { + Geometry point = geometry.GetGeometryRef(i); + points.put(getPointCoordinates(point)); + } + return points; + } + + /** + * 获取多线坐标 + */ + private static JSONArray getMultiLineStringCoordinates(Geometry geometry) throws JSONException { + JSONArray lines = new JSONArray(); + int geometryCount = geometry.GetGeometryCount(); + for (int i = 0; i < geometryCount; i++) { + Geometry line = geometry.GetGeometryRef(i); + lines.put(getLineStringCoordinates(line)); + } + return lines; + } + + /** + * 获取多多边形坐标 + */ + private static JSONArray getMultiPolygonCoordinates(Geometry geometry) throws JSONException { + JSONArray polygons = new JSONArray(); + int geometryCount = geometry.GetGeometryCount(); + for (int i = 0; i < geometryCount; i++) { + Geometry polygon = geometry.GetGeometryRef(i); + polygons.put(getPolygonCoordinates(polygon)); + } + return polygons; + } + + /** + * 获取几何集合 + */ + private static JSONArray getGeometryCollection(Geometry geometry) throws JSONException { + JSONArray geometries = new JSONArray(); + int geometryCount = geometry.GetGeometryCount(); + for (int i = 0; i < geometryCount; i++) { + Geometry geom = geometry.GetGeometryRef(i); + geometries.put(convertGeometryToJson(geom)); + } + return geometries; + } + + /** + * 创建空的MultiPolygon几何 + */ + private static JSONObject createEmptyMultiPolygon() throws JSONException { + JSONObject geometry = new JSONObject(); + geometry.put("type", "MultiPolygon"); + geometry.put("coordinates", new JSONArray()); + return geometry; + } + + /** + * 获取文件信息(辅助方法) + */ + public static Map getFileInfo(String filePath) { + Map info = new HashMap<>(); + try { + DataSource dataSource = ogr.Open(filePath, 0); + if (dataSource != null) { + info.put("driver", dataSource.GetDriver().GetName()); + info.put("layerCount", dataSource.GetLayerCount()); + + List> layers = new ArrayList<>(); + for (int i = 0; i < dataSource.GetLayerCount(); i++) { + Layer layer = dataSource.GetLayer(i); + Map layerInfo = new HashMap<>(); + layerInfo.put("name", layer.GetName()); + layerInfo.put("featureCount", layer.GetFeatureCount()); + layerInfo.put("geometryType", ogr.GeometryTypeToName(layer.GetGeomType())); + + // 添加坐标系信息 + String crs = getLayerCrs(layer); + layerInfo.put("crs", crs != null ? crs : "未知"); + + layers.add(layerInfo); + } + info.put("layers", layers); + dataSource.delete(); + } + } catch (Exception e) { + info.put("error", e.getMessage()); + } + return info; + } +} diff --git a/src/main/java/com/yj/earth/common/util/GdalUtil.java b/src/main/java/com/yj/earth/common/util/GdalUtil.java deleted file mode 100644 index acb9875..0000000 --- a/src/main/java/com/yj/earth/common/util/GdalUtil.java +++ /dev/null @@ -1,247 +0,0 @@ -package com.yj.earth.common.util; - -import org.gdal.gdal.gdal; -import org.gdal.ogr.*; -import com.fasterxml.jackson.databind.ObjectMapper; -import com.fasterxml.jackson.core.JsonProcessingException; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -public class GdalUtil { - - // 静态初始化ObjectMapper,避免重复创建 - private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper(); - - // 静态初始化GDAL - static { - // 可以考虑设置缓存目录来提升性能 - gdal.SetConfigOption("GDAL_CACHEMAX", "1024"); // 设置1GB缓存 - gdal.SetConfigOption("PROJ_LIB", "./lib"); - gdal.AllRegister(); - } - - /** - * 读取矢量文件、将每个图层转换为符合指定格式的 GeoJSON - */ - public static String readVectorToSpecifiedGeoJson(String filePath) { - DataSource dataSource = null; - try { - // 打开矢量文件,使用只读模式 - dataSource = ogr.Open(filePath, 0); - if (dataSource == null) { - throw new RuntimeException("无法打开矢量文件: " + filePath + ", 错误信息: " + gdal.GetLastErrorMsg()); - } - - // 创建结果结构,预先估计容量 - Map result = new HashMap<>(2); - int layerCount = dataSource.GetLayerCount(); - List> layerList = new ArrayList<>(layerCount); - - // 处理每个图层 - for (int i = 0; i < layerCount; i++) { - Layer layer = dataSource.GetLayer(i); - if (layer == null) continue; - - // 为当前图层创建符合格式的 GeoJSON - Map layerGeoJson = convertLayerToSpecifiedFormat(layer); - if (layerGeoJson != null) { - layerList.add(layerGeoJson); - } - // 提前释放图层资源 - layer.delete(); - } - - result.put("list", layerList); - - // 使用单例ObjectMapper转换为JSON字符串 - return OBJECT_MAPPER.writeValueAsString(result); - - } catch (Exception e) { - throw new RuntimeException("处理矢量文件时发生错误: " + e.getMessage() + ", 详细错误: " + gdal.GetLastErrorMsg(), e); - } finally { - // 确保数据源被正确释放 - if (dataSource != null) { - dataSource.delete(); - } - } - } - - /** - * 将单个图层转换为符合指定格式的 GeoJSON - */ - private static Map convertLayerToSpecifiedFormat(Layer layer) { - try { - // 获取要素数量,预分配集合大小 - long featureCount = layer.GetFeatureCount(); - if (featureCount <= 0) { - return null; // 跳过空图层 - } - - // 创建符合格式的 GeoJSON FeatureCollection - Map geoJson = new HashMap<>(3); - geoJson.put("type", "FeatureCollection"); - geoJson.put("name", layer.GetName()); - - // 预分配要素列表容量 - List> features = new ArrayList<>((int) Math.min(featureCount, Integer.MAX_VALUE)); - - // 获取字段定义 - FeatureDefn featureDefn = layer.GetLayerDefn(); - int fieldCount = featureDefn.GetFieldCount(); - - // 缓存字段信息,避免重复获取 - String[] fieldNames = new String[fieldCount]; - int[] fieldTypes = new int[fieldCount]; - for (int j = 0; j < fieldCount; j++) { - FieldDefn fieldDef = featureDefn.GetFieldDefn(j); - fieldNames[j] = fieldDef.GetName(); - fieldTypes[j] = fieldDef.GetFieldType(); - } - - // 读取图层中的所有要素 - layer.ResetReading(); - Feature feature; - - while ((feature = layer.GetNextFeature()) != null) { - Map featureMap = new HashMap<>(3); - featureMap.put("type", "Feature"); - - // 处理属性 - Map properties = new HashMap<>(fieldCount); - for (int j = 0; j < fieldCount; j++) { - properties.put(fieldNames[j], getFieldValue(feature, j, fieldTypes[j])); - } - featureMap.put("properties", properties); - - // 处理几何信息 - 直接构建Map而不是先转JSON再解析 - Geometry geometry = feature.GetGeometryRef(); - featureMap.put("geometry", convertGeometryToMap(geometry)); - - features.add(featureMap); - feature.delete(); // 及时释放要素资源 - } - - geoJson.put("features", features); - return geoJson; - - } catch (Exception e) { - System.err.println("转换图层 " + layer.GetName() + " 时发生错误: " + e.getMessage() + ", 详细错误: " + gdal.GetLastErrorMsg()); - return null; - } - } - - /** - * 将Geometry直接转换为Map,避免JSON序列化/反序列化开销 - */ - private static Map convertGeometryToMap(Geometry geometry) { - if (geometry == null) { - return createEmptyMultiPolygon(); - } - - try { - String geometryType = geometry.GetGeometryName(); - Map geometryMap = new HashMap<>(2); - - // 如果不是MultiPolygon,尝试转换 - if (!"MULTIPOLYGON".equals(geometryType)) { - // 转换为MultiPolygon - Geometry multiPolygon = geometry.MakeValid(); - if (!"MULTIPOLYGON".equals(multiPolygon.GetGeometryName())) { - multiPolygon = geometry.Buffer(0); // 修复几何问题 - } - if (!"MULTIPOLYGON".equals(multiPolygon.GetGeometryName())) { - // 如果仍然不是MultiPolygon,创建空的 - return createEmptyMultiPolygon(); - } - geometry = multiPolygon; - } - - geometryMap.put("type", "MultiPolygon"); - geometryMap.put("coordinates", getCoordinates(geometry)); - - return geometryMap; - } catch (Exception e) { - return createEmptyMultiPolygon(); - } - } - - /** - * 直接获取几何坐标,避免JSON转换 - */ - private static List getCoordinates(Geometry geometry) { - List coordinates = new ArrayList<>(); - - if (geometry == null) { - return coordinates; - } - - int geometryCount = geometry.GetGeometryCount(); - for (int i = 0; i < geometryCount; i++) { - Geometry polygon = geometry.GetGeometryRef(i); - if (polygon == null) continue; - - List polygonCoords = new ArrayList<>(); - int ringCount = polygon.GetGeometryCount(); - for (int j = 0; j < ringCount; j++) { - Geometry ring = polygon.GetGeometryRef(j); - if (ring == null) continue; - - List> ringCoords = new ArrayList<>(); - int pointCount = ring.GetPointCount(); - for (int k = 0; k < pointCount; k++) { - double[] point = ring.GetPoint(k); - List pointCoords = new ArrayList<>(2); - pointCoords.add(point[0]); - pointCoords.add(point[1]); - ringCoords.add(pointCoords); - } - polygonCoords.add(ringCoords); - } - coordinates.add(polygonCoords); - } - - return coordinates; - } - - /** - * 获取字段值、根据字段类型处理 - */ - private static Object getFieldValue(Feature feature, int fieldIndex, int fieldType) { - try { - switch (fieldType) { - case ogr.OFTInteger: - return feature.GetFieldAsInteger(fieldIndex); - case ogr.OFTInteger64: - return feature.GetFieldAsInteger64(fieldIndex); - case ogr.OFTReal: - return feature.GetFieldAsDouble(fieldIndex); - case ogr.OFTString: - case ogr.OFTStringList: - case ogr.OFTWideString: - return feature.GetFieldAsString(fieldIndex); - case ogr.OFTDate: - case ogr.OFTTime: - case ogr.OFTDateTime: - return feature.GetFieldAsString(fieldIndex); - default: - // 对于其他类型,统一返回字符串表示 - return feature.GetFieldAsString(fieldIndex); - } - } catch (Exception e) { - return feature.GetFieldAsString(fieldIndex); - } - } - - /** - * 创建空的 MultiPolygon 几何对象 - */ - private static Map createEmptyMultiPolygon() { - Map geometry = new HashMap<>(2); - geometry.put("type", "MultiPolygon"); - geometry.put("coordinates", new ArrayList<>()); - return geometry; - } -} diff --git a/src/main/java/com/yj/earth/common/util/SQLiteUtil.java b/src/main/java/com/yj/earth/common/util/SQLiteUtil.java index 028dd64..35aae50 100644 --- a/src/main/java/com/yj/earth/common/util/SQLiteUtil.java +++ b/src/main/java/com/yj/earth/common/util/SQLiteUtil.java @@ -89,7 +89,8 @@ public class SQLiteUtil { stmt.execute("PRAGMA temp_store=MEMORY;"); // 临时表/索引存内存(减少磁盘IO) stmt.execute("PRAGMA busy_timeout=2000;"); // 忙等待超时:2秒(避免瞬时并发锁等待) } catch (SQLException e) { - throw new RuntimeException("初始化SQLite数据源失败(路径:" + dbFilePath + ")", e); + e.printStackTrace(); + throw new RuntimeException("初始化SQLite数据源失败(路径:" + dbFilePath + ")+ 原因是:" + e.getMessage()); } return dataSource; diff --git a/src/main/java/com/yj/earth/datasource/DatabaseManager.java b/src/main/java/com/yj/earth/datasource/DatabaseManager.java index f63e370..e9a4d8a 100644 --- a/src/main/java/com/yj/earth/datasource/DatabaseManager.java +++ b/src/main/java/com/yj/earth/datasource/DatabaseManager.java @@ -52,6 +52,7 @@ public class DatabaseManager { classes.add(IconLibrary.class); classes.add(BusinessConfig.class); classes.add(WebSource.class); + classes.add(PbfInfo.class); ENTITY_CLASSES = Collections.unmodifiableList(classes); } diff --git a/src/main/java/com/yj/earth/design/PbfInfo.java b/src/main/java/com/yj/earth/design/PbfInfo.java new file mode 100644 index 0000000..5b9dbeb --- /dev/null +++ b/src/main/java/com/yj/earth/design/PbfInfo.java @@ -0,0 +1,22 @@ +package com.yj.earth.design; + +import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +public class PbfInfo { + @Schema(description = "主键") + private String id; + @Schema(description = "文件路径") + private String path; + @Schema(description = "文件名称") + private String name; + @Schema(description = "是否启用") + private Integer isEnable; + @Schema(description = "创建时间") + private LocalDateTime createdAt; + @Schema(description = "更新时间") + private LocalDateTime updatedAt; +} diff --git a/src/main/java/com/yj/earth/model/RouteResponse.java b/src/main/java/com/yj/earth/model/RouteResponse.java index 26bdb54..7923976 100644 --- a/src/main/java/com/yj/earth/model/RouteResponse.java +++ b/src/main/java/com/yj/earth/model/RouteResponse.java @@ -3,12 +3,37 @@ package com.yj.earth.model; import lombok.AllArgsConstructor; import lombok.Data; +import java.util.ArrayList; import java.util.List; @Data -@AllArgsConstructor public class RouteResponse { - private Double distanceKm; // 距离(公里) - private Double timeMinutes; // 时间(分钟) - private List pathPoints; // 路径点列表 + private List routes; + + public RouteResponse() {} + + public RouteResponse(Double distance, Double time, List points) { + this.routes = new ArrayList<>(); + this.routes.add(new RouteInfo(distance, time, points)); + } + + // 多条路线的构造函数 + public RouteResponse(List routes) { + this.routes = routes; + } + + @Data + public static class RouteInfo { + private Double distance; // 公里 + private Double time; // 分钟 + private List points; + + public RouteInfo() {} + + public RouteInfo(Double distance, Double time, List points) { + this.distance = distance; + this.time = time; + this.points = points; + } + } } diff --git a/src/main/java/com/yj/earth/vo/MilitaryVo.java b/src/main/java/com/yj/earth/vo/MilitaryVo.java index ae5e70c..6d72781 100644 --- a/src/main/java/com/yj/earth/vo/MilitaryVo.java +++ b/src/main/java/com/yj/earth/vo/MilitaryVo.java @@ -8,7 +8,7 @@ import lombok.Data; import java.time.LocalDateTime; @Data -public class MilitaryVo extends Military { +public class MilitaryVo{ @Schema(description = "军标类型名称") private String militaryTypeName; @Schema(description = "军标数据URL") diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 9317c2b..f990055 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -46,4 +46,4 @@ graphhopper: - foot sync: - folder: E:\\地理资源 + folder: F:\POI diff --git a/src/main/resources/data.json b/src/main/resources/data.json new file mode 100644 index 0000000..52d590a --- /dev/null +++ b/src/main/resources/data.json @@ -0,0 +1,123 @@ +{ + "list": [ + { + "type": "FeatureCollection", + "name": "JobDetails", + "features": [ + { + "type": "Feature", + "properties": { + "JobDetails": "执行(Manage Map Cache Tiles): ManageMapCacheTiles 商河电子地图:MapServer 2254.4677204803465;1127.2338602401733;563.61693012008664 3 RECREATE_ALL_TILES \"-180 -90 180 90\" # 要素集", + "JobID": "j140c6b943560494f9ac1ec04f87b9593", + "JobType": "RECREATE_ALL_TILES", + "NumThreads": 3, + "Scales": "2254.4677204803465;1127.2338602401733;563.61693012008664", + "Shape_Area": 64799.99999999998, + "Shape_Length": 1080 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [] + } + }, + { + "type": "Feature", + "properties": { + "JobDetails": "执行(Manage Map Cache Tiles): ManageMapCacheTiles 商河电子地图:MapServer 2254.4677204803465;1127.2338602401733;563.61693012008664 3 RECREATE_ALL_TILES \"116.9772866 37.112894162 117.439131151 37.53690506\" # 要素集", + "JobID": "je0622d35aeda4b8592e766171053dbbd", + "JobType": "RECREATE_ALL_TILES", + "NumThreads": 3, + "Scales": "2254.4677204803465;1127.2338602401733;563.61693012008664", + "Shape_Area": 0.11797651104571212, + "Shape_Length": 2.9442372034773885 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [] + } + } + ] + }, + { + "type": "FeatureCollection", + "name": "JobStatus", + "features": [ + { + "type": "Feature", + "properties": { + "ActualTiles": 2326528, + "CancelJobID": "j140c6b943560494f9ac1ec04f87b9593", + "DoneTasks": 142, + "EndTime": "2023/10/13 14:32:09+00", + "ExpectedTiles": 0, + "FailedTasks": 0, + "JobID": "j140c6b943560494f9ac1ec04f87b9593", + "JobStatus": "CANCELED", + "JobType": "RECREATE_ALL_TILES", + "PercentComplete": 0, + "Scale": 2254.4677204803465, + "Shape_Area": 64799.99999999998, + "Shape_Length": 1080, + "StartTime": "2023/10/13 12:31:12+00", + "TotalTasks": 2097152, + "percent": 0 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [] + } + }, + { + "type": "Feature", + "properties": { + "ActualTiles": 0, + "CancelJobID": "j140c6b943560494f9ac1ec04f87b9593", + "DoneTasks": 0, + "EndTime": "2023/10/13 14:32:09+00", + "ExpectedTiles": 0, + "FailedTasks": 0, + "JobID": "j140c6b943560494f9ac1ec04f87b9593", + "JobStatus": "CANCELED", + "JobType": "RECREATE_ALL_TILES", + "PercentComplete": 0, + "Scale": 1127.2338602401733, + "Shape_Area": 64799.99999999998, + "Shape_Length": 1080, + "StartTime": "2023/10/13 12:31:12+00", + "TotalTasks": 8388608, + "percent": 0 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [] + } + }, + { + "type": "Feature", + "properties": { + "ActualTiles": 0, + "CancelJobID": "j140c6b943560494f9ac1ec04f87b9593", + "DoneTasks": 0, + "EndTime": "2023/10/13 14:32:09+00", + "ExpectedTiles": 0, + "FailedTasks": 0, + "JobID": "j140c6b943560494f9ac1ec04f87b9593", + "JobStatus": "CANCELED", + "JobType": "RECREATE_ALL_TILES", + "PercentComplete": 0, + "Scale": 563.6169301200866, + "Shape_Area": 64799.99999999998, + "Shape_Length": 1080, + "StartTime": "2023/10/13 12:31:12+00", + "TotalTasks": 33554432, + "percent": 0 + }, + "geometry": { + "type": "MultiPolygon", + "coordinates": [] + } + } + ] + } + ] +} diff --git a/src/main/resources/mapper/PbfInfoMapper.xml b/src/main/resources/mapper/PbfInfoMapper.xml new file mode 100644 index 0000000..63d4dc7 --- /dev/null +++ b/src/main/resources/mapper/PbfInfoMapper.xml @@ -0,0 +1,20 @@ + + + + + + + + + + + + + + + + + id, path, name, is_enable, created_at, updated_at + + +