用XmlWriter手写KML最稳,因其可精准控制命名空间、格式与扩展元素,避免第三方库在GX扩展、缩进、BOM等方面的兼容问题;KMZ须为UTF-8无BOM的root.kml扁平压缩包;坐标顺序为经度,纬度,海拔;角度字段(heading/tilt/roll)必须用十进制度而非弧度;图标等资源需base64内联;GCJ-02坐标须提前转WGS84。

用 XmlWriter 手写 KML 最稳,别碰第三方库
Google Earth 对 KML 的解析其实很宽松,但第三方库(比如 SharpKml)常在命名空间、缩进、空格或 <gx:Tour> 这类扩展元素上出问题,导致文件能打开却无显示,或在移动端直接拒载。手写 KML 反而更可控——你只输出它真正认的那几样:<Placemark>、<Point>、<coordinates>,其他全砍掉。
实操建议:
- 用
XmlWriter.Create()配合XmlWriterSettings { OmitXmlDeclaration = true, Indent = false },避免 BOM 和换行干扰 KMZ 压缩 - 所有坐标必须是
经度,纬度,海拔顺序(不是纬度优先),例如"116.404,39.915,0";海拔可为0或省略,但逗号不能少 -
<name>和<description>内容需用XmlWriter.WriteCData()包裹,否则含<、&会崩解析
KMZ 就是 zip + root.kml,别用 ZipArchive 往里塞文件夹
Google Earth 只认顶层的 root.kml,且必须是 UTF-8 编码、无 BOM。很多人用 ZipArchive 添加 "doc.kml" 或嵌套路径如 "data/doc.kml",结果双击打开提示“无法读取文件”。
实操建议:
- 先生成纯文本
root.kml到内存流(MemoryStream),再用ZipArchive创建新归档 - 调用
archive.CreateEntry("root.kml"),立刻获取ZipArchiveEntry.Open()流,把 KML 字节直接写进去 - 别设
CompressionLevel—— Google Earth 不解压,只是读 zip 中的root.kml,压缩率不影响加载,反而可能因压缩算法不兼容报错
LookAt 和 Camera 的经纬度单位是度,但 heading/tilt 是角度制,别混成弧度
很多 KML 在 Google Earth 里一打开就歪着看天或倒扣,八成是把 heading 当成了 Math.PI / 2 这种弧度值传进去了。KML 规范明确要求:所有角度字段(heading、tilt、roll)单位是十进制度(0–360),不是弧度。
实操建议:
- 如果后端计算用了
Math.Atan2等返回弧度的函数,务必乘以180 / Math.PI转成角度再写入<heading> -
<LookAt>中<longitude>和<latitude>是小数度(如39.915),和<coordinates>格式一致,别转成 DMS 字符串 - 测试时右键点击地标 → “属性” → “视图”,看数值是否在合理范围(
heading: 0–360,tilt: 0–90)
中文路径/图标 URL 在 KMZ 里大概率失效,全用 base64 内联
想在 <IconStyle> 里引用 <href>images/pin.png</href>?KMZ 解压后路径其实是扁平的,images/ 文件夹根本不存在。Google Earth 也不支持相对路径查找,只认根目录下的文件名,或绝对 http URL。
实操建议:
- 图标必须转 base64:用
Convert.ToBase64String(File.ReadAllBytes("pin.png")),然后写成<href>data:image/png;base64,iVBORw...</href> - 字体、HTML 描述里的图片同理,外部路径一律不生效;
<description>中的<img src="..."/>只能是 data URL - 如果图标太大(>100KB),Google Earth 移动端可能渲染卡顿,建议压缩到 32×32 或 48×48,PNG-8 足够
最麻烦的其实是坐标系:KML 默认用 WGS84,如果你的数据来自 GCJ-02(国内火星坐标系),不纠偏的话所有点会偏移 200–500 米——这没法靠代码绕过,得提前转换。










