
简介这份资源是面向GIS从业者与地理信息相关专业师生的KML/KMZ转SHP自定义工具箱基于ArcGIS平台的Python语言开发用于解决Google Earth数字化矢量数据无法直接在GIS平台使用的问题。压缩包共5个文件约158KB包含两个tbx工具箱文件、一份Python源码脚本、一份安装说明PDF以及一份操作演示PPT分别对应工具部署、源码学习与使用引导体积轻巧便于分发。目前已有2717人学习下载说明其在格式转换场景中具有较高的实用认可度。使用者只需将工具箱添加至ArcGIS自定义工具箱双击即可完成KML到SHP的批量转换无需从零编写脚本同时附带的Python源码与安装指南也便于非专业读者理解转换逻辑、排查运行环境问题适合作为GIS数据处理入门与日常作业的辅助工具。1. kml 转 shp 这件事卡住你的往往不是格式本身手头拿到一个 kml 文件可能是从地图软件里导出的路线、可能是别人发来的点位集合也可能是某个项目交付的边界数据。你想把它转成 shp因为后续要在 ArcGIS 里做空间分析、要跟其他图层叠加、要算面积和长度。结果打开工具一看要么转换后属性表全是乱码要么坐标系对不上要么线要素断成一截一截。kml 和 shp 看起来都是地理数据文件但它们的底层逻辑完全不同——kml 是基于 XML 的文本格式天生为展示服务shp 是二进制格式为存储和分析服务。这个差异决定了转换不是改个后缀那么简单。这篇内容面向需要把 kml 批量转成 shp 的 GIS 从业者从工具选型讲到参数设置再到转换后校验把每一步能踩的坑都摊开说清楚。2. 先搞清楚 kml 和 shp 到底差在哪为什么不能直接改后缀2.1 两种格式的底层结构差异kml 的全称是 Keyhole Markup Language本质是一个 XML 文档。它用标签描述地理要素比如Placemark表示一个地标LineString表示一条线Polygon表示一个面。坐标直接写在coordinates标签里经度在前、纬度在后用逗号分隔。这种结构的好处是可读性强用文本编辑器就能打开看坏处是解析效率低数据量一大就卡。shp 是 Esri 在 1990 年代定义的二进制格式一个完整的 shp 数据集实际上是一组文件.shp存几何、.shx存索引、.dbf存属性、.prj存坐标系。几何坐标以双精度浮点数存储读写速度快但你必须用专门的库或软件才能打开。shp 对字段名有长度限制最多 10 个字符对字段类型也有约束不支持直接存储嵌套结构。这就解释了为什么直接改后缀行不通kml 里的层级结构、样式信息、嵌套的 MultiGeometry在 shp 里没有对应的存储位置。转换工具要做的是解析 XML、提取几何和属性、再按 shp 的规范重新写入。2.2 转换时到底丢了什么、留了什么我一般会先跟需求方确认三件事几何类型是什么、属性字段要保留哪些、坐标系是什么。这三件事决定了转换方案。几何类型方面kml 支持 Point、LineString、Polygon、MultiGeometry 以及它们的组合。shp 虽然也支持这些类型但一个 shp 文件只能存一种几何类型。如果你的 kml 里既有线又有点转换后要么分成两个 shp要么把点也转成线这通常没意义。属性字段方面kml 的ExtendedData或Description里可能塞了很多信息但 shp 的 dbf 字段名不能超过 10 个字符中文名更是容易出问题。常见做法是转换前先把字段名改成英文短名转换后再在 GIS 软件里改显示别名。坐标系方面kml 规范要求使用 WGS84 经纬度坐标EPSG:4326但 shp 可以用任意投影坐标系。如果你后续要做面积量算必须把 WGS84 转成投影坐标系比如 UTM 或高斯克吕格否则算出来的面积单位是平方度没有意义。注意kml 的坐标顺序是经度,纬度,高程而有些工具在读取时可能按纬度,经度解析转换后位置会跑到南极洲附近。转换完第一件事就是加载到地图里看一眼位置对不对。3. 用 Python 把 kml 批量转 shp从环境配置到跑通第一条命令3.1 工具选型为什么我最终选了 GDAL/OGR能转 kml 到 shp 的工具很多ArcGIS 的转换工具箱、QGIS 的另存为、在线转换网站、GDAL 的 ogr2ogr 命令行。我选 GDAL 的理由很直接免费、跨平台、能批量、能写脚本、能嵌到自动化流程里。ArcGIS 当然也能做但你要买 license而且批量处理时那个转换工具的速度实在让人着急。在线转换网站适合偶尔转一两个文件但涉及敏感数据时你不会想传到别人服务器上。GDAL 的 Python 绑定是 osgeo安装方式取决于你的系统。Windows 上最省事的是用 condaconda install -c conda-forge gdalLinux 上可以用 aptsudo apt-get install gdal-bin python3-gdal装完之后验证一下from osgeo import ogr, osr print(ogr.__version__)如果输出了版本号说明环境没问题。这里有个血泪经验不要混用 pip 和 conda 安装的 GDAL版本冲突会让你怀疑人生。要么全用 conda要么全用 pip 加预编译 wheel。3.2 核心转换脚本逐行拆解参数含义下面这个脚本是我用了很多次的模板能处理大多数 kml 转 shp 的场景from osgeo import ogr, osr import os def kml_to_shp(kml_path, shp_path, geom_typeogr.wkbUnknown): kml_path: 输入 kml 文件路径 shp_path: 输出 shp 文件路径不带扩展名 geom_type: 指定几何类型默认自动检测 # 打开 kml 数据源 driver ogr.GetDriverByName(KML) ds driver.Open(kml_path, 0) # 0 表示只读 if ds is None: raise FileNotFoundError(f无法打开 {kml_path}) # 获取第一个图层kml 通常只有一个图层 src_layer ds.GetLayer(0) # 创建输出 shp 数据源 shp_driver ogr.GetDriverByName(ESRI Shapefile) if os.path.exists(shp_path): shp_driver.DeleteDataSource(shp_path) out_ds shp_driver.CreateDataSource(shp_path) # 定义坐标系kml 固定为 WGS84 srs osr.SpatialReference() srs.ImportFromEPSG(4326) # 创建输出图层 out_layer out_ds.CreateLayer( os.path.basename(shp_path), srs, geom_typegeom_type ) # 复制字段定义注意字段名长度限制 src_defn src_layer.GetLayerDefn() for i in range(src_defn.GetFieldCount()): field_defn src_defn.GetFieldDefn(i) name field_defn.GetName()[:10] # 截断到 10 字符 out_layer.CreateField(ogr.FieldDefn(name, field_defn.GetType())) # 逐要素复制 out_layer.StartTransaction() for feature in src_layer: geom feature.GetGeometryRef() if geom is None: continue out_feature ogr.Feature(out_layer.GetLayerDefn()) out_feature.SetGeometry(geom.Clone()) # 复制属性值 for i in range(src_defn.GetFieldCount()): name src_defn.GetFieldDefn(i).GetName()[:10] out_feature.SetField(name, feature.GetField(i)) out_layer.CreateFeature(out_feature) out_feature None out_layer.CommitTransaction() ds None out_ds None print(f转换完成{shp_path}.shp) # 调用示例 kml_to_shp(route.kml, route_output)这段代码的逻辑链条是打开 kml → 读取图层 → 创建 shp → 复制坐标系 → 复制字段 → 逐要素写入。几个关键参数需要展开说。driver.Open(kml_path, 0)里的 0 表示只读模式改成 1 就是可写。转换场景下只读就够了避免误改源文件。geom_typeogr.wkbUnknown让 OGR 自动检测几何类型。但如果你的 kml 里混合了线和面自动检测可能只取第一种遇到的类型后面的要素会被丢弃。稳妥做法是先跑一遍看看数据里有什么再显式指定类型。field_defn.GetName()[:10]这行是处理 shp 字段名长度限制的。如果你的 kml 属性字段名超过 10 个字符不截断的话创建字段时会报错。截断后可能重名需要额外加去重逻辑。out_layer.StartTransaction()和CommitTransaction()是批量写入的性能优化。不加事务的话每个要素写入都会触发一次磁盘 IO几千个要素就能让你等到怀疑人生。3.3 批量处理多个 kml 文件的目录遍历写法单个文件转换跑通后批量处理就是加一层目录遍历import glob def batch_convert(input_dir, output_dir): os.makedirs(output_dir, exist_okTrue) kml_files glob.glob(os.path.join(input_dir, *.kml)) for kml_file in kml_files: base_name os.path.splitext(os.path.basename(kml_file))[0] shp_path os.path.join(output_dir, base_name) try: kml_to_shp(kml_file, shp_path) except Exception as e: print(f处理 {kml_file} 时出错{e}) batch_convert(./kml_data, ./shp_output)这里用 try-except 包住单个文件的转换是为了避免一个文件出错导致整个批次中断。实际跑的时候建议把错误信息写到日志文件里方便回头排查。提示如果 kml 文件很大超过 100MB建议先用 ogr2ogr 命令行做一次预处理把 kml 转成 GeoJSON 或 GPKG再用 Python 处理。OGR 的 KML 驱动在解析超大文件时内存占用比较高。4. 转换参数怎么调坐标系、字段映射和几何类型的实操建议4.1 坐标系转换从 WGS84 到投影坐标系的正确姿势kml 固定用 WGS84 地理坐标系EPSG:4326但 shp 用于分析时通常需要投影坐标系。如果你只是做展示保持 4326 没问题但如果要算长度、面积或者跟其他投影数据叠加就必须转换。在 OGR 里做坐标系转换的代码如下from osgeo import osr # 定义源坐标系和目标坐标系 src_srs osr.SpatialReference() src_srs.ImportFromEPSG(4326) dst_srs osr.SpatialReference() dst_srs.ImportFromEPSG(32650) # UTM zone 50N适用于中国东部 # 创建转换对象 transform osr.CoordinateTransformation(src_srs, dst_srs) # 在写入要素前对几何做转换 geom.Transform(transform)EPSG 编码的选择取决于你的数据所在区域。中国常用的投影坐标系包括坐标系EPSG 编码适用场景UTM zone 49N32649新疆、西藏西部UTM zone 50N32650华北、华东UTM zone 51N32651东北、东南沿海CGCS2000 高斯克吕格 3 度带按带号计算全国范围官方用图选错投影坐标系的后果是面积和长度算出来偏差很大。一个快速判断方法转换后在 GIS 里量一条已知长度的路线看结果跟实际差多少。4.2 字段映射中文属性名怎么处理才不乱码kml 的属性字段经常是中文的比如名称描述路线编号。shp 的 dbf 文件对中文支持不好直接写入可能乱码。我的处理策略是分两步转换时先把字段名映射成英文转换后再在 GIS 软件里设置显示别名。# 字段名映射表 field_mapping { 名称: name, 描述: descript, 路线编号: route_id, 起点: start_pt, 终点: end_pt } # 在创建字段时使用映射后的名称 for i in range(src_defn.GetFieldCount()): original_name src_defn.GetFieldDefn(i).GetName() mapped_name field_mapping.get(original_name, ffield_{i})[:10] out_layer.CreateField(ogr.FieldDefn(mapped_name, field_defn.GetType()))如果字段值本身是中文还需要设置编码。在创建数据源时加上ENCODINGUTF-8选项out_ds shp_driver.CreateDataSource(shp_path, options[ENCODINGUTF-8])但要注意不是所有 GIS 软件都能正确识别 UTF-8 编码的 dbf。ArcGIS 对 UTF-8 的支持在 10.2 之后才完善QGIS 则一直支持得比较好。如果目标用户用的是老版本 ArcGIS可能需要在转换后再用 ArcGIS 的转换编码工具处理一遍。4.3 几何类型不匹配时的处理策略kml 里的 MultiGeometry 是个麻烦事。一个 Placemark 里可能包含多条线、多个面甚至线和面混在一起。shp 不支持这种混合类型必须拆开。处理策略取决于你的需求如果 MultiGeometry 里都是同类型的几何比如都是线可以在转换时用ogr.wkbMultiLineString类型把所有线合并成一个多线要素。但这样会丢失子要素之间的独立性。如果需要保留每个子要素的独立性就要在读取时遍历 MultiGeometry 的子几何每个子几何生成一个独立的 shp 要素geom feature.GetGeometryRef() if geom.GetGeometryName() GEOMETRYCOLLECTION: for i in range(geom.GetGeometryCount()): sub_geom geom.GetGeometryRef(i) # 为每个子几何创建独立要素 out_feature ogr.Feature(out_layer.GetLayerDefn()) out_feature.SetGeometry(sub_geom.Clone()) # 复制属性... out_layer.CreateFeature(out_feature)如果 MultiGeometry 里混合了线和面那就只能拆成两个 shp 文件分别存储。这种情况我一般会先跟需求方确认你到底要线还是要面很多时候对方会说都要那就老老实实拆两个文件。5. 避坑指南kml 转 shp 时最容易翻车的 5 个地方5.1 转换后要素跑到南极洲附近现象转换完成后加载到地图里所有要素都跑到南纬 90 度附近或者位置整体偏移了几十度。原因kml 的坐标顺序是经度,纬度但某些工具或代码在解析时按纬度,经度处理。另外如果 kml 里写的是lon,lat,alt而你的代码按lat,lon,alt读取就会发生经纬度互换。解决转换后第一件事就是加载到地图里目视检查。如果发现位置不对检查代码里读取坐标的部分确认经度在前。OGR 的 KML 驱动通常能正确处理坐标顺序但如果你手动解析 XML就要特别注意。5.2 属性表字段名变成乱码或截断后重名现象转换后的 shp 属性表里字段名显示为field_1、field_2或者中文变成问号。原因shp 的 dbf 格式对字段名有 10 字符限制对中文支持差。如果原始字段名超过 10 个字符不截断会报错截断后可能多个字段变成同一个名字。解决转换前先做字段名映射把中文长名改成英文短名。映射表要保证唯一性不能有两个字段映射到同一个名字。如果字段值有中文设置 UTF-8 编码并确认目标 GIS 软件支持。5.3 线要素断成很多小段现象kml 里明明是一条完整的路线转换后变成几十条短线段。原因kml 的 LineString 可能被拆分成多个LineString标签存储或者原始数据里就是分段采集的。另外如果 kml 里用了MultiGeometry包含多条线转换时如果按子几何拆分就会得到多个线要素。解决如果业务上需要一条完整的线转换后要用 GIS 的合并工具ArcGIS 的 Merge 或 QGIS 的 Dissolve把相邻线段合并。但合并前要确认这些线段确实属于同一条路线否则会把不同路线错误合并。5.4 转换速度慢到无法接受现象几千个要素的 kml 转换要等好几分钟批量处理时更是遥遥无期。原因没有使用事务机制每个要素写入都触发一次磁盘 IO。另外如果 kml 文件很大OGR 的 KML 驱动解析效率本身就不高。解决在写入循环前后加上StartTransaction()和CommitTransaction()。如果还慢可以先把 kml 转成 GeoJSON 或 GPKG 作为中间格式再用 OGR 处理。GPKG 的读写效率比 shp 高很多适合作为中间过渡。5.5 坐标系信息丢失导致后续分析出错现象转换后的 shp 没有 .prj 文件或者 .prj 文件内容为空加载到 GIS 里提示未知坐标系。原因创建输出图层时没有指定坐标系或者指定的坐标系没有被正确写入 .prj 文件。解决在CreateLayer时传入srs参数并确保srs已经正确初始化。如果转换后还是没有 .prj可以手动创建一个内容就是 WKT 格式的坐标系定义。QGIS 和 ArcGIS 都支持手动指定坐标系。6. 转换后怎么验证三个检查动作和一条自动化校验命令转换完成不等于万事大吉。我一般会做三个检查位置对不对、属性全不全、几何有没有问题。位置检查最简单把 shp 加载到 QGIS 或 ArcGIS 里叠加一个底图看要素位置是否跟底图吻合。如果偏了大概率是坐标系问题。属性检查打开属性表看字段数量和记录数是否跟原始 kml 一致。如果少了字段检查字段名截断逻辑如果少了记录检查是否有要素因为几何为空被跳过。几何检查用 GIS 的几何检查工具看有没有空几何、自相交、重复节点等问题。QGIS 的检查有效性工具能一次性列出所有问题要素。如果要做自动化校验可以用 OGR 写一个简单的检查脚本def validate_shp(shp_path): ds ogr.Open(shp_path) layer ds.GetLayer(0) feature_count layer.GetFeatureCount() geom_types set() null_geoms 0 for feature in layer: geom feature.GetGeometryRef() if geom is None: null_geoms 1 else: geom_types.add(geom.GetGeometryName()) print(f要素总数{feature_count}) print(f几何类型{geom_types}) print(f空几何数{null_geoms}) ds None validate_shp(route_output.shp)这个脚本能快速告诉你要素数量、几何类型和空几何数量。如果空几何数量不为零就要回头检查转换逻辑看是哪些要素出了问题。还有一个容易被忽略的点shp 的 .dbf 文件有 2GB 的大小限制。如果你的 kml 数据量特别大转换后的 dbf 可能超限。这种情况下建议改用 GeoPackage 或 File Geodatabase 格式它们没有这个限制。我自己的习惯是每次转换完先跑一遍校验脚本确认要素数量和几何类型没问题再加载到 GIS 里目视检查位置。这两个动作花不了几分钟但能避免后面分析时才发现数据有问题、不得不重新转换的尴尬。希望帮到你。本文还有配套的精品资源点击获取