核心SDK提交
This commit is contained in:
11
src/main/java/com/yjdsj/SdkApp.java
Normal file
11
src/main/java/com/yjdsj/SdkApp.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
66
src/main/java/com/yjdsj/clt/controller/CltController.java
Normal file
66
src/main/java/com/yjdsj/clt/controller/CltController.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
8
src/main/java/com/yjdsj/clt/domain/CltInfos.java
Normal file
8
src/main/java/com/yjdsj/clt/domain/CltInfos.java
Normal file
@ -0,0 +1,8 @@
|
||||
package com.yjdsj.clt.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class CltInfos {
|
||||
private String params;
|
||||
}
|
10
src/main/java/com/yjdsj/clt/domain/CltTiles.java
Normal file
10
src/main/java/com/yjdsj/clt/domain/CltTiles.java
Normal file
@ -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;
|
||||
}
|
8
src/main/java/com/yjdsj/clt/mapper/CltInfosMapper.java
Normal file
8
src/main/java/com/yjdsj/clt/mapper/CltInfosMapper.java
Normal file
@ -0,0 +1,8 @@
|
||||
package com.yjdsj.clt.mapper;
|
||||
|
||||
import org.apache.ibatis.annotations.Mapper;
|
||||
|
||||
@Mapper
|
||||
public interface CltInfosMapper {
|
||||
String selectParams();
|
||||
}
|
9
src/main/java/com/yjdsj/clt/mapper/CltTilesMapper.java
Normal file
9
src/main/java/com/yjdsj/clt/mapper/CltTilesMapper.java
Normal file
@ -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);
|
||||
}
|
120
src/main/java/com/yjdsj/clt/service/CltService.java
Normal file
120
src/main/java/com/yjdsj/clt/service/CltService.java
Normal file
@ -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<String, Boolean> zipCache = new ConcurrentHashMap<>();
|
||||
|
||||
public Map<String, String> 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();
|
||||
}
|
||||
}
|
28
src/main/java/com/yjdsj/clt/vo/CltDetailVo.java
Normal file
28
src/main/java/com/yjdsj/clt/vo/CltDetailVo.java
Normal file
@ -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;
|
||||
}
|
||||
}
|
23
src/main/java/com/yjdsj/common/config/CustomCorsFilter.java
Normal file
23
src/main/java/com/yjdsj/common/config/CustomCorsFilter.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
15
src/main/java/com/yjdsj/common/config/ServerConfig.java
Normal file
15
src/main/java/com/yjdsj/common/config/ServerConfig.java
Normal file
@ -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;
|
||||
}
|
34
src/main/java/com/yjdsj/common/config/SwaggerConfig.java
Normal file
34
src/main/java/com/yjdsj/common/config/SwaggerConfig.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
18
src/main/java/com/yjdsj/common/config/WebConfig.java
Normal file
18
src/main/java/com/yjdsj/common/config/WebConfig.java
Normal file
@ -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); // 预检请求的缓存时间(单位:秒)
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
38
src/main/java/com/yjdsj/common/ds/DataSourceConfig.java
Normal file
38
src/main/java/com/yjdsj/common/ds/DataSourceConfig.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -0,0 +1,18 @@
|
||||
package com.yjdsj.common.ds;
|
||||
|
||||
public class DynamicDataSourceContextHolder {
|
||||
|
||||
private static final ThreadLocal<String> 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();
|
||||
}
|
||||
}
|
@ -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<String, DataSource> 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;
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
61
src/main/java/com/yjdsj/common/generator/CodeGenerator.java
Normal file
61
src/main/java/com/yjdsj/common/generator/CodeGenerator.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
36
src/main/java/com/yjdsj/common/handler/BlobTypeHandler.java
Normal file
36
src/main/java/com/yjdsj/common/handler/BlobTypeHandler.java
Normal file
@ -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<byte[]> {
|
||||
|
||||
@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);
|
||||
}
|
||||
}
|
9
src/main/java/com/yjdsj/common/map/MapDto.java
Normal file
9
src/main/java/com/yjdsj/common/map/MapDto.java
Normal file
@ -0,0 +1,9 @@
|
||||
package com.yjdsj.common.map;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MapDto {
|
||||
private String filePath;
|
||||
private Integer userId;
|
||||
}
|
91
src/main/java/com/yjdsj/common/map/SourceMap.java
Normal file
91
src/main/java/com/yjdsj/common/map/SourceMap.java
Normal file
@ -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<String, String> 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<String, String> getCltSourceList() {
|
||||
Map<String, String> sourceList = new HashMap<>();
|
||||
for (Map.Entry<String, String> 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<String, String> getMBTilesSourceList() {
|
||||
Map<String, String> sourceList = new HashMap<>();
|
||||
for (Map.Entry<String, String> 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<String, String> getPakSourceList() {
|
||||
Map<String, String> sourceList = new HashMap<>();
|
||||
for (Map.Entry<String, String> 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<String, String> entry : sourceMap.entrySet()) {
|
||||
System.out.println("MD5 Key: " + entry.getKey() + ", File Path: " + entry.getValue());
|
||||
}
|
||||
}
|
||||
}
|
27
src/main/java/com/yjdsj/common/map/SourceTypeMap.java
Normal file
27
src/main/java/com/yjdsj/common/map/SourceTypeMap.java
Normal file
@ -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<String, String> SourceType = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 初始化
|
||||
*/
|
||||
static {
|
||||
SourceType.put("clt", "tileset");
|
||||
}
|
||||
|
||||
// 获取资源对应的类型
|
||||
public static String getSourceType(String fileExtension) {
|
||||
return SourceType.get(fileExtension);
|
||||
}
|
||||
}
|
23
src/main/java/com/yjdsj/common/utils/DecompressionUtil.java
Normal file
23
src/main/java/com/yjdsj/common/utils/DecompressionUtil.java
Normal file
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
7
src/main/java/com/yjdsj/common/utils/JacksonUtil.java
Normal file
7
src/main/java/com/yjdsj/common/utils/JacksonUtil.java
Normal file
@ -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();
|
||||
}
|
36
src/main/java/com/yjdsj/common/utils/MD5HashUtil.java
Normal file
36
src/main/java/com/yjdsj/common/utils/MD5HashUtil.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
21
src/main/java/com/yjdsj/common/utils/PakTableName.java
Normal file
21
src/main/java/com/yjdsj/common/utils/PakTableName.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
51
src/main/java/com/yjdsj/common/utils/PositionUtil.java
Normal file
51
src/main/java/com/yjdsj/common/utils/PositionUtil.java
Normal file
@ -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);
|
||||
}
|
||||
}
|
@ -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());
|
||||
}
|
||||
}
|
@ -0,0 +1,9 @@
|
||||
package com.yjdsj.mbtiles.domain;
|
||||
|
||||
import lombok.Data;
|
||||
|
||||
@Data
|
||||
public class MbtilesMetaData {
|
||||
private String name;
|
||||
private String value;
|
||||
}
|
12
src/main/java/com/yjdsj/mbtiles/domain/MbtilesTiles.java
Normal file
12
src/main/java/com/yjdsj/mbtiles/domain/MbtilesTiles.java
Normal file
@ -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;
|
||||
}
|
@ -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<MbtilesMetaData> selectList();
|
||||
}
|
@ -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);
|
||||
}
|
132
src/main/java/com/yjdsj/mbtiles/service/MbtilesService.java
Normal file
132
src/main/java/com/yjdsj/mbtiles/service/MbtilesService.java
Normal file
@ -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<String, String> 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<MbtilesMetaData> mbtilesMetaDataList = mbtilesMetaDataMapper.selectList();
|
||||
Map<String, String> metaDataMap = new HashMap<>(mbtilesMetaDataList.size());
|
||||
for (MbtilesMetaData meta : mbtilesMetaDataList) {
|
||||
metaDataMap.put(meta.getName(), meta.getValue());
|
||||
}
|
||||
|
||||
// 处理边界信息
|
||||
String[] boundsArray = metaDataMap.get("bounds").split(",");
|
||||
|
||||
// 处理缩放级别
|
||||
Map<Integer, Integer> 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";
|
||||
}
|
||||
}
|
||||
}
|
19
src/main/java/com/yjdsj/mbtiles/vo/MbtilesIndexVo.java
Normal file
19
src/main/java/com/yjdsj/mbtiles/vo/MbtilesIndexVo.java
Normal file
@ -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;
|
||||
}
|
73
src/main/java/com/yjdsj/pak/controller/PakController.java
Normal file
73
src/main/java/com/yjdsj/pak/controller/PakController.java
Normal file
@ -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());
|
||||
}
|
||||
}
|
||||
}
|
11
src/main/java/com/yjdsj/pak/domain/PakBlocks.java
Normal file
11
src/main/java/com/yjdsj/pak/domain/PakBlocks.java
Normal file
@ -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;
|
||||
}
|
22
src/main/java/com/yjdsj/pak/domain/PakInfos.java
Normal file
22
src/main/java/com/yjdsj/pak/domain/PakInfos.java
Normal file
@ -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;
|
||||
}
|
9
src/main/java/com/yjdsj/pak/mapper/PakBlocksMapper.java
Normal file
9
src/main/java/com/yjdsj/pak/mapper/PakBlocksMapper.java
Normal file
@ -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);
|
||||
}
|
9
src/main/java/com/yjdsj/pak/mapper/PakInfosMapper.java
Normal file
9
src/main/java/com/yjdsj/pak/mapper/PakInfosMapper.java
Normal file
@ -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();
|
||||
}
|
134
src/main/java/com/yjdsj/pak/service/PakService.java
Normal file
134
src/main/java/com/yjdsj/pak/service/PakService.java
Normal file
@ -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<String, Boolean> isZipCache = new HashMap<>();
|
||||
// JSON数据缓存
|
||||
private final Map<String, String> jsonCache = new HashMap<>();
|
||||
|
||||
/**
|
||||
* 获取地形资源列表
|
||||
*/
|
||||
public Map<String, String> 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();
|
||||
}
|
||||
}
|
||||
}
|
19
src/main/java/com/yjdsj/pak/vo/PakIndexVo.java
Normal file
19
src/main/java/com/yjdsj/pak/vo/PakIndexVo.java
Normal file
@ -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;
|
||||
}
|
Reference in New Issue
Block a user