☰
SHP转KML含名称标注全攻略:GDAL/QGIS/Python实操与避坑
2026/10/9 19:16:38 网站建设 项目流程

简介:面向GIS数据转换与制图标注需求,FME工具包可实现SHP数据批量转为KML,并在转换过程中保留要素名称标注,解决跨平台数据共享和地图可视化时的格式适配问题。包体非常轻量,压缩包仅125KB,共11个文件,核心为FME工程模板(.fme/.fmw),支持直接打开运行;附带对应的shapefile配套文件(.shp、.dbf、.prj、.sbn、.sbx、.shx等)用于模板测试与验证,另有输出日志和字段属性说明图,帮助使用者快速理解输入数据的必填字段和转换流程。模板数据内置常用属性结构,可迁移应用到项目竣工图、地理要素批量出图等场景。目前已有2022人学习下载,适合GIS数据处理人员、测绘工程师及FME应用初学者快速获得可复用的转换方案。

1. shp 数据转 kml 文件:为什么单独强调“含名称标注”

手里有一份 shp 数据,要转成 KML 文件给外业人员在手机上查看,还要求每个地块都带名称标注——这是野外核查、规划公示、林业测量里最常见的需求。许多人第一次做这件事,以为“转格式”就是把后缀改成 kml,或者用某个在线小工具上传一下就完事。结果很统一:图形全在,名字全丢,所有要素在地图客户端里显示成“未命名要素”。名称标注不是转格式自带的功能,而是转换参数的一部分。这篇笔记按三条路线展开:GDAL 命令行一行转、QGIS 图形界面导出、Python 脚本批量控制,最后集中讲坐标系、中文编码、空名称这三类高频坑。适合想把转换彻底搞定的数据处理从业者,也适合被“名称丢一半”折磨过的老手对照排查。

2. 用 ogr2ogr 转 KML:一条命令把名称字段写进 Placemark

2.1 为什么优先选 GDAL 命令行

GDAL/OGR 是桌面 GIS 软件背后的矢量读写引擎,ogr2ogr 是它最常用的格式转换命令。推荐它作为第一套方案,原因只有三个:不挑系统、不依赖图形界面、参数可复现。你在 A 机器上跑通的命令,拿到 B 机器上结果一致,这是批处理和自动化脚本的基础。

安装方式常见有两种:如果你已经有 QGIS 环境,直接用自带的 OSGeo4W Shell;如果你喜欢干净环境,用 conda 创建独立环境安装 gdal 即可。环境配置好后,先确认版本:

ogr2ogr --version

如果你的环境里有多个 Python 或旧版 GDAL,建议把版本信息打印出来看一眼。GDAL 3.x 对编码检测和 SQL 方言支持都比 2.x 好很多,很多中文乱码问题在新版里已经默认处理了。

2.2 最简命令:不带名称 vs 带名称

先说一个最容易翻车的默认行为。直接执行:

ogr2ogr -f KML output.kml input.shp

这条命令能成功生成 KML,但打开后你会发现所有要素的 name 是 FID 一类的编号,或者干脆没有名称。原因在于 OGR 的 KML 驱动生成<Placemark>时,name 标签默认取内部 FID,不会聪明到自动挑一个属性字段当名称。

要指定名称字段,关键参数是-dsco NameField:

ogr2ogr -f KML -dsco NameField=NAME output.kml input.shp

这里-dsco是数据集创建选项,NameField 告诉 KML 驱动:读取目标属性字段时,把该字段的值写入 KML 每个 Placemark 的<name>节点。NAME 必须是源数据里真实存在的字段名,大小写一般无所谓,但字段名里如果有空格或特殊字符,建议确认一下实际名称。

如果你不确定字段名,先执行:

ogrinfo -al -so input.shp

-al表示读取全部图层,-so表示只输出概要信息。返回结果里会列出所有属性字段,挑一个最能表达“这个要素是什么”的字段做 NameField。常见的选择是地类名、名称、编号字段,具体叫什么取决于数据,别凭印象猜字段名。

2.3 参数组合:坐标系、字段拼接、多字段一次搞定

shp 转 KML 最常见的组合拳是“坐标系修正 + 名称字段指定”。KML 标准要求坐标是 WGS84 经纬度,也就是 EPSG:4326。如果你的 shp 是投影坐标系,直接转出来的 KML 位置会偏移,有些极端情况下甚至跑到海里。所以安全写法是显式加-t_srs:

