KML与GeoJSON互转工具 - 在线KML/KMZ/GeoJSON格式转换器,支持双向转换
KML ⇄ GeoJSON 互转工具
在线转换 KML/KMZ 与 GeoJSON 格式,支持双向转换和点、线、面要素
使用说明
支持 KML/KMZ 与 GeoJSON 双向转换,自动解析并转换为目标格式,可直接下载使用。
上传 KML/KMZ 文件
点击或拖拽文件到此处上传
支持 .kml 和 .kmz 格式
格式说明
KML(Keyhole Markup Language)是一种基于 XML 的地理数据标记语言,广泛用于 Google Earth 等 GIS 应用。
GeoJSON 是一种基于 JSON 的地理空间数据交换格式,易于解析和使用,是现代 Web 地图应用的标准格式。
支持的要素类型
- Point - 点要素(如地标、兴趣点)
- LineString - 线要素(如路径、轨迹)
- Polygon - 面要素(如区域、边界)
- MultiGeometry - 多重几何要素
转换特性
- 支持 KML/KMZ 与 GeoJSON 双向转换
- 自动解析 KMZ 压缩文件(包含 .kml 的 ZIP 压缩包)
- 保留要素属性信息(名称、描述等)
- 支持嵌套的 Folder 和 Document 结构
- 输出符合标准的 KML 和 GeoJSON 格式
代码实现
JavaScript
/**
* KML ⇄ GeoJSON 双向转换
* 需要安装: npm install jszip @tmcw/togeojson
*/
import JSZip from 'jszip';
import * as toGeoJSON from '@tmcw/togeojson';
/**
* 解析 KML 文件
* @param {string} kmlText - KML 文件内容
* @returns {Object} GeoJSON 对象
*/
function parseKML(kmlText) {
const parser = new DOMParser();
const kmlDom = parser.parseFromString(kmlText, 'text/xml');
return toGeoJSON.kml(kmlDom);
}
/**
* 解析 KMZ 文件
* @param {File} file - KMZ 文件对象
* @returns {Promise<Object>} GeoJSON 对象
*/
async function parseKMZ(file) {
const zip = await JSZip.loadAsync(file);
const kmlFile = Object.keys(zip.files).find(
name => name.endsWith('.kml') || name.endsWith('.wpml')
);
if (!kmlFile) {
throw new Error('KMZ 文件中未找到 KML 文件');
}
const kmlText = await zip.file(kmlFile).async('text');
return parseKML(kmlText);
}
/**
* 转换文件为 GeoJSON
* @param {File} file - KML 或 KMZ 文件
* @returns {Promise<Object>} GeoJSON 对象
*/
async function convertToGeoJSON(file) {
if (file.name.toLowerCase().endsWith('.kmz')) {
return await parseKMZ(file);
} else {
const text = await file.text();
return parseKML(text);
}
}
// 使用示例
const fileInput = document.getElementById('fileInput');
fileInput.addEventListener('change', async (e) => {
const file = e.target.files[0];
try {
const geoJSON = await convertToGeoJSON(file);
console.log('GeoJSON:', geoJSON);
console.log(`要素数量: ${geoJSON.features.length}`);
} catch (error) {
console.error('转换失败:', error);
}
});
/**
* GeoJSON 转 KML
*/
function geoJSONToKML(geojson) {
let kml = '<?xml version="1.0" encoding="UTF-8"?>\n<kml xmlns="http://www.opengis.net/kml/2.2">\n <Document>\n';
geojson.features.forEach(f => {
kml += ' <Placemark>\n';
if (f.properties?.name) kml += ` <name>${f.properties.name}</name>\n`;
const g = f.geometry;
if (g.type === 'Point') kml += ` <Point><coordinates>${g.coordinates[0]},${g.coordinates[1]},0</coordinates></Point>\n`;
else if (g.type === 'LineString') kml += ` <LineString><coordinates>${g.coordinates.map(c => `${c[0]},${c[1]},0`).join(' ')}</coordinates></LineString>\n`;
kml += ' </Placemark>\n';
});
kml += ' </Document>\n</kml>';
return kml;
}Java
import org.w3c.dom.*;
import javax.xml.parsers.*;
import org.json.*;
import java.io.*;
import java.util.zip.*;
/**
* KML 转 GeoJSON 工具类
* 依赖: org.json (Maven: org.json:json:20230227)
*/
public class KMLToGeoJSON {
/**
* 解析 KML 文件
*/
public static JSONObject parseKML(String kmlText) throws Exception {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
DocumentBuilder builder = factory.newDocumentBuilder();
Document doc = builder.parse(new ByteArrayInputStream(kmlText.getBytes()));
JSONObject geoJSON = new JSONObject();
geoJSON.put("type", "FeatureCollection");
geoJSON.put("features", new JSONArray());
NodeList placemarks = doc.getElementsByTagName("Placemark");
for (int i = 0; i < placemarks.getLength(); i++) {
Element placemark = (Element) placemarks.item(i);
JSONObject feature = parsePlacemark(placemark);
if (feature != null) {
geoJSON.getJSONArray("features").put(feature);
}
}
return geoJSON;
}
/**
* 解析 Placemark 元素
*/
private static JSONObject parsePlacemark(Element placemark) {
JSONObject feature = new JSONObject();
feature.put("type", "Feature");
// 解析属性
JSONObject properties = new JSONObject();
String name = getTextContent(placemark, "name");
String desc = getTextContent(placemark, "description");
if (name != null) properties.put("name", name);
if (desc != null) properties.put("description", desc);
feature.put("properties", properties);
// 解析几何
NodeList points = placemark.getElementsByTagName("Point");
if (points.getLength() > 0) {
feature.put("geometry", parsePoint((Element) points.item(0)));
}
NodeList lines = placemark.getElementsByTagName("LineString");
if (lines.getLength() > 0) {
feature.put("geometry", parseLineString((Element) lines.item(0)));
}
return feature;
}
/**
* 解析点要素
*/
private static JSONObject parsePoint(Element point) {
JSONObject geometry = new JSONObject();
geometry.put("type", "Point");
String coords = getTextContent(point, "coordinates");
geometry.put("coordinates", parseCoordinates(coords)[0]);
return geometry;
}
/**
* 解析线要素
*/
private static JSONObject parseLineString(Element line) {
JSONObject geometry = new JSONObject();
geometry.put("type", "LineString");
String coords = getTextContent(line, "coordinates");
geometry.put("coordinates", new JSONArray(parseCoordinates(coords)));
return geometry;
}
/**
* 解析坐标字符串
*/
private static double[][] parseCoordinates(String coordsText) {
String[] points = coordsText.trim().split("\\s+");
double[][] coords = new double[points.length][2];
for (int i = 0; i < points.length; i++) {
String[] parts = points[i].split(",");
coords[i][0] = Double.parseDouble(parts[0]); // 经度
coords[i][1] = Double.parseDouble(parts[1]); // 纬度
}
return coords;
}
/**
* 获取元素文本内容
*/
private static String getTextContent(Element parent, String tagName) {
NodeList nodes = parent.getElementsByTagName(tagName);
return nodes.getLength() > 0 ? nodes.item(0).getTextContent() : null;
}
/**
* 解析 KMZ 文件
*/
public static JSONObject parseKMZ(File kmzFile) throws Exception {
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(kmzFile))) {
ZipEntry entry;
while ((entry = zis.getNextEntry()) != null) {
if (entry.getName().endsWith(".kml")) {
StringBuilder kmlText = new StringBuilder();
byte[] buffer = new byte[1024];
int len;
while ((len = zis.read(buffer)) > 0) {
kmlText.append(new String(buffer, 0, len));
}
return parseKML(kmlText.toString());
}
}
}
throw new Exception("KMZ 文件中未找到 KML 文件");
}
public static void main(String[] args) throws Exception {
// 使用示例
File file = new File("example.kml");
JSONObject geoJSON = parseKML(new String(java.nio.file.Files.readAllBytes(file.toPath())));
System.out.println("GeoJSON: " + geoJSON.toString(2));
}
/**
* GeoJSON 转 KML(简化版)
*/
public static String geoJSONToKML(JSONObject geoJSON) {
StringBuilder kml = new StringBuilder("<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<kml xmlns=\"http://www.opengis.net/kml/2.2\">\n <Document>\n");
JSONArray features = geoJSON.getJSONArray("features");
for (int i = 0; i < features.length(); i++) {
JSONObject feature = features.getJSONObject(i);
kml.append(" <Placemark>\n");
if (feature.getJSONObject("properties").has("name")) {
kml.append(" <name>").append(feature.getJSONObject("properties").getString("name")).append("</name>\n");
}
JSONObject geom = feature.getJSONObject("geometry");
if ("Point".equals(geom.getString("type"))) {
JSONArray coords = geom.getJSONArray("coordinates");
kml.append(" <Point><coordinates>").append(coords.getDouble(0)).append(",").append(coords.getDouble(1)).append(",0</coordinates></Point>\n");
}
kml.append(" </Placemark>\n");
}
kml.append(" </Document>\n</kml>");
return kml.toString();
}
}Python
from xml.etree import ElementTree as ET
import json
import zipfile
from typing import Dict, List, Any
def parse_coordinates(coords_text: str) -> List[List[float]]:
"""解析坐标字符串"""
points = coords_text.strip().split()
coords = []
for point in points:
parts = point.split(',')
coords.append([float(parts[0]), float(parts[1])]) # [经度, 纬度]
return coords
def parse_point(point_elem: ET.Element) -> Dict[str, Any]:
"""解析点要素"""
coords_elem = point_elem.find('.//{http://www.opengis.net/kml/2.2}coordinates')
if coords_elem is None:
coords_elem = point_elem.find('.//coordinates')
coords = parse_coordinates(coords_elem.text)
return {
'type': 'Point',
'coordinates': coords[0]
}
def parse_linestring(line_elem: ET.Element) -> Dict[str, Any]:
"""解析线要素"""
coords_elem = line_elem.find('.//{http://www.opengis.net/kml/2.2}coordinates')
if coords_elem is None:
coords_elem = line_elem.find('.//coordinates')
coords = parse_coordinates(coords_elem.text)
return {
'type': 'LineString',
'coordinates': coords
}
def parse_polygon(polygon_elem: ET.Element) -> Dict[str, Any]:
"""解析面要素"""
outer = polygon_elem.find('.//{http://www.opengis.net/kml/2.2}outerBoundaryIs')
if outer is None:
outer = polygon_elem.find('.//outerBoundaryIs')
coords_elem = outer.find('.//{http://www.opengis.net/kml/2.2}coordinates')
if coords_elem is None:
coords_elem = outer.find('.//coordinates')
coords = parse_coordinates(coords_elem.text)
return {
'type': 'Polygon',
'coordinates': [coords]
}
def parse_placemark(placemark: ET.Element) -> Dict[str, Any]:
"""解析 Placemark 元素"""
feature = {
'type': 'Feature',
'properties': {},
'geometry': None
}
# 解析属性
name = placemark.find('.//{http://www.opengis.net/kml/2.2}name')
if name is None:
name = placemark.find('.//name')
if name is not None and name.text:
feature['properties']['name'] = name.text
desc = placemark.find('.//{http://www.opengis.net/kml/2.2}description')
if desc is None:
desc = placemark.find('.//description')
if desc is not None and desc.text:
feature['properties']['description'] = desc.text
# 解析几何
point = placemark.find('.//{http://www.opengis.net/kml/2.2}Point')
if point is None:
point = placemark.find('.//Point')
if point is not None:
feature['geometry'] = parse_point(point)
return feature
line = placemark.find('.//{http://www.opengis.net/kml/2.2}LineString')
if line is None:
line = placemark.find('.//LineString')
if line is not None:
feature['geometry'] = parse_linestring(line)
return feature
polygon = placemark.find('.//{http://www.opengis.net/kml/2.2}Polygon')
if polygon is None:
polygon = placemark.find('.//Polygon')
if polygon is not None:
feature['geometry'] = parse_polygon(polygon)
return feature
return feature
def parse_kml(kml_text: str) -> Dict[str, Any]:
"""解析 KML 文件"""
root = ET.fromstring(kml_text)
geojson = {
'type': 'FeatureCollection',
'features': []
}
# 查找所有 Placemark
placemarks = root.findall('.//{http://www.opengis.net/kml/2.2}Placemark')
if not placemarks:
placemarks = root.findall('.//Placemark')
for placemark in placemarks:
feature = parse_placemark(placemark)
if feature['geometry'] is not None:
geojson['features'].append(feature)
return geojson
def parse_kmz(kmz_path: str) -> Dict[str, Any]:
"""解析 KMZ 文件"""
with zipfile.ZipFile(kmz_path, 'r') as kmz:
for name in kmz.namelist():
if name.endswith('.kml') or name.endswith('.wpml'):
kml_text = kmz.read(name).decode('utf-8')
return parse_kml(kml_text)
raise Exception('KMZ 文件中未找到 KML 文件')
def convert_to_geojson(file_path: str) -> Dict[str, Any]:
"""转换 KML/KMZ 文件为 GeoJSON"""
if file_path.lower().endswith('.kmz'):
return parse_kmz(file_path)
else:
with open(file_path, 'r', encoding='utf-8') as f:
return parse_kml(f.read())
# 使用示例
if __name__ == '__main__':
geojson = convert_to_geojson('example.kml')
print(json.dumps(geojson, indent=2, ensure_ascii=False))
print(f"要素数量: {len(geojson['features'])}")
def geojson_to_kml(geojson: Dict[str, Any]) -> str:
"""GeoJSON 转 KML(简化版)"""
kml = '<?xml version="1.0" encoding="UTF-8"?>\n<kml xmlns="http://www.opengis.net/kml/2.2">\n <Document>\n'
for feature in geojson['features']:
kml += ' <Placemark>\n'
if 'name' in feature.get('properties', {}):
kml += f" <name>{feature['properties']['name']}</name>\n"
geom = feature['geometry']
if geom['type'] == 'Point':
kml += f" <Point><coordinates>{geom['coordinates'][0]},{geom['coordinates'][1]},0</coordinates></Point>\n"
elif geom['type'] == 'LineString':
coords = ' '.join([f"{c[0]},{c[1]},0" for c in geom['coordinates']])
kml += f" <LineString><coordinates>{coords}</coordinates></LineString>\n"
kml += ' </Placemark>\n'
kml += ' </Document>\n</kml>'
return kml