☰
CAD转SHP带属性转换插件:从原理到实战,解决字段丢失问题
2026/10/16 9:39:27 网站建设 项目流程

简介:面向GIS及测绘工作人员的CAD与SHP格式互转工具插件,基于CAD2008环境即可实现带属性数据的无缝转换。资源包内包含核心转换功能DLL库、配置文件XML以及一份使用说明文档,共12个文件,整体仅877KB,轻量易用。插件提供XDATA、XDATAEDIT、LOADSHP、EXPSHP四条命令,可便捷查看、编辑CAD扩展属性,并能在CAD与Shapefile之间双向加载导出图形及属性信息。当前已有4522人学习下载,适用于需要批量处理CAD图纸和GIS数据转换的项目场景。包内除核心动态库外,还附带运行配置与说明文档,方便快速部署与上手。

1. CAD 转 SHP 带属性这件事:先搞清楚为什么总要丢字段

做管线普查、竣工图入库、地籍整理的人,大概率都经历过这么一幕:CAD 图纸拿到了,GIS 图层建好了,结果一顿常规转换操作下来,SHP 里只剩几何体,井号、管径、材质、高程这些业务字段全丢了,等于画了个空壳。这就是 CADSHAPE 带属性转换要解决的核心问题——不是"能不能转",而是"属性丢不丢"。这份插件按 CAD 实体类型分流处理:普通线体转几何,文字注记进字段,图块属性映射成 DBF 列,最后输出带 .prj 坐标文件的完整 Shapefile。它适合两类人:一是做数据入库、批量整理历史 CAD 资料的测绘工程师,二是被临时要求"今天把这几百张图转成 SHP 还要带属性"的一线现场人员。下面按拆包、原理、跑通、避坑、进阶五步展开,新手能照步骤走,熟手直接跳第四章看坑。

2. 插件的两条主线:几何怎么提,属性怎么带

2.1 压缩包里能拆出什么:三个文件加一个样例目录

一份能落地的转换插件,不会只丢一个脚本给你。我拆过的同类资源里,比较标准的组织方式是下面这样:

CAD转SHP(带属性)插件/ ├── cad2shp.py # 主转换程序,命令行入口 ├── attrib_map.json # 属性映射配置,决定哪些 CAD 属性进哪些字段 ├── requirements.txt # Python 依赖清单 ├── README.md # 使用说明,含命令行参数和已知限制 └── sample/ ├── 管线样例.dwg # 测试数据,带图块属性 └── 输出示例/ # 作者跑好的结果,用来对照验证

主程序负责两件事:读 CAD 实体、写 SHP 文件。映射配置单独拆成 JSON,是因为不同项目的图块属性名完全不一样,有的叫"井号",有的叫"井编号",还有的下划线后缀不同。硬编码在代码里,每换一个项目改一次脚本,迟早改乱。拆出来以后,改配置就行,代码一行不动。README 我建议先花十分钟看一遍,里面通常写清了依赖版本、运行方式和已知限制,很多坑其实文档里已经写了,只是没人看。sample 目录是验证用的,拿到插件第一件事,就是拿样例图跑一遍,确认结果和"输出示例"一致,再上自己的图,这一步能省掉后面大量的排查时间。

注意:SHP 是四件套,.shp 存几何,.dbf 存属性,.shx 是索引,.prj 是坐标参考。很多第一次用的人只拷了 .shp 就走,在 GIS 里打开报"缺少 .shx"或者属性表空白,其实不是转换的问题,是文件没带全。

2.2 属性从哪来:文字注记、图块属性与扩展数据

很多人以为 CAD 的属性就是"图形旁边那行字",其实按来源分,至少有四类:

  1. TEXT / MTEXT 文字实体:独立注记,内容存在实体自身的文本串里,转换时可以直接取;
  2. INSERT + ATTRIB 图块属性:比如检查井块里的"井号""井底高程""管径",以标签-值成对挂在块引用上;
  3. 扩展数据 XDATA:第三方管线软件写进实体的业务字典,键值对形式,需要单独解析;
  4. 实体公共头:图层名、颜色、线型、线宽、标高,这些也算属性,是每个实体都有的。