ogr2ogr -f KML -t_srs EPSG:4326 -dsco NameField=NAME output.kml input.shp

-t_srs是输出坐标系目标,源数据的坐标系信息来自它自带的 .prj 文件。没有 .prj 文件的数据是“裸奔”状态,OGR 只能假设坐标系,这时转换结果不可信,建议先补上正确的投影文件。

遇到名称需要拼场景,比如“地类 + 编号”组合成标注,用 SQL 方言最省事:

ogr2ogr -f KML -dialect sqlite \ -sql "SELECT *, NAME || '-' || ID AS NAME FROM input" \ output.kml input.shp

这段命令把 NAME 字段和 ID 字段用短横线拼起来,生成新的 NAME 字段。-dialect sqlite启用 SQLite 方言,支持字符串拼接函数;字段名大小写敏感,shp 的字段名会被截断为 10 个字符,写 SQL 时注意用截断后的名字。如果你只是想把某个已有字段当名称,不需要走 SQL,直接在 NameField 里指定即可。

提示:把带中文的字段当 NameField 时,如果源数据没有 .cpg 文件,读出的中文字段名可能乱码。先给 shp 补一个 UTF-8 编码的 .cpg 文件,或者用后面的 Python 方案处理,能少踩很多坑。

3. QGIS 图形界面转换 KML:Name 字段下拉框的正确用法

3.1 图形化方案适合什么场景

命令行虽然可靠,但并不是所有人都习惯黑底白字的终端。QGIS 的优势是所见即所得:能直接看到图层要素、核对字段值、确认坐标范围。对单次转换、数据量不大、需要边操作边确认的场景,用 QGIS 效率更高。很多做规划或外业数据整理的朋友,日常电脑里只装了 QGIS,那这套方案就是他们的最佳入口。

QGIS 转 KML 有两种常见路径:右键图层导出,或者用 Processing 工具箱里的转换算法。两者底层都走 OGR,但参数暴露程度不同。

3.2 另存为对话框里的 Name 下拉框

最简单的方式是在图层列表里右键图层,选择“导出”并选择“另存为”。在对话框里,格式选择 Keyhole Markup Language [KML],文件名指定好,CRS 选择 WGS84。这里有个容易被忽略的细节:在“图层选项”或字段映射区域,QGIS 会尝试自动匹配一个字段作为 KML 的 name,但这个自动匹配不总是可靠。

更稳妥的做法是使用 Processing 工具箱里的“转换成格式”算法。这个工具把 OGR 的参数暴露成可视化选项,其中就有 NameField 参数。操作步骤如下:

  1. 打开 Processing 工具箱,搜索“转换成格式”。
  2. 输入图层选择你的 shp。
  3. 格式选择 KML。
  4. NameField 下拉框里选择要作为名称的字段。
  5. 文件路径填输出位置,点击运行。

这个工具相当于把第 2 章的命令行封装成了图形界面,NAME_FIELD 对应的就是 OG 的 NameField。它的好处是下拉框直接列出所有属性字段,不存在“字段名写错”的问题。

3.3 没有合适名称字段时的处理办法

很多从规划口拿来的 shp,属性表里字段名是“BSM”“DZM”“MJDW”这类编码,根本不是人能看懂的。这种数据直接转 KML,就算选了名称字段,标注出来也是一串代码,没有实际意义。我一般会先在 QGIS 里用字段计算器生成一个可读的名称字段。

打开属性表,新建字段,类型选“字符串”,表达式写成:

"地类名称" || '-' || "地块编号"

这个表达式会把地类名称和地块编号拼起来,生成类似“乔木林地 - P-012”的标注文字。用||拼接时注意两边的字段都用双引号,字符串用单引号。如果你只需要某个字段本身,表达式更简单:

"地类名称"

生成新字段后,再去走“转换成格式”流程,在 NameField 里选这个新字段。临时字段用完可以删,不影响原始数据。这里有个习惯建议:转换前先选中你的名称字段,确认字段值没有空值或 NULL。否则 KML 导出来之后,某个要素的名字是空的,在地图客户端里的表现就是“点击没反映,标注显示空白”,排查起来比你想的麻烦。

4. 自己写 Python 脚本转换:名称、样式、批量一次控制

4.1 脚本方案解决什么问题

命令行和 QGIS 能覆盖大部分单次需求,但有两个场景它们不是最优解:一是批量处理几十个 shp 文件,每个文件的名称字段还不一样;二是要给 KML 里的要素做分类着色、悬浮描述、图标定制。这时候再用命令行拼参数会把人逼疯,Python 脚本的灵活性就体现出来了。

常见组合是 pyshp 读 shp 文件,simplekml 生成 KML。pyshp 轻量,只做几何和字段读取;simplekml 把 KML 的细节封装成 Python 对象,写名称、描述、样式都很直观。这个方案不依赖桌面 GIS 环境,单个脚本可以直接挂到数据流程里跑。

4.2 最小可运行脚本:点线面通用转换

下面这个脚本读入一个 shp,输出带名称标注的 KML,支持点、线、面三种常见几何类型:

import shapefile import simplekml # 打开 shp,如果源文件是 GBK 编码,需要指定 encoding='gbk' reader = shapefile.Reader("input.shp", encoding="utf-8") fields = [f[0] for f in reader.fields[1:]] # 跳过 DeletionFlag 字段 kml = simplekml.Kml(name="转换结果") # 名称字段从这里改,改成你的实际字段名 name_column = "NAME" for sr in reader.shapeRecords(): # 把记录转成字典,方便按键名取值 record = dict(zip(fields, sr.record)) name = str(record.get(name_column, "未命名")).strip() if not name: name = "未命名" shape = sr.shape if shape.shapeType == 1: # 点要素 lon, lat = shape.points[0] pnt = kml.new_point(name=name, coords=[(lon, lat)]) elif shape.shapeType == 3: # 线要素 line_coords = [(pt[0], pt[1]) for pt in shape.points] kml.new_linestring(name=name, coords=line_coords) elif shape.shapeType == 5: # 简单面要素,单外环 ring_coords = [(pt[0], pt[1]) for pt in shape.points] kml.new_polygon(name=name, outerboundaryais=ring_coords) kml.save("output.kml") print("转换完成,共处理", len(reader.shapeRecords()), "个要素")

逻辑说明:脚本分四步走。第一步读取 shp 并取出字段列表,这里用reader.fields[1:]是因为 pyshp 返回的字段列表第一项是删除标记,不是真实属性。第二步遍历每个要素,用zip(fields, record)把属性记录映射成字典,这样取名称字段时不会因为字段顺序写错而翻车。第三步按shapeType分支处理不同几何类型,1 是点、3 是线、5 是面,坐标都用(经度, 纬度)的元组传给 simplekml。最后保存 KML。

参数说明:encoding="utf-8"必须和源 dbf 的实际编码一致。如果你的 shp 是 GBK 编码,不改这里导出的中文名称会全部乱码。name_column变量是唯一需要手工改的地方,它决定哪个字段写入 KML 的<name>标签。strip()处理掉字段值两边的空格,防止名称带着缩进。

这个脚本默认数据是 WGS84 经纬度坐标系。如果源 shp 是投影坐标系,先用第 2 章的 ogr2ogr 命令转一次坐标系,再喂给脚本。这种情况我一般建议直接先用命令行做坐标转换,不要在脚本里再引入坐标转换库,拆成两步反而更容易排查问题。

4.3 给 KML 加分类着色和描述信息

KML 的 name 只是标注显示的文本,真正让要素“活起来”的是描述信息和样式。比如外业核查时,点击一个地块,希望弹出这个地块的面积、权属、核查人。下面这段在最小脚本上扩展,加入 description 和分类颜色:

# 定义分类颜色,key 为属性值,value 为 RGB 元组 color_map = { "林地": (34, 139, 34), "草地": (154, 205, 50), "水域": (70, 130, 180), } for sr in reader.shapeRecords(): record = dict(zip(fields, sr.record)) name = str(record.get(name_column, "未命名")).strip() or "未命名" class_value = str(record.get("地类", "")).strip() # 取颜色,没匹配到的用灰色兜底 rgb = color_map.get(class_value, (169, 169, 169)) style = simplekml.Style() style.polystyle.color = simplekml.Color.rgb(rgb[0], rgb[1], rgb[2]) shape = sr.shape if shape.shapeType == 5: ring_coords = [(pt[0], pt[1]) for pt in shape.points] poly = kml.new_polygon(name=name, outerboundaryais=ring_coords) poly.style = style # 构造 description 内容,点击要素时显示 poly.description = ( f"名称: {name}\n" f"地类: {class_value}\n" f"面积: {record.get('面积', '')}" ) kml.save("output_style.kml")

