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