From 7964ce15696b364c29cb5f628bcc40e0596e2b08 Mon Sep 17 00:00:00 2001 From: ZZX9599 <536509593@qq.com> Date: Mon, 8 Sep 2025 17:06:22 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A0=B8=E5=BF=83SDK=E6=8F=90=E4=BA=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 38 +++ .idea/.gitignore | 0 .idea/encodings.xml | 6 + .idea/misc.xml | 13 ++ .idea/uiDesigner.xml | 124 ++++++++++ .idea/vcs.xml | 6 + .idea/workspace.xml | 216 ++++++++++++++++++ pom.xml | 107 +++++++++ src/main/java/com/yjdsj/SdkApp.java | 11 + .../yjdsj/clt/controller/CltController.java | 66 ++++++ .../java/com/yjdsj/clt/domain/CltInfos.java | 8 + .../java/com/yjdsj/clt/domain/CltTiles.java | 10 + .../com/yjdsj/clt/mapper/CltInfosMapper.java | 8 + .../com/yjdsj/clt/mapper/CltTilesMapper.java | 9 + .../com/yjdsj/clt/service/CltService.java | 120 ++++++++++ .../java/com/yjdsj/clt/vo/CltDetailVo.java | 28 +++ .../yjdsj/common/config/CustomCorsFilter.java | 23 ++ .../com/yjdsj/common/config/ServerConfig.java | 15 ++ .../yjdsj/common/config/SwaggerConfig.java | 34 +++ .../com/yjdsj/common/config/WebConfig.java | 18 ++ .../controller/SourceMapController.java | 33 +++ .../com/yjdsj/common/ds/DataSourceConfig.java | 38 +++ .../ds/DynamicDataSourceContextHolder.java | 18 ++ .../common/ds/DynamicDataSourceManager.java | 46 ++++ .../common/ds/DynamicRoutingDataSource.java | 28 +++ .../yjdsj/common/generator/CodeGenerator.java | 61 +++++ .../yjdsj/common/handler/BlobTypeHandler.java | 36 +++ .../java/com/yjdsj/common/map/MapDto.java | 9 + .../java/com/yjdsj/common/map/SourceMap.java | 91 ++++++++ .../com/yjdsj/common/map/SourceTypeMap.java | 27 +++ .../yjdsj/common/utils/DecompressionUtil.java | 23 ++ .../common/utils/GzipDecompressorUtil.java | 30 +++ .../com/yjdsj/common/utils/JacksonUtil.java | 7 + .../com/yjdsj/common/utils/MD5HashUtil.java | 36 +++ .../com/yjdsj/common/utils/PakTableName.java | 21 ++ .../com/yjdsj/common/utils/PositionUtil.java | 51 +++++ .../mbtiles/controller/MbtilesController.java | 59 +++++ .../yjdsj/mbtiles/domain/MbtilesMetaData.java | 9 + .../yjdsj/mbtiles/domain/MbtilesTiles.java | 12 + .../mbtiles/mapper/MbtilesMetaDataMapper.java | 11 + .../mbtiles/mapper/MbtilesTilesMapper.java | 11 + .../yjdsj/mbtiles/service/MbtilesService.java | 132 +++++++++++ .../com/yjdsj/mbtiles/vo/MbtilesIndexVo.java | 19 ++ .../yjdsj/pak/controller/PakController.java | 73 ++++++ .../java/com/yjdsj/pak/domain/PakBlocks.java | 11 + .../java/com/yjdsj/pak/domain/PakInfos.java | 22 ++ .../com/yjdsj/pak/mapper/PakBlocksMapper.java | 9 + .../com/yjdsj/pak/mapper/PakInfosMapper.java | 9 + .../com/yjdsj/pak/service/PakService.java | 134 +++++++++++ .../java/com/yjdsj/pak/vo/PakIndexVo.java | 19 ++ src/main/resources/application.yml | 3 + src/main/resources/mapper/CltInfosMapper.xml | 8 + src/main/resources/mapper/CltTilesMapper.xml | 17 ++ .../mapper/MbtilesMetaDataMapper.xml | 14 ++ .../resources/mapper/MbtilesTilesMapper.xml | 25 ++ src/main/resources/mapper/PakBlocksMapper.xml | 19 ++ src/main/resources/mapper/PakInfosMapper.xml | 29 +++ 57 files changed, 2060 insertions(+) create mode 100644 .gitignore create mode 100644 .idea/.gitignore create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/uiDesigner.xml create mode 100644 .idea/vcs.xml create mode 100644 .idea/workspace.xml create mode 100644 pom.xml create mode 100644 src/main/java/com/yjdsj/SdkApp.java create mode 100644 src/main/java/com/yjdsj/clt/controller/CltController.java create mode 100644 src/main/java/com/yjdsj/clt/domain/CltInfos.java create mode 100644 src/main/java/com/yjdsj/clt/domain/CltTiles.java create mode 100644 src/main/java/com/yjdsj/clt/mapper/CltInfosMapper.java create mode 100644 src/main/java/com/yjdsj/clt/mapper/CltTilesMapper.java create mode 100644 src/main/java/com/yjdsj/clt/service/CltService.java create mode 100644 src/main/java/com/yjdsj/clt/vo/CltDetailVo.java create mode 100644 src/main/java/com/yjdsj/common/config/CustomCorsFilter.java create mode 100644 src/main/java/com/yjdsj/common/config/ServerConfig.java create mode 100644 src/main/java/com/yjdsj/common/config/SwaggerConfig.java create mode 100644 src/main/java/com/yjdsj/common/config/WebConfig.java create mode 100644 src/main/java/com/yjdsj/common/controller/SourceMapController.java create mode 100644 src/main/java/com/yjdsj/common/ds/DataSourceConfig.java create mode 100644 src/main/java/com/yjdsj/common/ds/DynamicDataSourceContextHolder.java create mode 100644 src/main/java/com/yjdsj/common/ds/DynamicDataSourceManager.java create mode 100644 src/main/java/com/yjdsj/common/ds/DynamicRoutingDataSource.java create mode 100644 src/main/java/com/yjdsj/common/generator/CodeGenerator.java create mode 100644 src/main/java/com/yjdsj/common/handler/BlobTypeHandler.java create mode 100644 src/main/java/com/yjdsj/common/map/MapDto.java create mode 100644 src/main/java/com/yjdsj/common/map/SourceMap.java create mode 100644 src/main/java/com/yjdsj/common/map/SourceTypeMap.java create mode 100644 src/main/java/com/yjdsj/common/utils/DecompressionUtil.java create mode 100644 src/main/java/com/yjdsj/common/utils/GzipDecompressorUtil.java create mode 100644 src/main/java/com/yjdsj/common/utils/JacksonUtil.java create mode 100644 src/main/java/com/yjdsj/common/utils/MD5HashUtil.java create mode 100644 src/main/java/com/yjdsj/common/utils/PakTableName.java create mode 100644 src/main/java/com/yjdsj/common/utils/PositionUtil.java create mode 100644 src/main/java/com/yjdsj/mbtiles/controller/MbtilesController.java create mode 100644 src/main/java/com/yjdsj/mbtiles/domain/MbtilesMetaData.java create mode 100644 src/main/java/com/yjdsj/mbtiles/domain/MbtilesTiles.java create mode 100644 src/main/java/com/yjdsj/mbtiles/mapper/MbtilesMetaDataMapper.java create mode 100644 src/main/java/com/yjdsj/mbtiles/mapper/MbtilesTilesMapper.java create mode 100644 src/main/java/com/yjdsj/mbtiles/service/MbtilesService.java create mode 100644 src/main/java/com/yjdsj/mbtiles/vo/MbtilesIndexVo.java create mode 100644 src/main/java/com/yjdsj/pak/controller/PakController.java create mode 100644 src/main/java/com/yjdsj/pak/domain/PakBlocks.java create mode 100644 src/main/java/com/yjdsj/pak/domain/PakInfos.java create mode 100644 src/main/java/com/yjdsj/pak/mapper/PakBlocksMapper.java create mode 100644 src/main/java/com/yjdsj/pak/mapper/PakInfosMapper.java create mode 100644 src/main/java/com/yjdsj/pak/service/PakService.java create mode 100644 src/main/java/com/yjdsj/pak/vo/PakIndexVo.java create mode 100644 src/main/resources/application.yml create mode 100644 src/main/resources/mapper/CltInfosMapper.xml create mode 100644 src/main/resources/mapper/CltTilesMapper.xml create mode 100644 src/main/resources/mapper/MbtilesMetaDataMapper.xml create mode 100644 src/main/resources/mapper/MbtilesTilesMapper.xml create mode 100644 src/main/resources/mapper/PakBlocksMapper.xml create mode 100644 src/main/resources/mapper/PakInfosMapper.xml diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5ff6309 --- /dev/null +++ b/.gitignore @@ -0,0 +1,38 @@ +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### IntelliJ IDEA ### +.idea/modules.xml +.idea/jarRepositories.xml +.idea/compiler.xml +.idea/libraries/ +*.iws +*.iml +*.ipr + +### Eclipse ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ + +### Mac OS ### +.DS_Store \ No newline at end of file diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..e69de29 diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..63e9001 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..fcc773f --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,13 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/uiDesigner.xml b/.idea/uiDesigner.xml new file mode 100644 index 0000000..2b63946 --- /dev/null +++ b/.idea/uiDesigner.xml @@ -0,0 +1,124 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..35eb1dd --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/workspace.xml b/.idea/workspace.xml new file mode 100644 index 0000000..a2f424d --- /dev/null +++ b/.idea/workspace.xml @@ -0,0 +1,216 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + { + "associatedIndex": 3 +} + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 1743471024061 + + + + + + + + + + + + + \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 0000000..3ead853 --- /dev/null +++ b/pom.xml @@ -0,0 +1,107 @@ + + + 4.0.0 + + + com.yjdsj + geographysdk + 1.0.0 + + + + 1.8 + 1.8 + UTF-8 + + + + + org.springframework.boot + spring-boot-starter-parent + 2.3.3.RELEASE + + + + + + org.springframework.boot + spring-boot-starter-web + + + + + com.baomidou + mybatis-plus-boot-starter + 3.5.1 + + + + + com.baomidou + mybatis-plus-extension + 3.5.1 + + + + + com.baomidou + mybatis-plus-generator + 3.5.1 + + + + + org.apache.velocity + velocity-engine-core + 2.3 + + + + + org.projectlombok + lombok + 1.18.30 + provided + + + + + org.xerial + sqlite-jdbc + + + + + org.mybatis.spring.boot + mybatis-spring-boot-starter + 2.1.3 + + + + + com.github.xiaoymin + knife4j-spring-boot-starter + 2.0.7 + + + + + cn.hutool + hutool-all + 5.7.8 + + + + + geographysdk + + + + org.springframework.boot + spring-boot-maven-plugin + + + + diff --git a/src/main/java/com/yjdsj/SdkApp.java b/src/main/java/com/yjdsj/SdkApp.java new file mode 100644 index 0000000..d1899da --- /dev/null +++ b/src/main/java/com/yjdsj/SdkApp.java @@ -0,0 +1,11 @@ +package com.yjdsj; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class SdkApp { + public static void main(String[] args) { + SpringApplication.run(SdkApp.class, args); + } +} diff --git a/src/main/java/com/yjdsj/clt/controller/CltController.java b/src/main/java/com/yjdsj/clt/controller/CltController.java new file mode 100644 index 0000000..d9fbd4e --- /dev/null +++ b/src/main/java/com/yjdsj/clt/controller/CltController.java @@ -0,0 +1,66 @@ +package com.yjdsj.clt.controller; + + +import com.yjdsj.clt.domain.CltTiles; +import com.yjdsj.clt.service.CltService; +import com.yjdsj.common.map.SourceMap; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import javax.servlet.http.HttpServletRequest; +import java.io.IOException; + +@Slf4j +@RestController +@Api(tags = "倾斜模型资源") +@RequestMapping("/data/clt") +public class CltController { + + @Resource + private CltService cltService; + @Resource + private SourceMap sourceMap; + + @ApiOperation(value = "获取倾斜模型资源列表") + @GetMapping("/list") + public Object list() { + return sourceMap.getCltSourceList(); + } + + @ApiOperation(value = "获取倾斜模型资源的详情") + @GetMapping("/detail/{mark}") + public Object detail(@PathVariable @ApiParam(value = "资源标识") String mark) { + return cltService.detail(mark); + } + + @GetMapping("/{mark}/{path:.*}") + public ResponseEntity getData(@PathVariable @ApiParam(value = "资源标识") String mark, @PathVariable String path, HttpServletRequest request) { + try { + // 查询对应的 CltTiles 数据 + CltTiles cltTiles = cltService.getTileData(mark, path); + if (cltTiles == null) { + return ResponseEntity.notFound().build(); + } + + // 获取是否需要压缩 + boolean isZip = cltService.checkIsZip(mark); + + // 处理解压数据并返回 + Object responseData = cltService.handleDecompressedData(cltTiles, isZip); + return ResponseEntity.ok(responseData); + + } catch (IOException e) { + log.error("处理瓦片数据失败", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).build(); + } + } +} diff --git a/src/main/java/com/yjdsj/clt/domain/CltInfos.java b/src/main/java/com/yjdsj/clt/domain/CltInfos.java new file mode 100644 index 0000000..9d90218 --- /dev/null +++ b/src/main/java/com/yjdsj/clt/domain/CltInfos.java @@ -0,0 +1,8 @@ +package com.yjdsj.clt.domain; + +import lombok.Data; + +@Data +public class CltInfos { + private String params; +} diff --git a/src/main/java/com/yjdsj/clt/domain/CltTiles.java b/src/main/java/com/yjdsj/clt/domain/CltTiles.java new file mode 100644 index 0000000..4564793 --- /dev/null +++ b/src/main/java/com/yjdsj/clt/domain/CltTiles.java @@ -0,0 +1,10 @@ +package com.yjdsj.clt.domain; + +import lombok.Data; +@Data +public class CltTiles { + private String md5; + private String path; + private byte[] tile; + private String type; +} diff --git a/src/main/java/com/yjdsj/clt/mapper/CltInfosMapper.java b/src/main/java/com/yjdsj/clt/mapper/CltInfosMapper.java new file mode 100644 index 0000000..ff2c757 --- /dev/null +++ b/src/main/java/com/yjdsj/clt/mapper/CltInfosMapper.java @@ -0,0 +1,8 @@ +package com.yjdsj.clt.mapper; + +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CltInfosMapper { + String selectParams(); +} diff --git a/src/main/java/com/yjdsj/clt/mapper/CltTilesMapper.java b/src/main/java/com/yjdsj/clt/mapper/CltTilesMapper.java new file mode 100644 index 0000000..bcadd73 --- /dev/null +++ b/src/main/java/com/yjdsj/clt/mapper/CltTilesMapper.java @@ -0,0 +1,9 @@ +package com.yjdsj.clt.mapper; + +import com.yjdsj.clt.domain.CltTiles; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface CltTilesMapper { + CltTiles selectTileByMd5(String md5); +} diff --git a/src/main/java/com/yjdsj/clt/service/CltService.java b/src/main/java/com/yjdsj/clt/service/CltService.java new file mode 100644 index 0000000..9d067f9 --- /dev/null +++ b/src/main/java/com/yjdsj/clt/service/CltService.java @@ -0,0 +1,120 @@ +package com.yjdsj.clt.service; + +import com.fasterxml.jackson.databind.JsonNode; +import com.fasterxml.jackson.databind.node.ArrayNode; +import com.fasterxml.jackson.databind.node.ObjectNode; +import com.yjdsj.clt.domain.CltTiles; +import com.yjdsj.clt.mapper.CltInfosMapper; +import com.yjdsj.clt.mapper.CltTilesMapper; +import com.yjdsj.clt.vo.CltDetailVo; +import com.yjdsj.common.ds.DynamicDataSourceContextHolder; +import com.yjdsj.common.map.SourceMap; +import com.yjdsj.common.utils.*; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + +@Slf4j +@Service +public class CltService { + + @Resource + private CltInfosMapper cltInfosMapper; + + @Resource + private CltTilesMapper cltTilesMapper; + + @Resource + private SourceMap sourceMap; + + // 是否需要压缩缓存 + private final ConcurrentMap zipCache = new ConcurrentHashMap<>(); + + public Map getCltSourceList() { + return sourceMap.getCltSourceList(); + } + + public CltDetailVo detail(String mark) { + CltDetailVo result = new CltDetailVo(); + try { + // 读取 JSON 文件 + String json = DecompressionUtil.decompressAndConvertToString(getTileData(mark, "tileset.json").getTile()); + // 读取JSON字符串为根节点 + ObjectNode rootNode = (ObjectNode) JacksonUtil.mapper.readTree(json); + // 逐层定位到 root 节点 → 再定位到 transform 节点 + ObjectNode rootChildNode = (ObjectNode) rootNode.get("root"); + ArrayNode transformArrayNode = (ArrayNode) rootChildNode.get("transform"); + // 将 ArrayNode 转换为 double[] 数组 + double[] transform = new double[transformArrayNode.size()]; + for (int i = 0; i < transformArrayNode.size(); i++) { + transform[i] = transformArrayNode.get(i).asDouble(); + } + PositionUtil.BlhResult blhResult = PositionUtil.xyz2Blh(transform[12], transform[13], transform[14]); + // 设置 URL + result.setUrl(String.format("/data/clt/%s/tileset.json", mark)); + // 设置 3D 旋转参数 + CltDetailVo.Orientation orientation = new CltDetailVo.Orientation(0, 0, 0); + result.setOrientation(orientation); + // 设置位置 + CltDetailVo.Position position = new CltDetailVo.Position( + blhResult.lon, + blhResult.lat, + blhResult.alt + ); + result.setPosition(position); + result.setFileType("tileset"); + } catch (IOException e) { + e.printStackTrace(); + } catch (NullPointerException e) { + e.printStackTrace(); + } + return result; + } + + public CltTiles getTileData(String mark, String path) { + // 设置数据源 + DynamicDataSourceContextHolder.setDataSourceKey(mark); + try { + // 查询对应的 CltTiles 数据 + return cltTilesMapper.selectTileByMd5(MD5HashUtil.toMD5(path)); + } finally { + // 清理数据源设置 + DynamicDataSourceContextHolder.clearDataSourceKey(); + } + } + + public boolean checkIsZip(String mark) { + // 设置数据源 + DynamicDataSourceContextHolder.setDataSourceKey(mark); + String paramsJson = cltInfosMapper.selectParams(); + try { + JsonNode rootNode = JacksonUtil.mapper.readTree(paramsJson); + return rootNode.path("zip").asBoolean(false); + } catch (IOException e) { + return false; + } finally { + // 清理数据源设置 + DynamicDataSourceContextHolder.clearDataSourceKey(); + } + } + + public Object handleDecompressedData(CltTiles cltTiles, boolean isZip) throws IOException { + if (isZip) { + if ("tileset.json".equals(cltTiles.getPath())) { + // 解压缩并返回字符串 + return DecompressionUtil.decompressAndConvertToString(cltTiles.getTile()); + } else { + // 解压缩并返回字节数组 + return GzipDecompressorUtil.decompress(cltTiles.getTile()); + } + } + // 不需要解压则直接返回对应的数据 + return cltTiles.getTile(); + } +} diff --git a/src/main/java/com/yjdsj/clt/vo/CltDetailVo.java b/src/main/java/com/yjdsj/clt/vo/CltDetailVo.java new file mode 100644 index 0000000..ac9349c --- /dev/null +++ b/src/main/java/com/yjdsj/clt/vo/CltDetailVo.java @@ -0,0 +1,28 @@ +package com.yjdsj.clt.vo; + +import lombok.AllArgsConstructor; +import lombok.Data; + +@Data +public class CltDetailVo { + private String url; + private Orientation orientation; + private Position position; + private String fileType; + + @Data + @AllArgsConstructor + public static class Orientation { + private double heading; + private double roll; + private double pitch; + } + + @Data + @AllArgsConstructor + public static class Position { + private double lon; + private double lat; + private double alt; + } +} diff --git a/src/main/java/com/yjdsj/common/config/CustomCorsFilter.java b/src/main/java/com/yjdsj/common/config/CustomCorsFilter.java new file mode 100644 index 0000000..53edafd --- /dev/null +++ b/src/main/java/com/yjdsj/common/config/CustomCorsFilter.java @@ -0,0 +1,23 @@ +package com.yjdsj.common.config; + +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.cors.CorsConfiguration; +import org.springframework.web.cors.UrlBasedCorsConfigurationSource; +import org.springframework.web.filter.CorsFilter; + +@Configuration +public class CustomCorsFilter { + + @Bean + public CorsFilter corsFilter() { + UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); + CorsConfiguration config = new CorsConfiguration(); + config.setAllowCredentials(true); + config.addAllowedOrigin("*"); + config.addAllowedHeader("*"); + config.addAllowedMethod("*"); + source.registerCorsConfiguration("/**", config); + return new CorsFilter(source); + } +} diff --git a/src/main/java/com/yjdsj/common/config/ServerConfig.java b/src/main/java/com/yjdsj/common/config/ServerConfig.java new file mode 100644 index 0000000..903bdde --- /dev/null +++ b/src/main/java/com/yjdsj/common/config/ServerConfig.java @@ -0,0 +1,15 @@ +package com.yjdsj.common.config; + +import lombok.Data; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Component; + +@Data +@Component +public class ServerConfig { + @Value("${server.port}") + private int port; + + @Value("${server.host}") + private String host; +} diff --git a/src/main/java/com/yjdsj/common/config/SwaggerConfig.java b/src/main/java/com/yjdsj/common/config/SwaggerConfig.java new file mode 100644 index 0000000..cabc84e --- /dev/null +++ b/src/main/java/com/yjdsj/common/config/SwaggerConfig.java @@ -0,0 +1,34 @@ +package com.yjdsj.common.config; + +import io.swagger.annotations.ApiOperation; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import springfox.documentation.builders.ApiInfoBuilder; +import springfox.documentation.builders.PathSelectors; +import springfox.documentation.builders.RequestHandlerSelectors; +import springfox.documentation.service.ApiInfo; +import springfox.documentation.spi.DocumentationType; +import springfox.documentation.spring.web.plugins.Docket; +import springfox.documentation.swagger2.annotations.EnableSwagger2WebMvc; + +@Configuration +@EnableSwagger2WebMvc +public class SwaggerConfig { + @Bean + public Docket webApiConfig() { + return new Docket(DocumentationType.SWAGGER_2) + .apiInfo(apiInfo()) + .select() + .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class)) + .paths(PathSelectors.any()) + .build(); + } + + private ApiInfo apiInfo() { + return new ApiInfoBuilder() + .title("对接API") + .description("对接相关接口") + .version("1.0") + .build(); + } +} diff --git a/src/main/java/com/yjdsj/common/config/WebConfig.java b/src/main/java/com/yjdsj/common/config/WebConfig.java new file mode 100644 index 0000000..ba4a4c5 --- /dev/null +++ b/src/main/java/com/yjdsj/common/config/WebConfig.java @@ -0,0 +1,18 @@ +package com.yjdsj.common.config; + +import org.springframework.context.annotation.Configuration; +import org.springframework.web.servlet.config.annotation.CorsRegistry; +import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; + +@Configuration +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addCorsMappings(CorsRegistry registry) { + registry.addMapping("/**").allowedOrigins("*") // 允许所有来源 + .allowedMethods("*") // 允许所有方法(GET, POST, PUT, DELETE 等) + .allowedHeaders("*") // 允许所有头 + .allowCredentials(true) // 允许发送凭证(cookies 等) + .maxAge(3600); // 预检请求的缓存时间(单位:秒) + } +} diff --git a/src/main/java/com/yjdsj/common/controller/SourceMapController.java b/src/main/java/com/yjdsj/common/controller/SourceMapController.java new file mode 100644 index 0000000..b7819fd --- /dev/null +++ b/src/main/java/com/yjdsj/common/controller/SourceMapController.java @@ -0,0 +1,33 @@ +package com.yjdsj.common.controller; + +import com.yjdsj.common.map.SourceMap; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import javax.annotation.Resource; + +@Api(tags = "资源管理") +@Slf4j +@RestController +@RequestMapping("/sourceMap") +public class SourceMapController { + + @Resource + private SourceMap sourceMap; + + // 添加资源映射关系 + @PostMapping("/add") + @ApiOperation(value = "添加资源映射关系") + public Object addSourceMapping(@RequestParam String filePath) { + return sourceMap.addSourceMapping(filePath); + } + + // 移除资源映射关系 + @DeleteMapping("/remove") + @ApiOperation(value = "移除资源映射关系") + public void removeSourceMapping(@RequestParam String filePath) { + sourceMap.removeSourceMapping(filePath); + } +} diff --git a/src/main/java/com/yjdsj/common/ds/DataSourceConfig.java b/src/main/java/com/yjdsj/common/ds/DataSourceConfig.java new file mode 100644 index 0000000..2c9270a --- /dev/null +++ b/src/main/java/com/yjdsj/common/ds/DataSourceConfig.java @@ -0,0 +1,38 @@ +package com.yjdsj.common.ds; + +import org.apache.ibatis.session.SqlSessionFactory; +import org.mybatis.spring.SqlSessionFactoryBean; +import org.mybatis.spring.SqlSessionTemplate; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; + +import javax.annotation.Resource; +import javax.sql.DataSource; + +@Configuration +public class DataSourceConfig { + + @Resource + private DynamicDataSourceManager dynamicDataSourceManager; + + @Bean(name = "dynamicDataSource") + public DataSource dynamicDataSource() { + DynamicRoutingDataSource dynamicRoutingDataSource = new DynamicRoutingDataSource(dynamicDataSourceManager); + return dynamicRoutingDataSource; + } + + @Bean + public SqlSessionFactory sqlSessionFactory(@Autowired DataSource dynamicDataSource) throws Exception { + SqlSessionFactoryBean sessionFactoryBean = new SqlSessionFactoryBean(); + sessionFactoryBean.setDataSource(dynamicDataSource); + sessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver().getResources("classpath*:mapper/*.xml")); + return sessionFactoryBean.getObject(); + } + + @Bean + public SqlSessionTemplate sqlSessionTemplate(@Autowired SqlSessionFactory sqlSessionFactory) { + return new SqlSessionTemplate(sqlSessionFactory); + } +} diff --git a/src/main/java/com/yjdsj/common/ds/DynamicDataSourceContextHolder.java b/src/main/java/com/yjdsj/common/ds/DynamicDataSourceContextHolder.java new file mode 100644 index 0000000..95e25dc --- /dev/null +++ b/src/main/java/com/yjdsj/common/ds/DynamicDataSourceContextHolder.java @@ -0,0 +1,18 @@ +package com.yjdsj.common.ds; + +public class DynamicDataSourceContextHolder { + + private static final ThreadLocal CONTEXT_HOLDER = new ThreadLocal<>(); + + public static void setDataSourceKey(String key) { + CONTEXT_HOLDER.set(key); + } + + public static String getDataSourceKey() { + return CONTEXT_HOLDER.get(); + } + + public static void clearDataSourceKey() { + CONTEXT_HOLDER.remove(); + } +} diff --git a/src/main/java/com/yjdsj/common/ds/DynamicDataSourceManager.java b/src/main/java/com/yjdsj/common/ds/DynamicDataSourceManager.java new file mode 100644 index 0000000..20e3c36 --- /dev/null +++ b/src/main/java/com/yjdsj/common/ds/DynamicDataSourceManager.java @@ -0,0 +1,46 @@ +package com.yjdsj.common.ds; + +import com.yjdsj.common.map.SourceMap; +import com.zaxxer.hikari.HikariDataSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Component; + +import javax.sql.DataSource; +import java.util.HashMap; +import java.util.Map; + +@Component +public class DynamicDataSourceManager { + + private final Map dataSourceCache = new HashMap<>(); + private final SourceMap sourceMap; + + @Autowired + public DynamicDataSourceManager(SourceMap sourceMap) { + this.sourceMap = sourceMap; + } + + public DataSource getDataSource(String dbName) { + // 检查缓存中是否已有数据源 + if (dataSourceCache.containsKey(dbName)) { + return dataSourceCache.get(dbName); + } else { + // 动态创建数据源 + String dbPath = sourceMap.getSourceMapping(dbName); + if (dbPath != null) { + DataSource dataSource = createDataSource(dbPath); + dataSourceCache.put(dbName, dataSource); + return dataSource; + } else { + throw new IllegalArgumentException("Data source not found for name: " + dbName); + } + } + } + + private DataSource createDataSource(String dbPath) { + HikariDataSource dataSource = new HikariDataSource(); + dataSource.setJdbcUrl("jdbc:sqlite:" + dbPath); + dataSource.setDriverClassName("org.sqlite.JDBC"); + return dataSource; + } +} diff --git a/src/main/java/com/yjdsj/common/ds/DynamicRoutingDataSource.java b/src/main/java/com/yjdsj/common/ds/DynamicRoutingDataSource.java new file mode 100644 index 0000000..cb50ac4 --- /dev/null +++ b/src/main/java/com/yjdsj/common/ds/DynamicRoutingDataSource.java @@ -0,0 +1,28 @@ +package com.yjdsj.common.ds; + +import org.springframework.jdbc.datasource.lookup.AbstractRoutingDataSource; + +import javax.sql.DataSource; +import java.util.HashMap; + +public class DynamicRoutingDataSource extends AbstractRoutingDataSource { + + private final DynamicDataSourceManager dataSourceManager; + + public DynamicRoutingDataSource(DynamicDataSourceManager dataSourceManager) { + this.dataSourceManager = dataSourceManager; + this.setTargetDataSources(new HashMap<>()); + this.afterPropertiesSet(); + } + + @Override + protected Object determineCurrentLookupKey() { + return DynamicDataSourceContextHolder.getDataSourceKey(); + } + + @Override + protected DataSource determineTargetDataSource() { + String dataSourceKey = (String) determineCurrentLookupKey(); + return dataSourceManager.getDataSource(dataSourceKey); + } +} diff --git a/src/main/java/com/yjdsj/common/generator/CodeGenerator.java b/src/main/java/com/yjdsj/common/generator/CodeGenerator.java new file mode 100644 index 0000000..75281d3 --- /dev/null +++ b/src/main/java/com/yjdsj/common/generator/CodeGenerator.java @@ -0,0 +1,61 @@ +package com.yjdsj.common.generator; + +import com.baomidou.mybatisplus.generator.FastAutoGenerator; +import com.baomidou.mybatisplus.generator.config.OutputFile; +import com.baomidou.mybatisplus.generator.config.rules.NamingStrategy; + +import java.util.Collections; + +/** + * + * @date 2024/6/26 14:42 + */ +public class CodeGenerator { + + private static String databasePath = "D:\\DJ\\source\\广西提供poi.poi"; // SQLite 数据库文件路径 + private static String author = "周志雄"; + + public static void main(String[] args) { + Generation(databasePath, "tbl_pois"); // 生成指定表的代码,这里以 "users" 表为例 + } + + public static void Generation(String databasePath, String... tableName) { + FastAutoGenerator.create("jdbc:sqlite:" + databasePath, "", "") + .globalConfig(builder -> { + builder.author(author) // 设置作者名 + .enableSwagger() // 启用Swagger + .outputDir(System.getProperty("user.dir") + "/src/main/java"); // 指定输出目录 + }) + .packageConfig(builder -> { + builder.entity("domain") // 实体类包名 + .parent("com.xzjm.poi") // 父包名 + .controller("controller") // 控制层包名 + .mapper("mapper") // mapper 层包名 + .service("service") // service 层包名 + .serviceImpl("service.impl") // service 实现类包名 + // 自定义 mapper.xml 文件输出目录 + .pathInfo(Collections.singletonMap(OutputFile.mapperXml, System.getProperty("user.dir") + "/src/main/resources/mapper")); + }) + .strategyConfig(builder -> { + builder.addInclude(tableName) // 设置要生成的表名 + .addTablePrefix("t_") // 设置表前缀过滤(如果没有,可以去掉) + .entityBuilder() + .enableLombok() // 启用 Lombok + .naming(NamingStrategy.underline_to_camel) // 表名映射为实体类驼峰命名 + .columnNaming(NamingStrategy.underline_to_camel) // 字段映射为驼峰命名 + .mapperBuilder() + .enableMapperAnnotation() // 启用 @Mapper 注解 + .enableBaseResultMap() // 启用 BaseResultMap 生成 + .enableBaseColumnList() // 启用 BaseColumnList 生成 + .formatMapperFileName("%sMapper") // 格式化 Mapper 文件名称 + .formatXmlFileName("%sMapper") // 格式化 XML 文件名称 + .serviceBuilder() + .formatServiceFileName("%sService") // 格式化 Service 接口文件名称 + .formatServiceImplFileName("%sServiceImpl") // 格式化 Service 实现类文件名称 + .controllerBuilder() + .enableRestStyle() // 启用 Restful 风格 + .formatFileName("%sController") // 格式化 Controller 文件名称 + .enableHyphenStyle(); // 启用驼峰转连字符 + }).execute(); + } +} diff --git a/src/main/java/com/yjdsj/common/handler/BlobTypeHandler.java b/src/main/java/com/yjdsj/common/handler/BlobTypeHandler.java new file mode 100644 index 0000000..a8e2603 --- /dev/null +++ b/src/main/java/com/yjdsj/common/handler/BlobTypeHandler.java @@ -0,0 +1,36 @@ +package com.yjdsj.common.handler; + +import org.apache.ibatis.type.BaseTypeHandler; +import org.apache.ibatis.type.JdbcType; + +import java.sql.CallableStatement; +import java.sql.PreparedStatement; +import java.sql.ResultSet; +import java.sql.SQLException; + +/** + * + * @date 2024/8/20 11:46 + */ +public class BlobTypeHandler extends BaseTypeHandler { + + @Override + public void setNonNullParameter(PreparedStatement ps, int i, byte[] parameter, JdbcType jdbcType) throws SQLException { + ps.setBytes(i, parameter); + } + + @Override + public byte[] getNullableResult(ResultSet rs, String columnName) throws SQLException { + return rs.getBytes(columnName); + } + + @Override + public byte[] getNullableResult(ResultSet rs, int columnIndex) throws SQLException { + return rs.getBytes(columnIndex); + } + + @Override + public byte[] getNullableResult(CallableStatement cs, int columnIndex) throws SQLException { + return cs.getBytes(columnIndex); + } +} diff --git a/src/main/java/com/yjdsj/common/map/MapDto.java b/src/main/java/com/yjdsj/common/map/MapDto.java new file mode 100644 index 0000000..397b3bf --- /dev/null +++ b/src/main/java/com/yjdsj/common/map/MapDto.java @@ -0,0 +1,9 @@ +package com.yjdsj.common.map; + +import lombok.Data; + +@Data +public class MapDto { + private String filePath; + private Integer userId; +} diff --git a/src/main/java/com/yjdsj/common/map/SourceMap.java b/src/main/java/com/yjdsj/common/map/SourceMap.java new file mode 100644 index 0000000..2f1aa23 --- /dev/null +++ b/src/main/java/com/yjdsj/common/map/SourceMap.java @@ -0,0 +1,91 @@ +package com.yjdsj.common.map; + +import com.yjdsj.common.utils.MD5HashUtil; +import org.springframework.stereotype.Component; + +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashMap; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Component +public class SourceMap { + + private Map sourceMap = new ConcurrentHashMap<>(); + + // 添加资源映射关系 + public String addSourceMapping(String filePath) { + sourceMap.put(MD5HashUtil.toMD5(filePath), filePath); + return MD5HashUtil.toMD5(filePath); + } + + // 移除资源映射关系 + public void removeSourceMapping(String filePath) { + sourceMap.remove(MD5HashUtil.toMD5(filePath)); + } + + // 获取资源映射关系 + public String getSourceMapping(String mark) { + return sourceMap.get(mark); + } + + // 获取倾斜模型资源列表 + public Map getCltSourceList() { + Map sourceList = new HashMap<>(); + for (Map.Entry entry : sourceMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (value.endsWith(".clt")) { + String fileName = getFileNameWithExtension(value); + sourceList.put(key, fileName); + } + } + return sourceList; + } + + // 获取图层资源列表 + public Map getMBTilesSourceList() { + Map sourceList = new HashMap<>(); + for (Map.Entry entry : sourceMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (value.endsWith(".mbtiles")) { + String fileName = getFileNameWithExtension(value); + sourceList.put(key, fileName); + } + } + return sourceList; + } + + // 获取地形资源列表 + public Map getPakSourceList() { + Map sourceList = new HashMap<>(); + for (Map.Entry entry : sourceMap.entrySet()) { + String key = entry.getKey(); + String value = entry.getValue(); + if (value.endsWith(".pak")) { + String fileName = getFileNameWithExtension(value); + sourceList.put(key, fileName); + } + } + return sourceList; + } + + /** + * 获取文件名和后缀 + */ + public static String getFileNameWithExtension(String filePath) { + Path path = Paths.get(filePath); // 获取路径 + return path.getFileName().toString(); // 返回文件名和后缀 + } + + /** + * 遍历数据并打印内容、无参 + */ + public void printSourceMap() { + for (Map.Entry entry : sourceMap.entrySet()) { + System.out.println("MD5 Key: " + entry.getKey() + ", File Path: " + entry.getValue()); + } + } +} diff --git a/src/main/java/com/yjdsj/common/map/SourceTypeMap.java b/src/main/java/com/yjdsj/common/map/SourceTypeMap.java new file mode 100644 index 0000000..eff1378 --- /dev/null +++ b/src/main/java/com/yjdsj/common/map/SourceTypeMap.java @@ -0,0 +1,27 @@ +package com.yjdsj.common.map; + +import java.util.HashMap; +import java.util.Map; + +/** + * + * @date 2024/8/20 11:04 + */ +public class SourceTypeMap { + /** + * 文件类型映射 + */ + private static Map SourceType = new HashMap<>(); + + /** + * 初始化 + */ + static { + SourceType.put("clt", "tileset"); + } + + // 获取资源对应的类型 + public static String getSourceType(String fileExtension) { + return SourceType.get(fileExtension); + } +} diff --git a/src/main/java/com/yjdsj/common/utils/DecompressionUtil.java b/src/main/java/com/yjdsj/common/utils/DecompressionUtil.java new file mode 100644 index 0000000..444ece4 --- /dev/null +++ b/src/main/java/com/yjdsj/common/utils/DecompressionUtil.java @@ -0,0 +1,23 @@ +package com.yjdsj.common.utils; + +import java.io.IOException; +import java.nio.charset.StandardCharsets; + +public class DecompressionUtil { + + /** + * 将压缩数据解压并转换为字符串 + * + * @param compressedData + * @return + */ + public static String decompressAndConvertToString(byte[] compressedData) { + try { + byte[] decompressedData = GzipDecompressorUtil.decompress(compressedData); + return new String(decompressedData, StandardCharsets.UTF_8); + } catch (IOException e) { + e.printStackTrace(); + return "Failed to decompress data: " + e.getMessage(); + } + } +} diff --git a/src/main/java/com/yjdsj/common/utils/GzipDecompressorUtil.java b/src/main/java/com/yjdsj/common/utils/GzipDecompressorUtil.java new file mode 100644 index 0000000..73dca6d --- /dev/null +++ b/src/main/java/com/yjdsj/common/utils/GzipDecompressorUtil.java @@ -0,0 +1,30 @@ +package com.yjdsj.common.utils; + +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.util.zip.GZIPInputStream; + +public class GzipDecompressorUtil { + + /** + * 解压数据 + * + * @param compressedData + * @return + * @throws IOException + */ + public static byte[] decompress(byte[] compressedData) throws IOException { + try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(compressedData); + GZIPInputStream gzipInputStream = new GZIPInputStream(byteArrayInputStream); + ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream()) { + + byte[] buffer = new byte[1024]; + int len; + while ((len = gzipInputStream.read(buffer)) != -1) { + byteArrayOutputStream.write(buffer, 0, len); + } + return byteArrayOutputStream.toByteArray(); + } + } +} diff --git a/src/main/java/com/yjdsj/common/utils/JacksonUtil.java b/src/main/java/com/yjdsj/common/utils/JacksonUtil.java new file mode 100644 index 0000000..0881174 --- /dev/null +++ b/src/main/java/com/yjdsj/common/utils/JacksonUtil.java @@ -0,0 +1,7 @@ +package com.yjdsj.common.utils; + +import com.fasterxml.jackson.databind.ObjectMapper; + +public class JacksonUtil { + public static final ObjectMapper mapper = new ObjectMapper(); +} diff --git a/src/main/java/com/yjdsj/common/utils/MD5HashUtil.java b/src/main/java/com/yjdsj/common/utils/MD5HashUtil.java new file mode 100644 index 0000000..b0689b7 --- /dev/null +++ b/src/main/java/com/yjdsj/common/utils/MD5HashUtil.java @@ -0,0 +1,36 @@ +package com.yjdsj.common.utils; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +public class MD5HashUtil { + + /** + * 将输入字符串转换为MD5哈希值 + * + * @param input 要转换的字符串 + * @return 转换后的MD5哈希值 + */ + public static String toMD5(String input) { + try { + // 获取MD5算法实例 + MessageDigest md = MessageDigest.getInstance("MD5"); + + // 将输入字符串转换为字节数组 + byte[] messageDigest = md.digest(input.getBytes()); + + // 将字节数组转换为十六进制字符串 + StringBuilder hexString = new StringBuilder(); + for (byte b : messageDigest) { + String hex = Integer.toHexString(0xff & b); + if (hex.length() == 1) hexString.append('0'); + hexString.append(hex); + } + + // 返回MD5哈希值 + return hexString.toString(); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException("MD5算法不存在", e); + } + } +} diff --git a/src/main/java/com/yjdsj/common/utils/PakTableName.java b/src/main/java/com/yjdsj/common/utils/PakTableName.java new file mode 100644 index 0000000..12bc7cc --- /dev/null +++ b/src/main/java/com/yjdsj/common/utils/PakTableName.java @@ -0,0 +1,21 @@ +package com.yjdsj.common.utils; + +public class PakTableName { + /** + * 根据坐标动态计算获取表名 + * + * @param z + * @param x + * @param y + * @return + */ + public static String getTableName(int z, int x, int y) { + if (z < 10) { + return "blocks"; + } else { + double tx = Math.floorDiv(x, 512); + double ty = Math.floorDiv(y, 512); + return "blocks_" + Integer.toString(z) + "_" + Integer.toString((int) tx) + "_" + Integer.toString((int) ty); + } + } +} diff --git a/src/main/java/com/yjdsj/common/utils/PositionUtil.java b/src/main/java/com/yjdsj/common/utils/PositionUtil.java new file mode 100644 index 0000000..81af563 --- /dev/null +++ b/src/main/java/com/yjdsj/common/utils/PositionUtil.java @@ -0,0 +1,51 @@ +package com.yjdsj.common.utils; + +import lombok.AllArgsConstructor; +import lombok.Data; + +public class PositionUtil { + // 定义所需的常量,这些值需要根据实际应用场景设置 + private static final double a = 6378137.0; // 椭球长半轴 + private static final double e = 0.0818191908426; // 第一偏心率 + private static final double epsilon = 1e-8; // 迭代精度 + private static final double r2d = 180.0 / Math.PI; // 弧度转角度 + + /** + * 存储 BLH 转换结果的内部类 + */ + @Data + @AllArgsConstructor + public static class BlhResult { + public double lon; // 经度 + public double lat; // 纬度 + public double alt; // 高度 + } + + /** + * 将XYZ坐标转换为BLH坐标 + */ + public static BlhResult xyz2Blh(double x, double y, double z) { + double tmpX = x; + double tmpY = y; + double tmpZ = z; + + double curB = 0.0; + double N = 0.0; + double calB = Math.atan2(tmpZ, Math.sqrt(tmpX * tmpX + tmpY * tmpY)); + + int counter = 0; + while (Math.abs(curB - calB) * r2d > epsilon && counter < 25) { + curB = calB; + N = a / Math.sqrt(1 - e * e * Math.sin(curB) * Math.sin(curB)); + calB = Math.atan2(tmpZ + N * e * e * Math.sin(curB), + Math.sqrt(tmpX * tmpX + tmpY * tmpY)); + counter++; + } + + double longitude = Math.atan2(tmpY, tmpX) * r2d; + double latitude = curB * r2d; + double height = tmpZ / Math.sin(curB) - N * (1 - e * e); + + return new BlhResult(longitude, latitude, height); + } +} diff --git a/src/main/java/com/yjdsj/mbtiles/controller/MbtilesController.java b/src/main/java/com/yjdsj/mbtiles/controller/MbtilesController.java new file mode 100644 index 0000000..d7306d6 --- /dev/null +++ b/src/main/java/com/yjdsj/mbtiles/controller/MbtilesController.java @@ -0,0 +1,59 @@ +package com.yjdsj.mbtiles.controller; + +import com.yjdsj.mbtiles.domain.MbtilesTiles; +import com.yjdsj.mbtiles.service.MbtilesService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; + + +@Slf4j +@RestController +@Api(tags = "图层文件相关") +@RequestMapping("/data/mbtiles") +public class MbtilesController { + + @Resource + private MbtilesService mbtilesService; + + @ApiOperation(value = "获取图层资源列表") + @GetMapping("/list") + public Object list() { + return mbtilesService.getMbtilesSourceList(); + } + + @ApiOperation(value = "获取图层资源的详情") + @GetMapping("/detail/{mark}") + public Object detail(@PathVariable @ApiParam(value = "资源标识") String mark) { + return mbtilesService.detail(mark); + } + + @GetMapping("/{mark}/{z}/{x}/{y}.{format}") + public ResponseEntity getData( + @PathVariable @ApiParam(value = "资源标识") String mark, + @PathVariable("z") int z, + @PathVariable("x") int x, + @PathVariable("y") int y, + @PathVariable("format") String format) { + + MbtilesTiles mbtilesTiles = mbtilesService.getTileData(mark, z, x, y); + if (mbtilesTiles == null) { + return ResponseEntity.notFound().build(); + } + + // 获取响应媒体类型 + String contentType = mbtilesService.getContentTypeByFormat(format); + return ResponseEntity.ok() + .contentType(MediaType.parseMediaType(contentType)) + .body(mbtilesTiles.getTileData()); + } +} diff --git a/src/main/java/com/yjdsj/mbtiles/domain/MbtilesMetaData.java b/src/main/java/com/yjdsj/mbtiles/domain/MbtilesMetaData.java new file mode 100644 index 0000000..0667c74 --- /dev/null +++ b/src/main/java/com/yjdsj/mbtiles/domain/MbtilesMetaData.java @@ -0,0 +1,9 @@ +package com.yjdsj.mbtiles.domain; + +import lombok.Data; + +@Data +public class MbtilesMetaData { + private String name; + private String value; +} diff --git a/src/main/java/com/yjdsj/mbtiles/domain/MbtilesTiles.java b/src/main/java/com/yjdsj/mbtiles/domain/MbtilesTiles.java new file mode 100644 index 0000000..131e6f9 --- /dev/null +++ b/src/main/java/com/yjdsj/mbtiles/domain/MbtilesTiles.java @@ -0,0 +1,12 @@ +package com.yjdsj.mbtiles.domain; + + +import lombok.Data; + +@Data +public class MbtilesTiles { + private String zoomLevel; + private String tileColumn; + private String tileRow; + private byte[] tileData; +} diff --git a/src/main/java/com/yjdsj/mbtiles/mapper/MbtilesMetaDataMapper.java b/src/main/java/com/yjdsj/mbtiles/mapper/MbtilesMetaDataMapper.java new file mode 100644 index 0000000..bcc0b35 --- /dev/null +++ b/src/main/java/com/yjdsj/mbtiles/mapper/MbtilesMetaDataMapper.java @@ -0,0 +1,11 @@ +package com.yjdsj.mbtiles.mapper; + +import com.yjdsj.mbtiles.domain.MbtilesMetaData; +import org.apache.ibatis.annotations.Mapper; + +import java.util.List; + +@Mapper +public interface MbtilesMetaDataMapper { + List selectList(); +} diff --git a/src/main/java/com/yjdsj/mbtiles/mapper/MbtilesTilesMapper.java b/src/main/java/com/yjdsj/mbtiles/mapper/MbtilesTilesMapper.java new file mode 100644 index 0000000..08fdbcf --- /dev/null +++ b/src/main/java/com/yjdsj/mbtiles/mapper/MbtilesTilesMapper.java @@ -0,0 +1,11 @@ +package com.yjdsj.mbtiles.mapper; + +import com.yjdsj.mbtiles.domain.MbtilesTiles; +import org.apache.ibatis.annotations.Mapper; +import org.apache.ibatis.annotations.Param; +@Mapper +public interface MbtilesTilesMapper { + MbtilesTiles selectByCondition(@Param("z") int z, @Param("x") int x, @Param("y") int y); + + int selectByZoomLevel(@Param("zoomLevel") int zoomLevel); +} diff --git a/src/main/java/com/yjdsj/mbtiles/service/MbtilesService.java b/src/main/java/com/yjdsj/mbtiles/service/MbtilesService.java new file mode 100644 index 0000000..9883785 --- /dev/null +++ b/src/main/java/com/yjdsj/mbtiles/service/MbtilesService.java @@ -0,0 +1,132 @@ +package com.yjdsj.mbtiles.service; + +import com.yjdsj.common.ds.DynamicDataSourceContextHolder; +import com.yjdsj.common.map.SourceMap; +import com.yjdsj.mbtiles.domain.MbtilesMetaData; +import com.yjdsj.mbtiles.domain.MbtilesTiles; +import com.yjdsj.mbtiles.mapper.MbtilesMetaDataMapper; +import com.yjdsj.mbtiles.mapper.MbtilesTilesMapper; +import com.yjdsj.mbtiles.vo.MbtilesIndexVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +@Slf4j +@Service +public class MbtilesService { + + @Resource + private MbtilesMetaDataMapper mbtilesMetaDataMapper; + @Resource + private MbtilesTilesMapper mbtilesTilesMapper; + @Resource + private SourceMap sourceMap; + + + /** + * 获取图层资源列表 + */ + public Map getMbtilesSourceList() { + return sourceMap.getMBTilesSourceList(); + } + + /** + * 构建图层资源索引信息 + */ + public MbtilesIndexVo detail(String mark) { + String XYZ = "xyz"; + String WEBMERCATORTILINGSCHEME = "WebMercatorTilingScheme"; + String GEOGRAPHICTILINGSCHEME = "GeographicTilingScheme"; + + MbtilesIndexVo mbtilesIndexVo = new MbtilesIndexVo(); + DynamicDataSourceContextHolder.setDataSourceKey(mark); + try { + // 获取元数据并转换为Map + List mbtilesMetaDataList = mbtilesMetaDataMapper.selectList(); + Map metaDataMap = new HashMap<>(mbtilesMetaDataList.size()); + for (MbtilesMetaData meta : mbtilesMetaDataList) { + metaDataMap.put(meta.getName(), meta.getValue()); + } + + // 处理边界信息 + String[] boundsArray = metaDataMap.get("bounds").split(","); + + // 处理缩放级别 + Map zoomLevelCounts = new HashMap<>(31); + for (int i = 0; i <= 30; i++) { + zoomLevelCounts.put(i, mbtilesTilesMapper.selectByZoomLevel(i)); + } + + // 计算最大最小 zoomLevel + int minZoomLevel = zoomLevelCounts.entrySet().stream() + .filter(entry -> entry.getValue() > 0) + .map(Map.Entry::getKey) + .min(Integer::compare) + .orElse(Integer.MAX_VALUE); + int maxZoomLevel = zoomLevelCounts.entrySet().stream() + .filter(entry -> entry.getValue() > 0) + .map(Map.Entry::getKey) + .max(Integer::compare) + .orElse(Integer.MIN_VALUE); + + // 构建URL + String format = metaDataMap.getOrDefault("format", "png"); + String url = String.format("/data/mbtiles/%s/{z}/{x}/{y}.%s", mark, format); + + // 设置返回对象属性 + mbtilesIndexVo.setUrl(url); + mbtilesIndexVo.setMinZoom(minZoomLevel); + mbtilesIndexVo.setMaxZoom(maxZoomLevel); + mbtilesIndexVo.setWest(boundsArray[0]); + mbtilesIndexVo.setSouth(boundsArray[1]); + mbtilesIndexVo.setEast(boundsArray[2]); + mbtilesIndexVo.setNorth(boundsArray[3]); + + // 处理加载方式 + mbtilesIndexVo.setLoadMethod(metaDataMap.getOrDefault("load_method", XYZ)); + // 处理投影名称(优先级: profile 包含 longlat > schema_name > 默认) + String schemaName = metaDataMap.getOrDefault("schema_name", WEBMERCATORTILINGSCHEME); + if (metaDataMap.containsKey("profile") && metaDataMap.get("profile").contains("+proj=longlat")) { + schemaName = GEOGRAPHICTILINGSCHEME; + } + mbtilesIndexVo.setSchemaName(schemaName); + mbtilesIndexVo.setFileType("layer"); + return mbtilesIndexVo; + } finally { + DynamicDataSourceContextHolder.clearDataSourceKey(); + } + } + + + /** + * 获取瓦片数据 + */ + public MbtilesTiles getTileData(String mark, int z, int x, int y) { + // 坐标翻转 + int flippedY = (int) (Math.pow(2, z) - 1 - y); + // 获取数据源 + DynamicDataSourceContextHolder.setDataSourceKey(mark); + try { + return mbtilesTilesMapper.selectByCondition(z, x, flippedY); + } finally { + DynamicDataSourceContextHolder.clearDataSourceKey(); + } + } + + /** + * 根据格式获取对应的媒体类型 + */ + public String getContentTypeByFormat(String format) { + if ("png".equals(format)) { + return "image/png"; + } else if ("jpg".equals(format) || "jpeg".equals(format)) { + return "image/jpeg"; + } else { + return "application/octet-stream"; + } + } +} diff --git a/src/main/java/com/yjdsj/mbtiles/vo/MbtilesIndexVo.java b/src/main/java/com/yjdsj/mbtiles/vo/MbtilesIndexVo.java new file mode 100644 index 0000000..9b8797f --- /dev/null +++ b/src/main/java/com/yjdsj/mbtiles/vo/MbtilesIndexVo.java @@ -0,0 +1,19 @@ +package com.yjdsj.mbtiles.vo; + +import io.swagger.annotations.ApiModel; +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class MbtilesIndexVo { + private String url; + private Integer minZoom; + private Integer maxZoom; + private String west; + private String south; + private String east; + private String north; + private String loadMethod; + private String schemaName; + private String fileType; +} diff --git a/src/main/java/com/yjdsj/pak/controller/PakController.java b/src/main/java/com/yjdsj/pak/controller/PakController.java new file mode 100644 index 0000000..07ea27b --- /dev/null +++ b/src/main/java/com/yjdsj/pak/controller/PakController.java @@ -0,0 +1,73 @@ +package com.yjdsj.pak.controller; + + +import com.yjdsj.pak.service.PakService; +import io.swagger.annotations.Api; +import io.swagger.annotations.ApiOperation; +import io.swagger.annotations.ApiParam; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.MediaType; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import javax.annotation.Resource; +import java.io.IOException; + +@Slf4j +@RestController +@Api(tags = "地形资源相关") +@RequestMapping("/data/pak") +public class PakController { + @Resource + private PakService pakService; + + @ApiOperation(value = "获取地形资源列表") + @GetMapping("/list") + public Object list() { + return pakService.getPakSourceList(); + } + + @ApiOperation(value = "获取地形资源的详情") + @GetMapping("/detail/{mark}") + public Object cltIndex(@PathVariable @ApiParam(value = "资源标识") String mark) { + return pakService.buildPakIndex(mark); + } + + @ApiOperation(value = "获取JSON数据") + @GetMapping("/{mark}/layer.json") + public ResponseEntity jsonBySourceIdAndParam(@PathVariable("mark") @ApiParam(value = "资源标识") String mark) { + String layerJson = pakService.getCachedLayerJson(mark); + return layerJson != null ? ResponseEntity.ok(layerJson) : ResponseEntity.notFound().build(); + } + + @ApiOperation(value = "获取地形瓦片数据") + @GetMapping("/{mark}/{z}/{x}/{y}.{suffix}") + public ResponseEntity data( + @PathVariable("mark") @ApiParam(value = "资源标识") String mark, + @PathVariable("z") @ApiParam(value = "缩放级别") int z, + @PathVariable("x") @ApiParam(value = "X轴坐标") int x, + @PathVariable("y") @ApiParam(value = "Y轴坐标") int y, + @PathVariable("suffix") @ApiParam(value = "文件后缀") String suffix) { + + try { + // 调用 Service 获取处理后的瓦片数据 + byte[] tileData = pakService.getTileData(mark, z, x, y); + if (tileData == null) { + return ResponseEntity.notFound().build(); + } + + // 根据文件后缀设置正确的Content-Type + MediaType mediaType = "png".equalsIgnoreCase(suffix) ? + MediaType.IMAGE_PNG : MediaType.IMAGE_JPEG; + + return ResponseEntity.ok() + .contentType(mediaType) + .body(tileData); + } catch (IOException e) { + return ResponseEntity.status(500).body("瓦片数据处理失败: " + e.getMessage()); + } + } +} diff --git a/src/main/java/com/yjdsj/pak/domain/PakBlocks.java b/src/main/java/com/yjdsj/pak/domain/PakBlocks.java new file mode 100644 index 0000000..ff9e938 --- /dev/null +++ b/src/main/java/com/yjdsj/pak/domain/PakBlocks.java @@ -0,0 +1,11 @@ +package com.yjdsj.pak.domain; + +import lombok.Data; + +@Data +public class PakBlocks { + private String z; + private String x; + private String y; + private byte[] tile; +} diff --git a/src/main/java/com/yjdsj/pak/domain/PakInfos.java b/src/main/java/com/yjdsj/pak/domain/PakInfos.java new file mode 100644 index 0000000..1542ef6 --- /dev/null +++ b/src/main/java/com/yjdsj/pak/domain/PakInfos.java @@ -0,0 +1,22 @@ +package com.yjdsj.pak.domain; + +import lombok.Data; +@Data +public class PakInfos { + private String minX; + private String minY; + private String maxX; + private String maxY; + private String minLevel; + private String maxLevel; + private String source; + private String type; + private String tiletrans; + private String zip; + private String curLevel; + private String curX; + private String curY; + private byte[] layerJson; + private String contentType; + private Integer proj; +} diff --git a/src/main/java/com/yjdsj/pak/mapper/PakBlocksMapper.java b/src/main/java/com/yjdsj/pak/mapper/PakBlocksMapper.java new file mode 100644 index 0000000..25a0e90 --- /dev/null +++ b/src/main/java/com/yjdsj/pak/mapper/PakBlocksMapper.java @@ -0,0 +1,9 @@ +package com.yjdsj.pak.mapper; + +import com.yjdsj.pak.domain.PakBlocks; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface PakBlocksMapper { + PakBlocks selectByCondition(String tableName, int z, int x, int y); +} diff --git a/src/main/java/com/yjdsj/pak/mapper/PakInfosMapper.java b/src/main/java/com/yjdsj/pak/mapper/PakInfosMapper.java new file mode 100644 index 0000000..764f7ef --- /dev/null +++ b/src/main/java/com/yjdsj/pak/mapper/PakInfosMapper.java @@ -0,0 +1,9 @@ +package com.yjdsj.pak.mapper; + +import com.yjdsj.pak.domain.PakInfos; +import org.apache.ibatis.annotations.Mapper; + +@Mapper +public interface PakInfosMapper { + PakInfos selectInfo(); +} diff --git a/src/main/java/com/yjdsj/pak/service/PakService.java b/src/main/java/com/yjdsj/pak/service/PakService.java new file mode 100644 index 0000000..eab0111 --- /dev/null +++ b/src/main/java/com/yjdsj/pak/service/PakService.java @@ -0,0 +1,134 @@ +package com.yjdsj.pak.service; + +import com.yjdsj.common.ds.DynamicDataSourceContextHolder; +import com.yjdsj.common.map.SourceMap; +import com.yjdsj.common.utils.GzipDecompressorUtil; +import com.yjdsj.common.utils.PakTableName; +import com.yjdsj.pak.domain.PakBlocks; +import com.yjdsj.pak.domain.PakInfos; +import com.yjdsj.pak.mapper.PakBlocksMapper; +import com.yjdsj.pak.mapper.PakInfosMapper; +import com.yjdsj.pak.vo.PakIndexVo; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.BeanUtils; +import org.springframework.stereotype.Service; + +import javax.annotation.Resource; +import java.io.IOException; +import java.util.HashMap; +import java.util.Map; + +@Slf4j +@Service +public class PakService { + @Resource + private SourceMap sourceMap; + @Resource + private PakInfosMapper pakInfosMapper; + @Resource + private PakBlocksMapper pakBlocksMapper; + + // 资源压缩状态缓存 + private final Map isZipCache = new HashMap<>(); + // JSON数据缓存 + private final Map jsonCache = new HashMap<>(); + + /** + * 获取地形资源列表 + */ + public Map getPakSourceList() { + return sourceMap.getPakSourceList(); + } + + /** + * 构建地形资源索引信息(含URL、元数据)并更新缓存 + */ + public PakIndexVo buildPakIndex(String mark) { + final String XYZ = "xyz"; + final String TMS = "tms"; + final String TERRAIN_TYPE = "terrain"; + final String WEBMERCATORTILINGSCHEME = "WebMercatorTilingScheme"; + final String GEOGRAPHICTILINGSCHEME = "GeographicTilingScheme"; + final int PROJ_4326 = 4326; + + DynamicDataSourceContextHolder.setDataSourceKey(mark); + try { + // 查询资源元数据并校验 + PakInfos pakInfos = pakInfosMapper.selectInfo(); + if (pakInfos == null) { + return null; + } + // 复制属性并初始化VO + PakIndexVo pakIndexVo = new PakIndexVo(); + BeanUtils.copyProperties(pakInfos, pakIndexVo); + // 设置地理坐标范围 + pakIndexVo.setWest(pakInfos.getMinX()); + pakIndexVo.setSouth(pakInfos.getMinY()); + pakIndexVo.setEast(pakInfos.getMaxX()); + pakIndexVo.setNorth(pakInfos.getMaxY()); + // 设置加载方式 + String tiletrans = pakInfos.getTiletrans(); + pakIndexVo.setLoadMethod(TMS.equals(tiletrans) ? TMS : XYZ); + // 优化投影名称设置 + pakIndexVo.setSchemaName(WEBMERCATORTILINGSCHEME); + Integer proj = pakInfos.getProj(); + if (proj != null && proj == PROJ_4326) { + pakIndexVo.setSchemaName(GEOGRAPHICTILINGSCHEME); + } + // 构建资源访问URL + String baseUrl = String.format("/data/pak/%s", mark); + if (TERRAIN_TYPE.equals(pakInfos.getType())) { + pakIndexVo.setUrl(baseUrl); + pakIndexVo.setFileType("terrain"); + } else { + // 安全处理 contentType 解析 + String contentType = pakInfos.getContentType(); + String suffix = "unknown"; + if (contentType != null && contentType.contains("/")) { + String[] typeParts = contentType.split("/"); + if (typeParts.length > 1) { + suffix = typeParts[1]; + } + } + pakIndexVo.setUrl(baseUrl + "/{z}/{x}/{y}." + suffix); + pakIndexVo.setFileType("layer"); + } + // 缓存更新 + String zipFlag = pakInfos.getZip(); + isZipCache.put(mark, zipFlag != null && Integer.parseInt(zipFlag) > 0); + jsonCache.put(mark, new String(pakInfos.getLayerJson() != null ? pakInfos.getLayerJson() : new byte[0])); + return pakIndexVo; + } finally { + // 确保数据源清理 + DynamicDataSourceContextHolder.clearDataSourceKey(); + } + } + + /** + * 获取缓存的 layer.json 数据 + */ + public String getCachedLayerJson(String mark) { + return jsonCache.get(mark); + } + + /** + * 根据坐标获取瓦片数据(含压缩处理) + */ + public byte[] getTileData(String mark, int z, int x, int y) throws IOException { + DynamicDataSourceContextHolder.setDataSourceKey(mark); + try { + // 确定目标表名(通过工具类计算) + String tableName = PakTableName.getTableName(z, x, y); + // 查询瓦片数据 + PakBlocks pakBlocks = pakBlocksMapper.selectByCondition(tableName, z, x, y); + if (pakBlocks == null) { + return null; + } + // 处理压缩数据(根据缓存的压缩状态) + Boolean isZip = isZipCache.getOrDefault(mark, false); + return isZip ? GzipDecompressorUtil.decompress(pakBlocks.getTile()) : pakBlocks.getTile(); + } finally { + DynamicDataSourceContextHolder.clearDataSourceKey(); + } + } +} diff --git a/src/main/java/com/yjdsj/pak/vo/PakIndexVo.java b/src/main/java/com/yjdsj/pak/vo/PakIndexVo.java new file mode 100644 index 0000000..9ae6a8d --- /dev/null +++ b/src/main/java/com/yjdsj/pak/vo/PakIndexVo.java @@ -0,0 +1,19 @@ +package com.yjdsj.pak.vo; + +import io.swagger.annotations.ApiModelProperty; +import lombok.Data; + +@Data +public class PakIndexVo { + private String west; + private String south; + private String east; + private String north; + private String minLevel; + private String maxLevel; + private String url; + private String type; + private String loadMethod; + private String schemaName; + private String fileType; +} diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml new file mode 100644 index 0000000..9cbecf9 --- /dev/null +++ b/src/main/resources/application.yml @@ -0,0 +1,3 @@ +server: + host: 127.0.0.1 + port: 8080 diff --git a/src/main/resources/mapper/CltInfosMapper.xml b/src/main/resources/mapper/CltInfosMapper.xml new file mode 100644 index 0000000..7926be3 --- /dev/null +++ b/src/main/resources/mapper/CltInfosMapper.xml @@ -0,0 +1,8 @@ + + + + + + diff --git a/src/main/resources/mapper/CltTilesMapper.xml b/src/main/resources/mapper/CltTilesMapper.xml new file mode 100644 index 0000000..b61a12e --- /dev/null +++ b/src/main/resources/mapper/CltTilesMapper.xml @@ -0,0 +1,17 @@ + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/MbtilesMetaDataMapper.xml b/src/main/resources/mapper/MbtilesMetaDataMapper.xml new file mode 100644 index 0000000..e12449d --- /dev/null +++ b/src/main/resources/mapper/MbtilesMetaDataMapper.xml @@ -0,0 +1,14 @@ + + + + + + + + + diff --git a/src/main/resources/mapper/MbtilesTilesMapper.xml b/src/main/resources/mapper/MbtilesTilesMapper.xml new file mode 100644 index 0000000..36a05ef --- /dev/null +++ b/src/main/resources/mapper/MbtilesTilesMapper.xml @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/PakBlocksMapper.xml b/src/main/resources/mapper/PakBlocksMapper.xml new file mode 100644 index 0000000..c339442 --- /dev/null +++ b/src/main/resources/mapper/PakBlocksMapper.xml @@ -0,0 +1,19 @@ + + + + + + + + + + + + + diff --git a/src/main/resources/mapper/PakInfosMapper.xml b/src/main/resources/mapper/PakInfosMapper.xml new file mode 100644 index 0000000..ef73291 --- /dev/null +++ b/src/main/resources/mapper/PakInfosMapper.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + + + + + + + + +