1. 为什么需要GeoJSON转KML工具?
作为一名长期与地理数据打交道的开发者,我深刻理解不同格式间的转换痛点。GeoJSON和KML作为两种主流地理数据格式,在实际项目中经常需要相互转换。GeoJSON因其轻量和Web友好的特性,成为前端地图应用的宠儿;而KML作为Google Earth的"母语"格式,在企业展示和政府GIS系统中仍占据重要地位。
最近接手的一个乡镇街道数据可视化项目就遇到了典型场景:后端API返回的是标准的GeoJSON数据,而客户要求在Google Earth上展示完整的三维效果。手动转换不仅效率低下,当数据量达到数百个多边形时,简直是一场灾难。这就是为什么我们需要一个可靠的自动化转换工具。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. GeoJSON与KML的核心差异解析
2.1 数据结构对比
GeoJSON采用纯JSON格式,一个简单的点要素看起来像这样:
json复制{
"type": "Feature",
"geometry": {
"type": "Point",
"coordinates": [102.0, 0.5]
},
"properties": {
"name": "示例点"
}
}
而KML则基于XML,同样的数据在KML中表现为:
xml复制<Placemark>
<name>示例点</name>
<Point>
<coordinates>102.0,0.5</coordinates>
</Point>
</Placemark>
2.2 特性支持差异
在实际转换过程中,有几个关键差异需要特别注意:
- 坐标系处理:GeoJSON默认使用WGS84(EPSG:4326),而KML虽然也使用WGS84,但其坐标顺序是经度、纬度、高度(与GeoJSON的纬度、经度相反)
- 样式定义:KML支持丰富的样式(颜色、线宽、图标等),而GeoJSON本身不包含样式信息
- 属性存储:GeoJSON的properties对象可以包含任意JSON数据,KML则需要通过ExtendedData处理复杂属性
3. 手把手实现转换工具
3.1 基础转换原理
转换的核心逻辑其实很简单:
- 解析输入的GeoJSON
- 遍历所有要素(Feature)
- 根据要素类型生成对应的KML结构
- 处理属性数据转换
- 输出KML文档
3.2 使用JavaScript/TypeScript实现
以下是基于TypeScript的核心转换代码:
typescript复制interface GeoJSONFeature {
type: string;
geometry: {
type: string;
coordinates: any;
};
properties?: Record<string, any>;
}
function convertGeoJSONToKML(geojson: GeoJSONFeature[]): string {
let kml = `<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>`;
geojson.forEach((feature, index) => {
kml += `
<Placemark>
<name>Feature ${index}</name>`;
if(feature.properties) {
kml += `
<ExtendedData>`;
Object.entries(feature.properties).forEach(([key, value]) => {
kml += `
<Data name="${key}">
<value>${value}</value>
</Data>`;
});
kml += `
</ExtendedData>`;
}
kml += `
${convertGeometry(feature.geometry)}
</Placemark>`;
});
kml += `
</Document>
</kml>`;
return kml;
}
function convertGeometry(geometry: any): string {
switch(geometry.type) {
case 'Point':
return `<Point><coordinates>${geometry.coordinates.join(',')}</coordinates></Point>`;
// 其他几何类型处理...
}
}
3.3 处理复杂几何类型
实际项目中会遇到各种复杂几何类型,需要特殊处理:
- 多边形(Polygon):GeoJSON允许带孔洞的多边形,KML则需要用
表示 - 多几何体(MultiGeometry):需要拆分为多个KML几何元素
- 3D坐标:注意高度值的处理顺序
4. 高级功能实现
4.1 样式自定义
虽然GeoJSON本身不支持样式,但我们可以通过约定properties中的特定字段来传递样式信息:
typescript复制// 在properties中添加样式信息
const styledFeature = {
...feature,
properties: {
...feature.properties,
_kmlStyle: {
lineColor: 'ff0000ff', // ABGR格式
lineWidth: 2,
fillColor: '80ff0000'
}
}
};
// 转换时处理样式
if(feature.properties?._kmlStyle) {
const style = feature.properties._kmlStyle;
kml += `
<Style>
<LineStyle>
<color>${style.lineColor}</color>
<width>${style.lineWidth}</width>
</LineStyle>
<PolyStyle>
<color>${style.fillColor}</color>
</PolyStyle>
</Style>`;
}
4.2 批量转换优化
处理大型GeoJSON文件时,内存管理很关键。我们可以使用流式处理:
typescript复制import { createReadStream, createWriteStream } from 'fs';
import { parse } from 'JSONStream';
function convertLargeGeoJSON(inputPath: string, outputPath: string) {
const readStream = createReadStream(inputPath);
const writeStream = createWriteStream(outputPath);
writeStream.write(`<?xml version="1.0" encoding="UTF-8"?>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>`);
readStream
.pipe(parse('features.*'))
.on('data', (feature) => {
writeStream.write(convertFeature(feature));
})
.on('end', () => {
writeStream.write(`
</Document>
</kml>`);
writeStream.end();
});
}
5. 实战中的坑与解决方案
5.1 坐标系翻转问题
最常见的坑就是坐标顺序。GeoJSON使用[纬度, 经度],而KML需要[经度, 纬度]。我曾因此导致一批数据在Google Earth上显示到了完全错误的位置。
解决方案:
typescript复制function flipCoordinates(coords: number[]): string {
return `${coords[1]},${coords[0]}${coords[2] ? ',' + coords[2] : ''}`;
}
5.2 属性值转义
XML对特殊字符(<, >, &等)有严格限制,必须进行转义:
typescript复制function escapeXML(str: string): string {
return str.replace(/[<>&'"]/g, (char) => {
switch(char) {
case '<': return '<';
case '>': return '>';
case '&': return '&';
case '\'': return ''';
case '"': return '"';
default: return char;
}
});
}
5.3 性能优化
处理包含数万个要素的GeoJSON时,纯字符串拼接会消耗大量内存。可以采用以下优化:
- 使用StringBuilder模式
- 对大文件采用流式处理
- 对几何坐标进行精度控制(减少小数点位数)
6. 完整工具链搭建
6.1 命令行工具实现
通过commander.js创建易用的CLI工具:
typescript复制import { program } from 'commander';
program
.version('1.0.0')
.description('GeoJSON to KML转换工具')
.requiredOption('-i, --input <path>', '输入GeoJSON文件路径')
.option('-o, --output <path>', '输出KML文件路径')
.option('-s, --style <json>', '样式配置JSON')
.action((options) => {
// 转换逻辑
});
program.parse(process.argv);
6.2 网页版工具
使用Express搭建简单Web服务:
typescript复制import express from 'express';
import multer from 'multer';
const app = express();
const upload = multer();
app.post('/convert', upload.single('geojson'), (req, res) => {
const geojson = JSON.parse(req.file.buffer.toString());
const kml = convertGeoJSONToKML(geojson);
res.set('Content-Type', 'application/vnd.google-earth.kml+xml');
res.set('Content-Disposition', 'attachment; filename="converted.kml"');
res.send(kml);
});
app.listen(3000, () => {
console.log('服务已启动: http://localhost:3000');
});
6.3 打包发布
使用Rollup打包为浏览器可用版本:
javascript复制import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { terser } from 'rollup-plugin-terser';
export default {
input: 'src/main.js',
output: {
file: 'dist/geojson-to-kml.min.js',
format: 'umd',
name: 'GeoJSONToKML'
},
plugins: [
resolve(),
commonjs(),
terser()
]
};
7. 扩展应用场景
7.1 CAD到KML的转换流程
虽然本文主要讨论GeoJSON,但实际工作中常遇到CAD数据需要转换为KML的情况。典型流程为:
- 使用QGIS或ArcGIS将CAD转换为GeoJSON
- 应用我们的工具将GeoJSON转为KML
- 在Google Earth中验证效果
7.2 与GIS系统集成
在企业GIS系统中,可以部署此工具作为服务,实现:
- 动态生成KML供移动端使用
- 定时批量转换数据仓库中的GeoJSON
- 与其他系统(如Minecraft地图生成器)集成
7.3 乡镇街道数据可视化
针对热词中提到的"乡镇街道geojson"需求,我们可以:
- 从政府开放数据平台获取基础GeoJSON
- 添加行政区划样式
- 转换为KML后叠加卫星影像
- 生成三维行政边界效果