这段的关键在simplekml.Style()和Color.rgb()。KML 的颜色编码和常规理解相反,不是#RRGGBB而是AABBGGRR,如果手写十六进制很容易把红蓝写反。用simplekml.Color.rgb()封装后就避免了这个问题,直接传 RGB 值即可。polystyle.color只对面要素有效,点要素要用iconstyle,线要素用linestyle,不要张冠李戴。

description 字段最终会写进 KML 的<description>标签,多数地图客户端支持在气泡里显示换行文本。值得提醒的是:description 里不要放敏感字段。KML 文件会被复制、转发,不要因为顺手就把手机号、身份证号写进去。

提示:多部件面要素或带内环(岛洞)的面,上面的脚本只保证外环正确。遇到复杂面数据,建议先用 QGIS 查一下要素是否有多部件,或者改用 OGR 的-explodecollections选项拆散后再进脚本。

5. KML 转换常见避坑:坐标系、乱码、空名称与超大文件

5.1 图形跑到海里的“玄学”

现象:在 QGIS 里图层叠加正常,导出 KML 后放到手机地图客户端,整个图层偏移到海洋里,或者距离真实位置几公里远。

原因:shp 是投影坐标系,比如常用的 CGCS2000 3-degree GK Zone,KML 却要求 WGS84 经纬度。没有做坐标系转换就输出,几何坐标被当成经纬度直接写进 KML,位置自然错得离谱。这不是 QGIS 或 GDAL 的 bug,是坐标参考系没对齐的经典错误。

解决:转换时显式指定输出坐标系。命令行的写法是-t_srs EPSG:4326;QGIS 另存为时在 CRS 下拉框里选 WGS84。有个细节,如果你的 shp 没有 .prj 文件,QGIS 里会弹出“未知坐标参考系”的选择框,这时候你选“WGS84”只是告诉软件一个猜测值,不解决真实坐标系问题,需要先联系数据提供方拿到正确的坐标系信息。

5.2 中文名称全部变成问号或乱码

现象:shp 属性表里字段值是正常中文,converted KML 后名称变成一串问号,或者像“鏉ㄦ灄”这种根本无法阅读的字符。

原因:dbf 属性文件是 GBK/GB2312 编码,但读取时没有正确解码。GDAL 3.x 会尝试通过 .cpg 文件判断编码,可很多数据没有 .cpg,软件猜错编码后就把中文搞乱了。Python 脚本里如果用了默认编码读取 GBK 文件,也会出同样问题。

解决:如果 shp 有 .cpg 文件,先用文本编辑器打开确认内容是UTF-8还是GBK。没有 .cpg 的情况下,命令行可以先强制转一层编码:

ogr2ogr -f "ESRI Shapefile" -lco ENCODING=UTF-8 temp_utf8.shp input.shp ogr2ogr -f KML -dsco NameField=NAME output.kml temp_utf8.shp

第一条命令把整个 shp 重新拷贝一份,同时把属性编码转为 UTF-8 并写入 .cpg,第二条命令再做 KML 转换。Python 脚本里的处理更直接,读文件时指定编码即可:shapefile.Reader("input.shp", encoding="gbk")。判断编码的方法是:用文本编辑器打开 dbf 文件(二进制方式),如果中文显示为乱码但字段结构清晰,多半是 GBK;如果打开属性表中文正常,那就是读取环境已经帮你处理了编码。

5.3 NameField 指定了但名称依然为空

现象:命令行带了-dsco NameField=NAME,KML 也生成了,但打开发现每个要素的 name 是空的。

原因:第一种可能是字段名对不上,你以为叫 NAME,实际叫 Name 或 NAME1( shp 字段名超过 10 字符会被截断)。第二种可能是该字段在部分要素上是空值,OGR 不会为空的字段生成<name>标签,而是保留一个空节点。

解决:先执行ogrinfo -al -so input.shp,把字段列表完整看一遍,复制真实的字段名。如果你是 SQL 拼接生成的新字段,转完用第 2 章的ogrinfo -al -so output.kml检查输出。注意 KML 作为 OGR 数据源读取时,name 会作为属性字段出现在列表里。检查到空名称的处理方法是:在 SQL 里给字段加一个兜底值:

