22 changed files with 810 additions and 96 deletions
@ -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() { |
||||
|
} |
||||
|
} |
@ -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; |
||||
|
|
||||
|
} |
@ -0,0 +1,140 @@ |
|||||
|
package org.dromara.system.utils; |
||||
|
|
||||
|
import cn.hutool.core.util.ObjectUtil; |
||||
|
|
||||
|
import java.io.*; |
||||
|
import java.util.*; |
||||
|
|
||||
|
/** |
||||
|
* KML文件解析器 |
||||
|
* KML是Google Earth使用的标记语言格式 |
||||
|
*/ |
||||
|
public class KmlParserUtil { |
||||
|
|
||||
|
/** |
||||
|
* 从KML内容中提取所有Placemark数据 |
||||
|
* |
||||
|
* @param kmlContent KML内容 |
||||
|
* @return 包含name和coordinates的Map列表 |
||||
|
*/ |
||||
|
public static List<Map<String, String>> extractPlacemarks(String kmlContent) { |
||||
|
List<Map<String, String>> placemarkList = new ArrayList<>(); |
||||
|
int startIndex = 0; |
||||
|
|
||||
|
while (true) { |
||||
|
// 查找Placemark开始标签
|
||||
|
int startPlacemark = kmlContent.indexOf("<Placemark>", startIndex); |
||||
|
if (startPlacemark == -1) { |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
// 查找Placemark结束标签
|
||||
|
int endPlacemark = kmlContent.indexOf("</Placemark>", startPlacemark); |
||||
|
if (endPlacemark == -1) { |
||||
|
break; |
||||
|
} |
||||
|
|
||||
|
// 提取当前Placemark的内容
|
||||
|
String placemarkContent = kmlContent.substring(startPlacemark, endPlacemark + "</Placemark>".length()); |
||||
|
|
||||
|
// 提取name
|
||||
|
String name = ""; |
||||
|
int nameStart = placemarkContent.indexOf("<name>"); |
||||
|
int nameEnd = placemarkContent.indexOf("</name>"); |
||||
|
if (nameStart != -1 && nameEnd != -1) { |
||||
|
name = placemarkContent.substring(nameStart + "<name>".length(), nameEnd).trim(); |
||||
|
} |
||||
|
|
||||
|
// 提取coordinates
|
||||
|
String coordinates = ""; |
||||
|
int coordStart = placemarkContent.indexOf("<coordinates>"); |
||||
|
int coordEnd = placemarkContent.indexOf("</coordinates>"); |
||||
|
if (coordStart != -1 && coordEnd != -1) { |
||||
|
coordinates = placemarkContent.substring(coordStart + "<coordinates>".length(), coordEnd).trim(); |
||||
|
} |
||||
|
|
||||
|
//提取<Data name ="name">
|
||||
|
if (ObjectUtil.isEmpty(name)){ |
||||
|
int dataStart = placemarkContent.indexOf("Data name =\"name\""); |
||||
|
if (dataStart != -1) { |
||||
|
int valueStart = placemarkContent.indexOf("<value>", dataStart); |
||||
|
int valueEnd = placemarkContent.indexOf("</value>", valueStart); |
||||
|
if (valueStart != -1 && valueEnd != -1) { |
||||
|
name = placemarkContent.substring(valueStart + "<value>".length(), valueEnd).trim(); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
||||
|
// 创建Map并添加到列表
|
||||
|
Map<String, String> placemarkMap = new HashMap<>(); |
||||
|
placemarkMap.put("O_NAME", name); |
||||
|
|
||||
|
String wkt = convertToWKT(coordinates); |
||||
|
|
||||
|
placemarkMap.put("the_geom", wkt); |
||||
|
|
||||
|
placemarkList.add(placemarkMap); |
||||
|
|
||||
|
// 更新搜索起始位置
|
||||
|
startIndex = endPlacemark + "</Placemark>".length(); |
||||
|
} |
||||
|
|
||||
|
return placemarkList; |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将坐标字符串转换为WKT(Well-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(); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 解析KML文件的主方法 |
||||
|
* |
||||
|
* @param inputStream KML文件的输入流 |
||||
|
* @return 包含name和coordinates的Map列表 |
||||
|
* @throws IOException 如果文件读取或处理过程中发生错误 |
||||
|
*/ |
||||
|
public static List<Map<String, String>> parseKml(InputStream inputStream) throws IOException { |
||||
|
try (BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream))) { |
||||
|
StringBuilder kmlContent = new StringBuilder(); |
||||
|
String line; |
||||
|
while ((line = reader.readLine()) != null) { |
||||
|
kmlContent.append(line).append("\n"); |
||||
|
} |
||||
|
return extractPlacemarks(kmlContent.toString()); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
@ -0,0 +1,262 @@ |
|||||
|
package org.dromara.system.utils; |
||||
|
|
||||
|
import cn.hutool.core.util.ObjectUtil; |
||||
|
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(); |
||||
|
} |
||||
|
}); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 处理当前Placemark的所有坐标,为每个坐标创建新的Placemark对象 |
||||
|
* |
||||
|
* @param currentPlacemark 当前Placemark对象 |
||||
|
* @param currentCoordinates 当前Placemark的所有坐标 |
||||
|
* @param placemarks 存储所有Placemark的列表 |
||||
|
*/ |
||||
|
private static void processCurrentPlacemarkCoordinates(Placemark currentPlacemark, List<String> currentCoordinates, List<Placemark> placemarks) { |
||||
|
if (currentPlacemark != null && !currentCoordinates.isEmpty()) { |
||||
|
for (String coords : currentCoordinates) { |
||||
|
Placemark newPlacemark = new Placemark(); |
||||
|
newPlacemark.setName(currentPlacemark.getName()); |
||||
|
if (ObjectUtil.isNotEmpty(currentPlacemark.getAttributes())){ |
||||
|
newPlacemark.setAttributes(new HashMap<>(currentPlacemark.getAttributes())); |
||||
|
} |
||||
|
newPlacemark.setCoordinates(coords); |
||||
|
placemarks.add(newPlacemark); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 解析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(); |
||||
|
List<String> currentCoordinates = new ArrayList<>(); |
||||
|
|
||||
|
// 添加调试日志
|
||||
|
System.out.println("开始解析KML内容,总行数: " + lines.length); |
||||
|
|
||||
|
for (String line : lines) { |
||||
|
line = line.trim(); |
||||
|
|
||||
|
if (line.startsWith("<Placemark>")) { |
||||
|
// 处理当前Placemark的所有坐标
|
||||
|
processCurrentPlacemarkCoordinates(currentPlacemark, currentCoordinates, placemarks); |
||||
|
currentPlacemark = new Placemark(); |
||||
|
currentCoordinates = new ArrayList<>(); |
||||
|
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(); |
||||
|
currentCoordinates.add(coords); |
||||
|
System.out.println("解析到坐标: " + coords); |
||||
|
} |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
// 处理最后一个Placemark
|
||||
|
processCurrentPlacemarkCoordinates(currentPlacemark, currentCoordinates, placemarks); |
||||
|
|
||||
|
// 打印解析结果
|
||||
|
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); |
||||
|
} |
||||
|
|
||||
|
/** |
||||
|
* 将坐标字符串转换为WKT(Well-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(); |
||||
|
} |
||||
|
} |
||||
|
|
||||
|
|
@ -1,11 +0,0 @@ |
|||||
package org.dromara.system.utils; |
|
||||
|
|
||||
|
|
||||
/** |
|
||||
* 解析ovkmz文件 |
|
||||
*/ |
|
||||
public class OvkmzParser { |
|
||||
|
|
||||
|
|
||||
|
|
||||
} |
|
Loading…
Reference in new issue