插件读取时逐实体遍历模型空间,按类型分流。文字实体直接取文本串;图块实体要递归进块表定义,把 ATTRIB 当成键值对收进字典;对 CIRCLE、ARC 这类曲线实体,先按参数离散成折线再输出。普通转换工具之所以丢属性,是因为它们只做了第 4 类公共头的拾取,甚至图层名都不一定保留,对第 1、2 类完全不碰。而"带属性转换"的核心,就是补上这几层抽取。核心抽取逻辑长这样:

import ezdxf def extract_entity_attrs(entity, msp): rec = { "LAYER": entity.dxf.layer, "HANDLE": entity.dxf.handle, "ENT_TYPE": entity.dxftype(), "TEXT": "", "ELEV": getattr(entity.dxf, "elevation", 0) # 不是所有实体都有标高,用 getattr 兜底 } if entity.dxftype() in ("TEXT", "MTEXT"): # TEXT 直接取 dxf.text,MTEXT 用 entity.text 拿剥掉格式码的纯文本 rec["TEXT"] = entity.dxf.text if entity.dxftype() == "TEXT" else entity.text elif entity.dxftype() == "INSERT": # 图块属性:遍历 ATTRIB,按标签 tag 收集,中文名先原样保留 for attrib in entity.attribs: rec[attrib.dxf.tag] = attrib.dxf.text return rec

逻辑说明:函数按实体类型返回一个字典,公共字段 LAYER、HANDLE、ENT_TYPE、ELEV 每个实体都有。TEXT 实体把文本写进 TEXT 键,MTEXT 用 entity.text 取纯文本,ezdxf 会自动处理多行文本的内部格式控制符。INSERT 实体通过 attribs 迭代器,把属性标签作键、属性值作值塞进字典,这一层先不转字段名,等写 DBF 时再统一映射。参数说明:HANDLE 是字符串形式的十六进制句柄,在 CAD 里可以按 Handle 反查定位实体,强烈建议保留;ENT_TYPE 存的是 DXF 类型名,比如 LINE、CIRCLE、INSERT,方便后续在 GIS 里做符号化分类。

2.3 字段映射规则:中文属性名怎么变成 ASCII 字段

DBF 的字段名规则很老,10 个字符以内、推荐 ASCII 大写。中文名在一些 GIS 平台里要么乱码,要么直接读不出来,所以插件不会把"井号"直接当字段名,而是通过 attrib_map.json 做一次映射。默认字段加业务字段,转换后的 DBF 大概长这样:LAYER、HANDLE、ENT_TYPE、TEXT、ELEV,然后是 WELL_NO、DIAMETER、MATERIAL 这些业务列。对应的配置文件:

{ "input_encoding": "gbk", "output_encoding": "utf-8", "shp_type": "auto", "default_fields": ["LAYER", "HANDLE", "ENT_TYPE", "TEXT", "ELEV"], "attr_map": { "井号": "WELL_NO", "管径": "DIAMETER", "材质": "MATERIAL", "埋深": "BURY_DEPTH" } }

逻辑说明:attr_map 的键是 CAD 图块属性标签,值是输出的字段名。插件每读到一个属性标签,先查映射表,命中就用 ASCII 名建字段,没命中就丢弃并写日志。input_encoding 决定读 CAD 内部字符串时按什么字符集解码,output_encoding 决定 DBF 用什么编码写。参数说明:shp_type 设 auto 时,插件按几何类型自动拆成点、线、面三个 SHP;如果业务只导管线,设成 line 能省掉多边形检测的开销。output_encoding 建议保持 utf-8,旧项目里如果有老 GIS 平台不认 UTF-8 的 DBF,改成 gbk 再试。

这里有个取舍要说明:CAD 里一个图块可以挂几十个属性,但业务真正用到的可能就三五个。映射表只留要用的那几个,能让 DBF 干净很多。剩下的属性不是丢了,只是没带进来,真需要的时候在映射表里加一行再重跑即可。全量保留的后果是 DBF 里塞满旋转角、比例因子、重复的属性定义这类对业务没意义的字段,入库前还得手工删。

3. 跑通第一条转换:环境、命令与结果校验

3.1 先把依赖装对:ezdxf、pyshp 与 pyproj 的分工

这个插件栈里三个库各管一段:ezdxf 读 DXF 实体和属性,pyshp 负责写 Shapefile,pyproj 做坐标参考系变换。装依赖:

pip install ezdxf pyshp pyproj

逻辑说明:三个库都是纯 Python 实现,装完就能跑,不需要装 CAD 软件本体,也不需要装 GIS 平台。ezdxf 是目前开源生态里对 DXF 支持比较全的解析库,支持 R12 到 R2018 的 DXF 版本;pyshp 是最常用的纯 Python SHP 读写库,API 简单,写 DBF 时能精确控制字段名和字段类型;pyproj 是 PROJ 的 Python 绑定,坐标系转换全靠它。参数说明:如果你的 Python 是 3.10 以上的新版本,直接装这三个包没有兼容性问题;如果是老机器上自带的 Python 2 环境,建议单独建一个虚拟环境,避免依赖冲突。

装完跑一句验证:

python -c "import ezdxf, shapefile, pyproj; print('deps ok')"

输出 deps ok 表示环境就绪。这里要提醒一句:DWG 不是公开格式,ezdxf 只读 DXF,直接喂 DWG 大概率报格式不支持。装完依赖先确认手上文件是 DXF 还是 DWG,这决定下一步走哪条分支。

3.2 DWG 转 DXF:跑插件前必做的前置步骤

如果手头全是 DWG,先用免费转换器批量转成 DXF,常见做法是装一个 DWG→DXF 转换器,完全离线批量处理。转换器参数一般就两个:目标版本选 2007 或 2010,输出目录指定好。2007 版 DXF 兼容性最好,老版本 CAD 和新版本都能打开,插件读起来也稳。

dwg2dxf --input ./dwg_all/ --output ./dxf_all/ --version 2007

逻辑说明:这是转换器的命令行形式,把整个目录的 DWG 一次性转成 DXF 2007。转完检查输出目录,DXF 文件数量应该和 DWG 一致,有失败的单独看报错。参数说明:--version 指定 DXF 版本,不是越新越好,2018 版 DXF 对老工具不一定友好;转换器默认按文件同名输出,目录结构会保留。注意,转换这一步不会丢属性,图块属性、文字注记都还在,可以放心转。

转换器转出来的 DXF 可能混合了多种字符编码,插件读的时候按 input_encoding 统一解码,所以 DWG 阶段如果混着 GBK 和 UTF-8 两种来源的文件,建议按来源分目录转,分别建配置,避免同一批里两种编码互相干扰。

提示:DWG→DXF 转换不会丢属性和文字,可以放心批量处理,这步别自己写脚本解析 DWG 二进制,吃力不讨好。

3.3 单文件转换命令:参数逐个说

第一次转换直接用样例数据:

python cad2shp.py -i sample/管线样例.dxf -o out_sample/ -c attrib_map.json

逻辑说明:-i 指定输入 DXF,-o 指定输出目录,-c 指定映射配置。插件遍历模型空间,按配置抽取属性,最后在输出目录生成 .shp、.dbf、.shx、.prj 四个文件。参数说明:输出目录不存在会自动创建;同名文件不会覆盖,会提示换目录或加 --force 强制覆盖;配置文件不传时用默认映射,只输出公共字段,业务属性全部丢弃,新手最容易漏掉 -c 参数,结果转完又说属性没了。

跑完终端会打印统计:

读取实体 1284 个,其中 TEXT 356,INSERT 421,LINE 507 输出点要素 421 条,线要素 507 条,文本要素 356 条 写入 out_sample/管线样例.shp 完成

这个统计对得上是最基本的验证。实体总数 1284,输出 1284,说明没有实体被静默丢弃;如果对不上,说明有实体类型没进分流逻辑,日志里会给出警告。常用命令行参数整理成表:

参数作用常见取值
-i / --input输入 DXF 或 DWG文件路径
-o / --output输出目录目录路径
-c / --config属性映射配置attrib_map.json
--layer-filter只转指定图层图层名或通配符
--scale坐标缩放系数0.001(毫米转米)
--force覆盖同名输出开关,默认关

参数说明:--layer-filter 在图层很乱的图里很有用,比如只要"给水""排水"两个图层,写成--layer-filter "给水,排水",其余图层不输出;--scale 解决单位问题,CAD 用毫米、SHP 用米时设 0.001,坐标整体缩放,不需要动源文件。

3.4 转完别急着入库:先做属性对照检查

拿到 SHP 先别往 GIS 里导,用 pyshp 读一遍字段和记录最快:

import shapefile sf = shapefile.Reader("out_sample/管线样例.shp") print("要素数:", len(sf)) print("字段列表:", [f[0] for f in sf.fields[1:]]) # 第一项是 DeletionFlag,从 1 开始取 for i in range(3): print(sf.record(i))

逻辑说明:这段代码把 SHP 的字段名和头三条记录打出来。要素数应该和转换统计里的输出数一致;字段列表里能看到 WELL_NO、DIAMETER;记录值要和 CAD 原图属性对得上。参数说明:sf.fields 的第一项是内部删除标记字段,取字段列表要从下标 1 开始;record(i) 返回的记录值顺序和字段列表一致,可直接按位置对齐。如果 WELL_NO 是空字符串,多半是图块属性标签不叫"井号",回到 CAD 里用属性面板查实际 tag,改映射表键名重跑。

对照检查重点就两个:数量对不对,值对不对。数量对、值不对,优先怀疑编码;值对、数量不对,优先怀疑实体类型分流。这一步花两分钟,能挡住后面入库返工的大麻烦。

4. 避坑指南:编码、坐标和属性丢失的五个典型问题

这五个坑是我在多个转换项目里实际撞过的,按出现频率排了个序。每个都按"现象 → 原因 → 解决"写,你在自己的数据上遇到同类问题,直接对号入座。

4.1 现象:中文字段值全是问号

转完打开属性表,井号、材质全是"????",或者干脆是乱码。原因是 CAD 里文字按 GBK 存储,插件却按 UTF-8 解码,或者正好反过来,源文件是 UTF-8 却按 GBK 解。解决:先确认图纸编码。最直接的办法是用文本编辑器打开 DXF,看头部 $DWGCODEPAGE 声明,ANSI_936 就是 GBK,UTF-8 会有明确标识;然后把 attrib_map.json 里的 input_encoding 改成图纸实际编码。输出端 output_encoding 保持 utf-8 即可,当代 GIS 平台基本都认。改完重跑,乱码基本消失。这个坑频率最高,十个转 SHP 的项目里至少三个栽在这,而且不容易自查,因为几何看起来完全正常。

4.2 现象:SHP 打开了,属性表一片空白

图层、几何都在,但 DBF 里除了默认字段没别的内容。原因多半是遍历逻辑只处理了 LINE、CIRCLE 这类基础实体,图块 INSERT 里的 ATTRIB 没进抽取流程;或者属性写在 XDATA 扩展数据里,而插件没解析这一段。解决:先在 CAD 里选中一个检查井,看属性面板确认属性是块属性还是扩展数据。块属性 tag 对不上映射键,就改 attrib_map.json;如果是 XDATA,要么找插件里是否带扩展数据解析的开关,要么自己补一段按字典键提取的代码。还有一种伪装情况:属性其实是块旁边的独立文字注记,不是真正的 ATTRIB,插件读不到,只能接受用 TEXT 字段承载内容,或者在前处理里把注记批量转成真属性。这一步是"带属性转换"最容易翻车的地方,比乱码更隐蔽,因为转换日志里看不出任何异常。

4.3 现象:转出来的坐标在 GIS 里偏出几百米

几何明显不在底图位置上,比例尺对,就是整体偏移或整体错位。原因有两个:一是 CAD 坐标系和 GIS 底图坐标系不一致,源数据是地方坐标系、任意坐标系,直接转没做变换;二是单位问题,CAD 常用毫米,SHP 标准单位是米,没做缩放就相当于整体缩小了 1000 倍。解决:先用已知控制点(比如道路交叉口)量一下两份数据的坐标差,确认是整体平移还是缩放;然后在配置里加 source_epsg 和 target_epsg 两个参数,插件用 pyproj 在写文件前做投影变换;单位问题用 --scale 0.001 处理。source_epsg 一般能从测绘成果说明里找到,找不到就按图廓和公里网标注反推投影带。坐标错位不是玄学,就是坐标系和单位两个变量没对上,逐个排查即可。

4.4 现象:圆弧、圆转成 SHP 后变成折线,弧段不光滑

SHP 格式不支持圆弧实体,只能把弧线离散成线段,离散密度不够就出现折角,弯头处最明显。原因是弦高或分段数参数设得太粗,圆转出来像多边形。解决:调整插件里的离散参数,常见做法是按弦高控制,1:500 地形图弦高取 0.05 米就够,1:10000 的大比例尺图可以放宽到 0.5 米;或者按圆心角控制,每段不超过 1 度。代价是输出文件变大,节点数成倍上涨,大图要先权衡要不要全图统一加密。做管线转 SHP 时我一般先用一个弯头密集的小图试参数,看着折线变光滑了再铺开跑全量,避免一整批转完才发现弯头都是锯齿。

4.5 现象:DWG 直接转换报"格式不支持"或"文件读取失败"

开源库对 DWG 原生格式支持有限,新版本 DWG(R2013 之后)基本读不了,只有 DXF 是公开格式。解决:先把 DWG 批量转成 DXF 再喂给插件,这是这类插件的通用前置步骤。我一般会直接让出图方另存一份 DXF 2007,既避开版本问题,又保留全部属性,省得自己转。批量场景就把 DWG→DXF 转进批处理脚本里,全程不用手动操作。凡是遇到"读取失败",先别怀疑插件坏了,九成是文件格式和版本问题,先确认扩展名和另存版本,再排查代码。

5. 进阶用法:整目录批量转换与字段完整性自查

5.1 整目录批量转换:一条命令处理几百张图

竣工图入库很少只转一张,几百张是常态,手工一张张敲命令不现实。插件支持目录模式:

python cad2shp.py --input-dir ./dxf_all/ --recursive --output ./shp_out/ -c attrib_map.json

逻辑说明:--input-dir 指定目录,--recursive 递归子目录,插件遍历目录下所有 .dxf 逐文件转换,输出到 shp_out 平铺目录,避免子目录结构影响 GIS 导入。参数说明:输出目录同名文件会被覆盖,建议每次转换前清空或换一个带时间戳的目录;跑完终端会打印每张图的实体统计和跳过原因,批量场景建议把输出重定向到日志文件,事后再查有据可依。

5.2 用配置模板固定业务字段

批量场景最怕每张图字段不齐,这张有井号,那张没有。在 attrib_map.json 里把业务字段定死,整个项目共用一套:

{ "default_fields": ["LAYER", "HANDLE", "ENT_TYPE", "TEXT", "ELEV"], "attr_map": { "井号": "WELL_NO", "井底高程": "EL_BOTTOM", "管径": "DIAMETER", "材质": "MATERIAL", "埋深": "BURY_DEPTH" }, "strict": true }

strict 设为 true 时,插件遇到映射表里没有的属性标签会输出警告而不是静默丢弃,方便你发现图纸里混进了意料之外的属性。第一次全量跑完,统计日志里命中的属性标签集合,把这个集合和映射表对比,缺的补上,多余的确认无用,两三轮以后模板就稳定了。之后批量转换就是无脑跑,重点放在结果自查上。

5.3 转完自动自查:字段完整性脚本

我现在批量转换后不靠肉眼抽查,直接跑一段自查脚本:

import glob, shapefile required = {"LAYER", "HANDLE", "WELL_NO", "DIAMETER", "TEXT"} for shp_path in sorted(glob.glob("./shp_out/*.shp")): sf = shapefile.Reader(shp_path) fields = [f[0] for f in sf.fields[1:]] # 跳过 DeletionFlag missing = required - set(fields) well_idx = fields.index("WELL_NO") if "WELL_NO" in fields else -1 empty_count = 0 if well_idx >= 0: # 统计 WELL_NO 为空字符串或空格的记录 empty_count = sum(1 for rec in sf.records() if rec[well_idx] in ("", " ")) status = "OK" if not missing and empty_count == 0 else "CHECK" print(f"{shp_path}: {status} 缺字段={sorted(missing)} 井号空值={empty_count}")

逻辑说明:required 是项目必填字段集合,遍历输出目录所有 SHP,检查字段是否齐全,再统计 WELL_NO 空值数量。字段缺失或空值存在就把状态标成 CHECK。参数说明:glob 按路径匹配,sorted 保证输出顺序稳定,方便对照日志排查;well_idx 先判断字段存在再取下标,避免字段缺失时 index() 抛异常。这段脚本建议存成 check.py 放项目根目录,每次批量转完先跑它,全 OK 再导 GIS,比人工抽查快得多,也不会漏掉几十张图里某一张的属性缺失。

从那以后,我每次做 CADSHAPE 带属性转换,不管单张还是一百张,都强制先跑一遍 check.py,字段齐、空值少,才往入库流程走。这个习惯帮我挡掉好几次深夜返工,尤其是那种转完才发现"井号全空"的案例——查到最后往往只是某张图的属性标签多了一个空格,映射没命中。先自查再入库,是这类转换项目里性价比最高的一道保险。希望帮到你。

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

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

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

立即咨询