Browse Source

[feat]提交:修改部门区域解析逻辑

pull/7/head
杨威 2 weeks ago
parent
commit
1842065bfc
  1. 6
      dk-modules/system/src/main/java/org/dromara/system/controller/system/SysDepartBoundaryController.java
  2. 25
      dk-modules/system/src/main/java/org/dromara/system/domain/kmz/KmzResult.java
  3. 15
      dk-modules/system/src/main/java/org/dromara/system/domain/kmz/Placemark.java
  4. 90
      dk-modules/system/src/main/java/org/dromara/system/service/impl/SysDepartBoundaryServiceImpl.java
  5. 240
      dk-modules/system/src/main/java/org/dromara/system/utils/KmzParserUtil.java
  6. 11
      dk-modules/system/src/main/java/org/dromara/system/utils/OvkmzParser.java
  7. 53
      dk-modules/system/src/main/java/org/dromara/system/utils/ShpAnalysisUtil.java
  8. 10
      dk-modules/system/src/main/resources/mapper/system/SysDepartBoundaryMapper.xml

6
dk-modules/system/src/main/java/org/dromara/system/controller/system/SysDepartBoundaryController.java

@ -59,10 +59,10 @@ public class SysDepartBoundaryController extends BaseController {
* @return
*/
@Operation(summary ="新增部门区域",description = "新增部门区域(areaType: 0:市级 1:区级(县) 2:镇级(街道) 3:村级)")
@RequestMapping(value = "/upload", method = RequestMethod.POST)
@RequestMapping(value = "/{areaType}/{parentId}/upload", method = RequestMethod.POST)
public R<Void> uploadShpFile(@RequestParam("file") MultipartFile file,
@RequestParam("areaType") Integer areaType,
@RequestParam("parentId") Long parentId) {
@PathVariable Integer areaType,
@PathVariable Long parentId) {
return toAjax(departBoundaryService.uploadShpFile(file,areaType,parentId));
}

25
dk-modules/system/src/main/java/org/dromara/system/domain/kmz/KmzResult.java

@ -0,0 +1,25 @@
package org.dromara.system.domain.kmz;
import lombok.Data;
import java.util.Map;
@Data
public class KmzResult {
private String name;
private Map<String, String> attributes;
private String wkt;
public KmzResult(String name, Map<String, String> attributes, String wkt) {
this.name = name;
this.attributes = attributes;
this.wkt = wkt;
}
public KmzResult() {
}
}

15
dk-modules/system/src/main/java/org/dromara/system/domain/kmz/Placemark.java

@ -0,0 +1,15 @@
package org.dromara.system.domain.kmz;
import lombok.Data;
import java.util.Map;
@Data
public class Placemark {
private String name;
private String coordinates;
private Map<String, String> attributes;
}

90
dk-modules/system/src/main/java/org/dromara/system/service/impl/SysDepartBoundaryServiceImpl.java

@ -13,12 +13,14 @@ import org.dromara.system.domain.SysGeospatialVectors;
import org.dromara.system.domain.SysVectorDict;
import org.dromara.system.domain.bo.SysDepartBoundaryBo;
import org.dromara.system.domain.bo.SysDeptBo;
import org.dromara.system.domain.kmz.KmzResult;
import org.dromara.system.domain.vo.SysDeptVo;
import org.dromara.system.mapper.SysDepartBoundaryMapper;
import org.dromara.system.service.ISysDepartBoundaryService;
import org.dromara.system.service.ISysDeptService;
import org.dromara.system.service.ISysVectorDictService;
import org.dromara.system.utils.BatchProcessorUtil;
import org.dromara.system.utils.KmzParserUtil;
import org.dromara.system.utils.ShpAnalysisUtil;
import org.dromara.common.core.exception.ServiceException;
import org.dromara.common.mybatis.core.page.PageQuery;
@ -30,10 +32,7 @@ import org.springframework.web.multipart.MultipartFile;
import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.*;
import java.util.stream.Collectors;
/**
@ -70,11 +69,14 @@ public class SysDepartBoundaryServiceImpl extends ServiceImpl<SysDepartBoundaryM
@Transactional(rollbackFor = Exception.class)
public boolean uploadShpFile(MultipartFile file,Integer areaType,Long parentId) {
try {
List<SysGeospatialVectors> geospatialVectorsList = buildGeospatialVector(file);
//6、生成新的对象集合存储数据表中
List<SysDepartBoundary> boundaryList = buildBusinessDepartBoundary(geospatialVectorsList,areaType,parentId);
boundaryList = boundaryList.stream().filter(p-> ObjectUtil.isNotEmpty(p.getBoundary())).toList();
// 分批处理
int startIndex = 0; // 从第 0 条开始
int batchSize = 2000; // 每批处理 2000 条
@ -97,9 +99,29 @@ public class SysDepartBoundaryServiceImpl extends ServiceImpl<SysDepartBoundaryM
private List<SysGeospatialVectors> buildGeospatialVector(MultipartFile file){
List<SysGeospatialVectors> geospatialVectorsList = new ArrayList<>();
try {
List<Map<String, String>> mapList = new ArrayList<>();
//1、首先调用解析工具拿到解析的字段集合
InputStream inputStream = file.getInputStream();
List<Map<String, String>> mapList = ShpAnalysisUtil.analysisShpFile(inputStream);
if (file.getOriginalFilename().endsWith(".zip")) {
mapList = ShpAnalysisUtil.analysisShpFile(file.getInputStream());
} else if (file.getOriginalFilename().endsWith(".kmz") || file.getOriginalFilename().endsWith(".ovkmz")) {
List<KmzResult> kmzResults = KmzParserUtil.parseKmz(file.getInputStream());
List<Map<String, String>> kmzList = new ArrayList<>();
kmzResults.forEach(kmzResult -> {
Map<String, String> map = new HashMap<>();
Map<String, String> attributes = kmzResult.getAttributes();
if (ObjectUtil.isNotEmpty(attributes)) {
map.putAll(attributes);
}
map.put("XZQMC", kmzResult.getName());
map.put("the_geom", kmzResult.getName());
kmzList.add(map);
});
mapList = kmzList;
}
// 3. 构建字典map
List<SysVectorDict> fieldsInfoList = vectorDictService.listVectorField();
@ -224,10 +246,47 @@ public class SysDepartBoundaryServiceImpl extends ServiceImpl<SysDepartBoundaryM
.collect(Collectors.groupingBy(item -> item.get("deptName").toString()));
geospatialVectorsList.forEach(param->{
SysDepartBoundary businessDepartBoundary = new SysDepartBoundary();
//获取区划名称
String divisionName = param.getAdminDivisionName();
//是集合
if (isCollectionString(param.getLandCategories())){
List<String> geomList = convertStringToList(param.getLandCategories());
geomList.forEach(item->{
SysDepartBoundary businessDepartBoundary = new SysDepartBoundary();
businessDepartBoundary.setBoundary(item);
//判断是否存在表中,在表中更新部门区域中的部门id信息
if (ObjectUtil.isNotEmpty(namePathMap.get(divisionName))){
Map<String, Object> objectMap = namePathMap.get(divisionName).get(0);
businessDepartBoundary.setDeptName(objectMap.get("deptName") + "");
businessDepartBoundary.setDeptId(Long.valueOf(objectMap.get("deptId") + ""));
businessDepartBoundary.setNamePath(objectMap.get("namePath") + "");
businessDepartBoundary.setAreaType(areaType);
resultList.add(businessDepartBoundary);
}else {
//如果不存在在部门表中则添加
SysDeptBo sysDeptBo = new SysDeptBo();
sysDeptBo.setParentId(parentId);
sysDeptBo.setDeptName(param.getAdminDivisionName());
SysDept sysDept = deptService.addBoundaryDept(sysDeptBo);
List<Map<String,Object>> deptNamePath = deptService.getNamePathList(sysDept.getDeptId());
businessDepartBoundary.setDeptName(sysDept.getDeptName());
businessDepartBoundary.setDeptId(sysDept.getDeptId());
businessDepartBoundary.setNamePath(deptNamePath.getFirst().get("namePath").toString());
businessDepartBoundary.setAreaType(areaType);
resultList.add(businessDepartBoundary);
}
});
}else {
SysDepartBoundary businessDepartBoundary = new SysDepartBoundary();
businessDepartBoundary.setBoundary(param.getLandCategories());
//判断是否存在表中,在表中更新部门区域中的部门id信息
if (ObjectUtil.isNotEmpty(namePathMap.get(divisionName))){
@ -254,10 +313,27 @@ public class SysDepartBoundaryServiceImpl extends ServiceImpl<SysDepartBoundaryM
resultList.add(businessDepartBoundary);
}
}
});
return resultList;
}
public static List<String> convertStringToList(String str) {
// 去除首尾的 "[" 和 "]"
str = str.substring(1, str.length() - 1);
// 使用逗号分割
String[] items = str.split(", ");
// 返回新的 List
return Arrays.asList(items);
}
// 判断字符串是否是集合的字符串形式
public static boolean isCollectionString(String str) {
return str != null && str.startsWith("[") && str.endsWith("]") && str.contains(",");
}
private static String capitalizeFirstLetter(String str) {
if (str == null || str.isEmpty()) {

240
dk-modules/system/src/main/java/org/dromara/system/utils/KmzParserUtil.java

@ -0,0 +1,240 @@
package org.dromara.system.utils;
import org.dromara.system.domain.kmz.KmzResult;
import org.dromara.system.domain.kmz.Placemark;
import java.io.*;
import java.nio.file.*;
import java.util.*;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
/**
* KMZ文件解析器
* KMZ是Google Earth使用的压缩格式包含KML文件和其他资源
*/
public class KmzParserUtil {
/**
* 解析KMZ文件的主方法
*
* @param inputStream KMZ文件的路径
* @return 解析结果列表
* @throws IOException 如果文件读取或处理过程中发生错误
*/
public static List<KmzResult> parseKmz(InputStream inputStream) throws IOException {
// 存储解析出的地标信息
List<Placemark> placemarks = new ArrayList<>();
// 存储最终结果
List<KmzResult> results = new ArrayList<>();
// 创建临时目录用于解压KMZ文件
Path tempDir = Files.createTempDirectory("kmz_parser_");
try {
// 将KMZ文件解压到临时目录
extractKmz(inputStream, tempDir.toString());
// 遍历临时目录,查找并解析所有KML文件
Files.walk(tempDir)
.filter(path -> path.toString().endsWith(".kml"))
.forEach(path -> {
try {
String kmlContent = new String(Files.readAllBytes(path));
parseKmlContent(kmlContent, placemarks);
} catch (IOException e) {
e.printStackTrace();
}
});
// 将解析出的地标信息转换为最终结果格式
for (Placemark placemark : placemarks) {
String wkt = convertToWKT(placemark.getCoordinates());
results.add(new KmzResult(placemark.getName(), placemark.getAttributes(), wkt));
}
} finally {
// 清理临时目录
deleteDirectory(tempDir);
}
return results;
}
/**
* 解压KMZ文件到指定目录
*
* @param inputStream KMZ文件路径
* @param outputDir 输出目录
*/
private static void extractKmz(InputStream inputStream, String outputDir) throws IOException {
try (ZipInputStream zis = new ZipInputStream(inputStream)) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
Path outputPath = Paths.get(outputDir, entry.getName());
// 创建必要的父目录
Files.createDirectories(outputPath.getParent());
// 跳过目录条目
if (entry.isDirectory()) {
continue;
}
// 解压文件内容
try (OutputStream out = Files.newOutputStream(outputPath)) {
byte[] buffer = new byte[1024];
int len;
while ((len = zis.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
}
}
}
}
/**
* 递归删除目录及其内容
*
* @param directory 要删除的目录
*/
private static void deleteDirectory(Path directory) throws IOException {
Files.walk(directory)
.sorted(Comparator.reverseOrder())
.forEach(path -> {
try {
Files.delete(path);
} catch (IOException e) {
e.printStackTrace();
}
});
}
/**
* 解析KML文件内容
*
* @param kmlContent KML文件内容
* @param placemarks 存储解析出的地标信息
*/
private static void parseKmlContent(String kmlContent, List<Placemark> placemarks) {
String[] lines = kmlContent.split("\n");
Placemark currentPlacemark = null;
boolean inDescription = false;
StringBuilder descriptionContent = new StringBuilder();
// 添加调试日志
System.out.println("开始解析KML内容,总行数: " + lines.length);
for (String line : lines) {
line = line.trim();
if (line.startsWith("<Placemark>")) {
if (currentPlacemark != null) {
placemarks.add(currentPlacemark);
}
currentPlacemark = new Placemark();
System.out.println("发现新的Placemark");
} else if (line.startsWith("<name>")) {
if (currentPlacemark != null) {
String name = line.replace("<name>", "").replace("</name>", "").trim();
currentPlacemark.setName(name);
System.out.println("解析到名称: " + name);
}
} else if (line.startsWith("<description>")) {
inDescription = true;
descriptionContent = new StringBuilder();
System.out.println("开始解析描述");
} else if (line.endsWith("</description>")) {
inDescription = false;
if (currentPlacemark != null) {
String desc = descriptionContent.toString();
System.out.println("描述内容: " + desc);
parseDescription(desc, currentPlacemark);
}
} else if (inDescription) {
descriptionContent.append(line).append("\n");
} else if (line.startsWith("<coordinates>")) {
if (currentPlacemark != null) {
String coords = line.replace("<coordinates>", "").replace("</coordinates>", "").trim();
currentPlacemark.setCoordinates(coords);
System.out.println("解析到坐标: " + coords);
}
}
}
if (currentPlacemark != null) {
placemarks.add(currentPlacemark);
}
// 打印解析结果
System.out.println("解析完成,共找到 " + placemarks.size() + " 个地标");
for (Placemark p : placemarks) {
System.out.println("地标信息:");
System.out.println(" 名称: " + p.getName());
System.out.println(" 属性: " + p.getAttributes());
System.out.println(" 坐标: " + p.getCoordinates());
}
}
/**
* 解析描述内容提取属性信息
*
* @param description 描述内容
* @param placemark 地标对象
*/
private static void parseDescription(String description, Placemark placemark) {
System.out.println("开始解析描述内容: " + description);
String[] lines = description.split("\n");
Map<String, String> attributes = new HashMap<>();
for (String line : lines) {
line = line.trim();
if (line.contains("\":")) {
String[] parts = line.split("\":");
if (parts.length == 2) {
String key = parts[0].replace("\"", "").trim();
String value = parts[1].replace("\"", "").trim();
attributes.put(key, value);
System.out.println("解析到属性 - 键: " + key + ", 值: " + value);
}
}
}
placemark.setAttributes(attributes);
}
/**
* 将坐标字符串转换为WKTWell-Known Text格式
*
* @param coordinates 坐标字符串
* @return WKT格式的字符串
*/
public static String convertToWKT(String coordinates) {
if (coordinates == null || coordinates.trim().isEmpty()) {
return "";
}
// 构建WKT格式的多边形
StringBuilder wkt = new StringBuilder("POLYGON((");
String[] coordPairs = coordinates.trim().split("\\s+");
// 处理每个坐标对
for (int i = 0; i < coordPairs.length; i++) {
String[] coords = coordPairs[i].split(",");
if (coords.length >= 2) {
wkt.append(coords[0]).append(" ").append(coords[1]);
if (i < coordPairs.length - 1) {
wkt.append(", ");
}
}
}
wkt.append("))");
StringBuffer polygonSb = new StringBuffer("GEOMETRYCOLLECTION(");
polygonSb.append(wkt);
polygonSb.append(")");
return polygonSb.toString();
}
}

11
dk-modules/system/src/main/java/org/dromara/system/utils/OvkmzParser.java

@ -1,11 +0,0 @@
package org.dromara.system.utils;
/**
* 解析ovkmz文件
*/
public class OvkmzParser {
}

53
dk-modules/system/src/main/java/org/dromara/system/utils/ShpAnalysisUtil.java

@ -45,6 +45,7 @@ public class ShpAnalysisUtil {
/**
* 读取shp文件返回要素集合
*
* @param shpPath shp文件路径
* @return 要素集合包含地理位置和属性信息
*/
@ -52,7 +53,7 @@ public class ShpAnalysisUtil {
List<Map<String, String>> resultList = new ArrayList<>();
try {
resultList=analysisShpFile(shpPath, "GBK");
resultList = analysisShpFile(shpPath, "GBK");
} catch (Exception e) {
log.error("无法使用支持的字符编码读取shp文件: {}", e.getMessage());
}
@ -62,6 +63,7 @@ public class ShpAnalysisUtil {
/**
* 读取shp文件返回要素集合
*
* @param shpInputStream shp文件输入流
* @return 要素集合包含地理位置和属性信息
*/
@ -102,7 +104,7 @@ public class ShpAnalysisUtil {
boolean shpFound = false;
while ((entry = zipInputStream.getNextEntry()) != null) {
// 处理文件名编码问题
log.info("文件名字:{}",entry.getName());
log.info("文件名字:{}", entry.getName());
File outputFile = new File(tempDir, entry.getName());
// 创建父目录
@ -296,20 +298,23 @@ public class ShpAnalysisUtil {
}
private static String analysisGeometry(Geometry geometry, MathTransform transform) throws TransformException {
String resultStr = "";
private static Object analysisGeometry(Geometry geometry, MathTransform transform) throws TransformException {
// 对每个 Polygon 进行坐标转换
Geometry transformedGeometry = JTS.transform(geometry, transform);
MultiPolygon multiPolygon = (MultiPolygon) transformedGeometry;
Geometry unionGeometry = UnaryUnionOp.union(multiPolygon);
if (unionGeometry instanceof Polygon) {
Polygon polygon = (Polygon) unionGeometry;
resultStr = convertPolygonToWKT(polygon);
}
return convertPolygonToWKT(polygon);
} else if (unionGeometry instanceof MultiPolygon) {
MultiPolygon muPolygon = (MultiPolygon) unionGeometry;
return resultStr;
return convertMultiPolygonToWKT(muPolygon);
}
return "";
}
private static String convertPolygonToWKT(Polygon polygon) {
@ -321,16 +326,45 @@ public class ShpAnalysisUtil {
if (i > 0) polygonSb.append(",");
polygonSb.append(coord.y + " " + coord.x);
}
if(coordinates[0].x !=coordinates[coordinates.length-1].x){
polygonSb.append(","+coordinates[0].y + " " + coordinates[0].x);
if (coordinates[0].x != coordinates[coordinates.length - 1].x) {
polygonSb.append("," + coordinates[0].y + " " + coordinates[0].x);
}
polygonSb.append(")))");
return polygonSb.toString();
}
private static List<String> convertMultiPolygonToWKT(MultiPolygon multiPolygon) {
List<String> resultList = new ArrayList<>();
// 获取 MultiPolygon 中的每个 Polygon
for (int j = 0; j < multiPolygon.getNumGeometries(); j++) {
Polygon polygon = (Polygon) multiPolygon.getGeometryN(j);
StringBuffer polygonSb = new StringBuffer("GEOMETRYCOLLECTION(POLYGON((");
Coordinate[] coordinates = polygon.getCoordinates();
for (int i = 0; i < coordinates.length; i++) {
Coordinate coord = coordinates[i];
if (i > 0) polygonSb.append(",");
polygonSb.append(coord.y + " " + coord.x);
}
if (coordinates[0].x != coordinates[coordinates.length - 1].x) {
polygonSb.append("," + coordinates[0].y + " " + coordinates[0].x);
}
polygonSb.append(")))");
resultList.add(polygonSb.toString());
}
return resultList;
}
/**
* 递归删除目录
*
* @param directory 要删除的目录
*/
private static void deleteDirectory(File directory) {
@ -356,6 +390,7 @@ public class ShpAnalysisUtil {
/**
* 在指定目录中查找第一个.shp文件
*
* @param directory 搜索目录
* @return 找到的第一个.shp文件未找到返回null
*/

10
dk-modules/system/src/main/resources/mapper/system/SysDepartBoundaryMapper.xml

@ -7,7 +7,7 @@
db.dept_id,
db.dept_name,
db.type,
db.community_name,
db.name_path,
CONCAT(
REPLACE ( REPLACE ( REPLACE ( REPLACE ( ST_AsText ( db.boundary ), 'GEOMETRYCOLLECTION(POLYGON((', '[[' ), ')))', ']]' ), ',', '],[' ), ' ', ',' )
) AS boundary,
@ -20,8 +20,8 @@
<if test="condition.deptId != null and condition.deptId != ''">
and (db.dept_id = #{condition.deptId} or d.parent_id = #{condition.deptId})
</if>
<if test="condition.communityName != null and condition.communityName != ''">
and db.community_name like concat(concat('%',#{condition.communityName}),'%')
<if test="condition.namePath != null and condition.namePath != ''">
and db.name_path like concat(concat('%',#{condition.namePath}),'%')
</if>
</where>
@ -34,7 +34,7 @@
db.dept_id,
db.dept_name,
db.type,
db.community_name,
db.name_path,
CONCAT(
REPLACE ( REPLACE ( REPLACE ( REPLACE ( ST_AsText ( db.boundary ), 'GEOMETRYCOLLECTION(POLYGON((', '[[' ), ')))', ']]' ), ',', '],[' ), ' ', ',' )
) AS boundary,
@ -63,7 +63,7 @@
db.dept_id,
db.dept_name,
db.type,
db.community_name,
db.name_path,
CONCAT(
REPLACE ( REPLACE ( REPLACE ( REPLACE ( ST_AsText ( db.boundary ), 'GEOMETRYCOLLECTION(POLYGON((', '[[' ), ')))', ']]' ), ',', '],[' ), ' ', ',' )
) AS boundary,

Loading…
Cancel
Save