ogr2ogr -f KML -dialect sqlite \ -sql "SELECT *, CASE WHEN NAME IS NULL THEN '未命名' ELSE NAME END AS NAME FROM input" \ output.kml input.shp

5.4 ArcGIS 转 KML 标注丢一半

现象:用 ArcGIS 的“图层转 KML”工具,属性表里明明有名称字段,转出来的 KML 在客户端里却看不到文字标注。

原因:这个工具认的是图层的标注表达式,不是属性字段。如果你的图层没有设置标注,或者标注表达式对应的是空字段,转换时它不知道拿什么作为名称。把工具当黑匣子用,就会在这类场景上浪费时间。

解决:转换前先在图层的“属性”里设置标注表达式,指定你要的字段,比如[地类名称],确认标注能在视图中显示出来,再用“图层转 KML”工具。这样生成的 KML 才会把标注内容写进<name>。如果你只是想把属性字段完整带过去,记得在转换工具的属性字段列表里勾选需要的字段。

5.5 KML 文件太大,手机打开卡死

现象:几十 MB 的 KML 放进手机,地图客户端直接卡顿,或者加载到一半停住。

原因:shp 转 KML 后每个要素的每个顶点都会被展开写成 XML 标签,面要素的密度越高文件越大。另外如果属性表有大量用不到的字段,这些属性会全部进 KML。手机处理 XML 本身就慢,文件体积一大更明显。

解决:转换前做几何简化是常规做法:

ogr2ogr -f KML -simplify 0.0001 -dsco NameField=NAME output.kml input.shp

-simplify的参数是简化容差,单位与输出坐标系一致。这里 0.0001 表示约 11 米,适用于一般面状要素。精度要求高的场景容差要调小,比如 0.00001。另外推荐用 SQL 只保留必要字段:

ogr2ogr -f KML -dialect sqlite \ -sql "SELECT NAME, 地类 FROM input" \ output.kml input.shp

不需要的字段直接不进入输出,文件体积能明显下降。顶部和几何精度之间要找个平衡,数据密度大时优先保证图形完整,不要一刀切地简化到看不清边界。

6. 进阶验证技巧:如何保证每个 KML 要素都带名称

转换完成后,很多人的习惯是直接用地图客户端打开看一眼。肉眼看没问题,但你知道哪个要素的名字是空标注吗?在小比例尺下,空名称和正常名称的显示差异很小。我现在的习惯是:所有 KML 转换完,先用命令行做一次字段验证,再发给使用方。

最简单的验证是读回 KML 的概要:

ogrinfo -al -so output.kml

这条命令会列出 KML 中 name字段、description 字段和要素数量。如果你的名称字段没写进去,这里根本不会出现 name 属性。但 ogrinfo 只能说明“有 name 字段”,不能说明“每个要素的 name 都有值”。要做逐要素检查,用一段短 Python 脚本更直观:

import re with open("output.kml", encoding="utf-8") as f: xml = f.read() # KML 文档本身也有一个 name,去掉第一个 Document 的 name names = re.findall(r"<name>(.*?)</name>", xml) data_names = names[1:] # 去掉文档名称 empty_count = sum(1 for n in data_names if not n.strip()) print("要素名称总数:", len(data_names)) print("空名称数量:", empty_count)

正则匹配虽然不是 XML 解析的正规方式,但对于验证 KML 这种结构稳定的格式足够。names[1:]去掉的是 Document 名称,如果你的 KML 里还有其他辅助名称节点,自己做一次抽查就能确定偏移量。这个脚本是“检查”,不是“修复”,发现空名称后回到第 4 章的 SQL 兜底方案重新转。

进阶玩法是把转换流程写成一个批处理脚本,让所有 shp 走同一套“坐标系修正 + 名称字段映射 + 输出验证”流程。命令行方案适合服务器环境,Python 方案适合和数据清洗流程合并。有人用这套路把 shp 转换成 KML 后,又用 KML 作为中转格式去做数据发布,还有人顺手把同一份数据用其他工具导出成文本格式做属性核对。KML 的优势始终是可视化展示和移动端分发,它不是数据存储的终极格式,别指望用它替代 shp 或地理数据库。

最后说个我自己的习惯:凡是发出去的 KML,我都会先用地图客户端把要素点一遍,随机核对五六个名称,再用命令行验证字段完整性。这一套下来虽然多了几分钟,但几乎没再遇到过“用户收到文件发现标注是空白”的返工。希望帮到你。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询