shp数据从获取到转换全流程实战:以大理州行政区划为例
2026/8/28 3:10:53 网站建设 项目流程

简介:在GIS数据交换中,shapefile(shp)作为最通用的矢量格式,广泛用于行政区划边界存储与可视化。其本质是由多个文件构成的集合,其中.shp存储几何、.dbf存储属性、.shx提供索引,而.prj与.cpg分别记录坐标系与字符编码。理解这些底层原理,能有效规避图层打不开、属性乱码、图形偏移等高频问题。实际工程中,针对区县级行政边界,如大理州12个区县的数据获取、校验、拆分合并及格式转换,是数据工程师和GIS开发者的常见需求。从数据源选择、属性表检查到坐标系定义,再到批量转换为3dtiles、CAD、GeoJSON等下游格式,一套标准化的处理流程可大幅提升效率。掌握基于QGIS、ArcGIS及Python geopandas的工具链,配合对cpg编码和投影坐标的排查技巧,能轻松应对跨平台数据交换场景。 做 GIS 项目最常碰到的第一道坎,往往不是算法设计,也不是服务器部署,而是数据本身。我两年前接了一个云南方向的可视化需求,对方开口就说"你先拿大理州各区县的行政区划 shp 做一张底图"。当时我手头只有全国分省数据,偏偏缺按区县拆分的细粒度边界。后来几天时间,从数据来源、打开工具、属性表处理,到改成各种格式给下游用,我把大理州 12 个区县的 shp 数据从获取到转换整个流程完整踩了一遍,才发现这种看起来"不就是个区划文件嘛"的 shp,实际用起来到处都是坑。

这篇文章就把这套处理流程里最实用的部分一次性讲明白。核心围绕大理州各区县行政区划 shp 文件的文件结构、获取渠道、读取方式、拆分合并、格式转换以及避坑经验展开。不管你是刚接触 GIS 的新人,还是被 shp 的编码和坐标系折腾过几次的老手,这套方法论换成其他州市、其他区县级数据一样能照搬。

1. shp 到底是个啥:一堆文件搭伙过日子

很多人第一次接触 shp,习惯把它当成一个文件来理解,然后就会出现"为什么我拷走一个 .shp,到别的电脑就打不开了"这种灵魂拷问。实际上,shapefile(shp)不是单个文件,而是一组配套文件的集合。理解这一点,后面遇到各种打不开、乱码、坐标系漂移,至少能先有个排查方向。

1.1 shp/dbf/shx 是铁三角,谁缺了都尴尬

一个完整的 shapefile 至少包含三个基础文件:.shp、.shx、.dbf。这三个文件各干各的活:

  • .shp:存储几何信息,也就是点、线、面的坐标数据,是图形本身。
  • .shx:存储几何索引,作用是让软件快速定位到某条几何记录。它不直接画图,但就像一个目录。
  • .dbf:存储属性表,每一行对应一个要素,记录名称、代码、面积等信息。格式是经典的 dBase 数据库表。

用一个生活化的类比:shp 是户型图,dbf 是住户登记表,shx 是楼层索引目录。三者一起,才能完整描述"这一户是谁、长什么样、住几楼"。

实际使用中最常见的翻车情况是:从项目文件夹里单拷了一个 .shp 文件到 U 盘,到另一台电脑打开就报错"无法识别文件"或者图层是空的。因为你漏了 .shx 和 .dbf。更有意思的是,有些软件能容忍缺 .shx,但极慢;缺 .dbf 的话图形能显示,但所有属性全为空,区县名字全部消失;缺 .shp 那就不用说了,游戏直接结束。

所以拿到任何 shp 数据,第一件事就是确认这三个基础文件是否齐全。如果别人发你的压缩包里只有一个 .shp,先别急着骂对方,检查一下是不是邮件或网盘把这个多文件集合当成了碎片文件,自行过滤了。

1.2 prj 和 cpg:这两个"小跟班"最容易被忽略

铁三角之外,还有两个经常一起出现的文件,虽然名字不起眼,但往往决定你的数据能不能用对。

.prj 文件存储坐标系和投影信息。如果打开 shp 时软件弹窗提示"未知空间参考"或"缺少投影信息",基本就是没有 .prj 或 .prj 损坏。没有坐标系信息,数据在软件里就会默认按无坐标系处理,叠加别的图层时极容易出现位置错乱。比如明明是 CGCS2000 投影坐标的数据,软件按 WGS84 经纬度读取,图形会异常拉伸甚至跑到海里。

.cpg 文件记录字符编码,常见内容是 UTF-8 或 GBK。它不在铁三角里,但丢了以后,属性表里的中文大概率会变成乱码。这个文件的内容极其简单,往往就是一行文字。比如:

UTF-8

我见过不少人手动创建一个 .cpg 文件,内容写 "UTF-8",问题就解决了。所以 cpg 不是可有可无的摆设,它是属性表中文能否正常显示的关键。

另外还有一些附加文件,比如 .sbn/.sbx(空间索引)、.qpj(QGIS 的投影辅助文件)、.xml(元数据)。这些丢了基本不影响打开,但 .qpj 偶尔会影响 QGIS 的坐标系识别,好在新版 QGIS 都会自动读取 .prj 并生成 .qpj。

1.3 一个区县 shp 的属性表里通常有什么

以大理州各区县行政区划 shp 为例,打开属性表后一般会看到这些字段:

  • NAME 或 XZQMC:区县名称,比如"大理市""祥云县""宾川县"。
  • XZQDM 或 PAC:行政区划代码,每位数字都有含义,可以用它关联统计年鉴、人口数据等外部表。
  • SHENG / ZHOU:所属省、州,有时数据源层级清晰的话会附带。
  • AREA 或 SHAPE_Area:要素面积,注意不同数据源单位可能不同,有的是平方米,有的是平方千米,甚至有些是投影平面面积而非椭球面积。

这里我特别建议确认一下数据的行政区划代码是否和现行标准一致。因为区划代码会随着撤县设区、县界调整而变动,旧数据可能还保留着已经废止的代码。哪怕是官方发布的数据,也存在更新滞后,使用时最好以最新公布的代码为准做个抽样比对。

在实际项目里,我曾经拿到一个大理州的 shp 文件,属性表里只有图形没有名称字段,全靠一个数字代码撑着。这种情况下也不要慌,如果代码字段完整,可以写一段 Python 脚本,用代码字典把名称字段补全。但如果没有代码字段、也没有名称字段,这块数据就基本废了,只能重新找来源。

2. 先把手头没有的数据弄到手:获取与校验

这个标题放在哪都是热门搜索,因为它戳中了一个痛点:网上有大量"云南省县域轮廓 shp""某某市行政区划 shp",但是真正能保证数据准确、坐标规范、字段完整的并不多。我可以给你几个实践下来比较靠谱的渠道,再说说下载之后应该怎么校验。

2.1 靠谱的数据渠道

渠道这个东西不能只知道一个,因为很多平台会调整下载策略。我常用的有三个方向:

第一类是官方公开地理数据平台。比如全国地理信息资源目录服务系统,提供 1:100 万公众版基础地理数据,里面包含县级行政区域面。这类数据权威性高,属性字段规范,但生产时间可能早一些,县界不一定是最新的。

第二类是互联网地图开放平台。比如阿里云 DataV.GeoAtlas,提供了全国省市县级行政区划的 JSON 数据,可以下载到单个区县或者整个地级市的边界数据。格式主要是 GeoJSON,拿到手之后用 QGIS 或 Python 转成 shp 非常方便。这个路径适合做前端可视化项目,速度快、免费、数据更新比较及时,但坐标基准是互联网地图常用的国测局坐标,带偏移,不适合做专业测绘用途,这一点稍后展开说。

第三类是开源社区和 GIS 数据论坛。OpenStreetMap 可以导出全球的行政边界,但精度和官方边界之间可能存在差异,而且经过人工编辑,不同区域的完成度不一样。各类 GIS 交流社区里也会有人整理和分享省市级行政区划数据包,质量参差不齐,需要自己核查。

我的建议是:如果是做正式项目、对边界精度和坐标基准有要求,优先选择官方来源;如果只是做可视化、展示型应用,互联网平台的数据转换更轻量,完全够用。

2.2 下载后别急着用,先做三件事

我见过太多人下载完数据,双击打开看到图形,就以为万事大吉,结果做到一半才发现坐标系不对、属性表缺字段,回头返工。所以下载之后,务必先花五分钟做三件事:

第一,检查文件清单。压缩包解压后确认是否包含 .shp、.shx、.dbf、.prj 四个文件。如果缺 .prj,后面十有八九要处理坐标系问题。

第二,用 GIS 软件打开,查看属性表。确认有没有"区县名称"和"行政区划代码"字段,字段值是否规范。比如大理州应该有 1 市 11 县共 12 个区县,如果你的 shp 里只有 5 个要素,那数据八成被裁剪过或不完整。

第三,查看坐标系。在图层属性里看单位是度(地理坐标系)还是米(投影坐标系),看参考椭球是 WGS84、CGCS2000 还是西安 1980。推荐用下面的表格检查:

检查项正常情况异常情况
文件清单shp/shx/dbf/prj 齐全缺 prj 或 dbf,后续处理成本极高
要素数量大理州 12 个区县数量明显偏少,大概率被裁剪
名称字段大理市、祥云县等完整字段为空或乱码
坐标系CGCS2000 / WGS84 / 地方投影无坐标系或混合坐标系
属性编码中文正常显示乱码,需要处理 cpg 文件

以上任何一项不过关,建议优先解决,不要带着问题继续。

2.3 如果你只有云南省的州界,没有县级分割怎么办

有些数据包只提供了云南省级轮廓,没有大理州按区县拆分的面。这种情况我一般建议用两个思路解决:

第一,直接去数据源找更细粒度版本。全国地理信息资源目录服务系统提供"地市级"和"区县级"的面数据,可以精确筛选大理州。下载后按行政区代码或名称属性筛选出大理州下辖区县即可。

第二,如果只能拿到云南省县级边界数据,那就自己"切"出大理州。具体做法是:把云南省县级面数据和全国/云南的州界数据做空间相交或裁剪。QGIS 里的 Clip(裁剪)工具就能实现。先用州界把省界范围裁剪出大理州区域,再用县界图层做相交,最后按"州名称 = 大理州"过滤。这样处理出来的数据结构和你直接下载区县数据基本一样。

这里有个细节:注意"行政区面"和"行政区界线"是两种不同数据。"面"是填满整个区域的封闭多边形,适合做底色和统计可视化;"线"只包含边界线,适合做样式更灵活的边界叠加。如果拿到的数据是线文件,要先转成面文件(QGIS 提供 Polygonize 工具),否则后面做裁剪和相交会吃亏。

3. 打开一个 shp 的正确姿势:三种常用工具链

拿到数据之后,用哪种工具打开,取决于你的项目场景。常见的有三条工具链:ArcGIS / ArcGIS Pro、QGIS、Python。每一套都有它的优势和坑,我分别说说。

3.1 ArcGIS / ArcGIS Pro:拖进去就行吗?

在 ArcGIS 里打开 shp 确实很简单,直接把文件从文件夹拖到地图画布,软件会加载为一个图层。但如果你是第一次打开且文件缺少 .prj,软件会弹出提示框问"未知空间参考",这时你选择"以 WGS84 打开"只是这一次会话临时生效,并没有修改文件本身。如果你的数据实际是 CGCS2000 投影坐标,这一步就会埋下坐标系错误的隐患。

正确做法是:遇到未知坐标系的提示后,先搞清楚数据原本的坐标系,然后用"定义投影"工具给数据补上正确的坐标系信息,再叠加其他图层。这样之后每次打开都是对的。

ArcGIS Pro 和 ArcMap 在加载 shp 上的行为基本一致,只是 Pro 在投影处理上更智能,部分投影信息可以自动识别。不过 Pro 对机器配置要求高,家庭电脑没装的话直接看下面两个方案。

3.2 QGIS:免费、跨平台的兜底工具

如果你不想为偶尔一次打开 shp 专门装一个重型商业软件,QGIS 是最合适的替代。它免费、跨平台、启动快,对 shp 的兼容性几乎是最好的。QGIS 打开 shp 同样可以直接拖拽,也支持"图层-添加图层-添加矢量图层"的常规操作。

QGIS 在编码处理上比 ArcGIS 更透明。遇到中文乱码时,可以右键图层-属性-源设置,有一个"数据源编码"选项,手动切换 UTF-8 或 GBK,马上就能看到属性表恢复正常。这个功能在 ArcGIS 里反而没有这么直观,cpg 文件丢失时你只能靠手动创建 cpg 文件或转编码。

3.3 Python:geopandas 是处理 shp 最省心的库

用 Python 处理 shp,我首选 geopandas,其次才是 pyshp/shapely/fiona 这些底层库。geopandas 把数据读入 DataFrame 结构,处理属性表、筛选、拆分、合并都特别顺手。

安装方式建议用 conda,因为直接 pip 安装 geopandas 时,GDAL、Fiona 这些底层依赖很容易出现版本打架。我的建议是:

conda create -n geo python=3.10 conda activate geo conda install geopandas

读取大理州区县 shp 的代码非常简单:

import geopandas as gpd gdf = gpd.read_file('dali_county.shp') print(gdf.shape) # 查看行数和列数 print(gdf.columns) # 查看字段名 print(gdf.crs) # 查看坐标系 print(gdf[['name', 'xzdm']].head()) # 预览关键字段

如果只想快速画个图:

gdf.plot()

这里要注意的一点是:geopandas 读取 shp 时的路径尽量不要包含中文。Windows 环境下中文路径偶尔会触发编码异常,明明数据没问题,就是读不进去。把项目目录都改成英文,能少踩很多坑。

3.4 打不开或图层为空时,按顺序排查

加载 shp 图层为空或者完全打不开,95% 的情况逃不出下面几个原因:

  • 缺 .shx:文件不完整,图层可能加载成功但不显示内容。
  • 路径含中文:尤其在 ArcGIS 和 Python 的 GDAL 环境下,中文路径会引发读取异常。
  • 用文本编辑器打开过 shp 并保存了:shp 是二进制文件,用记事本打开再保存,文件头被破坏,整个文件就废了。
  • 几何数据损坏:图形要素数异常,但属性表正常,这种大多数是源数据问题,需要重新下载。

排查顺序建议:先看文件清单 → 再改英文路径 → 再用 QGIS 打开试试 → 最后检查 shp 文件大小是否异常小。这样一步步定位,基本不会白折腾。

4. 从"全州一张图"到"任意区县组合":拆分、合并与区域重组

很多时候我们拿到的是一整张大理州区县图,但项目只需要其中几个区县,或者需要把每个区县单独存成一份 shp。这个章节就把拆分、合并、融合和网格化这些高频操作一次说透。

4.1 按区县导出:右键导出最直接

如果你只需要某几个县,最简单的方法是打开属性表,选中相应要素,再右键图层-数据-导出数据,在弹出的对话框中勾选"所选要素",指定输出路径即可生成新的 shp。

按属性选择时,SQL 语句可以这样写:

"NAME" = '大理市' OR "NAME" = '祥云县' OR "NAME" = '宾川县'

需要注意,属性字段名如果是中文,在 SQL 表达式里要用双引号括起来。有些版本的 ArcGIS 对中文 SQL 的解析不友好,如果总报语法错误,可以先看一下字段的别名是否存在冲突。

QGIS 里操作也很方便:右键图层-筛选,输入同样格式的表达式,然后选中筛选结果,右键导出-保存所选要素为。

4.2 批量一次性拆分:ArcGIS 的 Split 工具

如果要把大理州下辖 12 个区县全部一次性拆成 12 个独立 shp,可以试试 ArcGIS 的 Split 工具。路径是"分析工具-Analysis Tools-提取-Extract-Split"。这个工具需要一个分割要素图层,并指定一个字段作为拆分依据。比如把全州面数据作为输入,用"NAME"字段做分割字段,运行后会在输出目录里生成按每个区县名称命名的 shp 文件。

实操中有两个坑必须提醒:

第一,输出文件夹必须为空。Split 工具不会自动覆盖已有文件,如果输出目录里已经有同名文件,运行会直接报错。

第二,分割字段的值不能包含非法字符。假如某个区县名称里带了空格或特殊符号,生成的 shp 文件名可能会异常。大理州的区县名称都是规范地名,一般没有问题,但如果你处理的是其他数据源,建议先检查字段值。

QGIS 里对应的工具叫"拆分矢量图层"(Split vector layer),在 Processing Toolbox 里直接搜索即可,输入图层和唯一 ID 字段就能批量输出。

4.3 Python 按属性循环拆分,灵活度最高

当数据字段复杂、输出要求多样时,用 Python 写循环拆分是最灵活的。

import geopandas as gpd from pathlib import Path gdf = gpd.read_file('dali_county.shp') out_dir = Path('dali_split') out_dir.mkdir(exist_ok=True) for name, group in gdf.groupby('name'): # name 字段是区县名,group 是当前区县的要素集合 group.to_file(out_dir / f'{name}.shp', encoding='utf-8')

执行完会在 dali_split 目录下生成 12 个 shp 文件,每个文件都带完整的属性表和坐标信息。这里的关键点是encoding='utf-8'参数,它会在输出时自动生成对应的 .cpg 文件,保证后续打开时中文不乱码。

如果你需要按多个县合并输出,比如"东三县"(祥云、弥渡、宾川)合并成一份,可以先构造一个分组字典,再用条件筛选。

4.4 反过来合并:多份县界拼成一张州图

合并操作最常发生在两种情况:一是手上有多个县的独立 shp,想拼成大理州全州图;二是从其他来源拿到的分块数据,需要合成一张完整覆盖区域。

ArcGIS 的"合并"工具(Merge)可以处理多个输入图层,但有一个大前提:各图层的字段结构最好一致。如果 A 县数据有"NAME"字段,B 县数据却是"XZQMC",合并后字段会直接分开,其中一个字段大量为空。这种问题可以通过"字段映射"功能手动对应,但比较繁琐,最好在合并前统一字段结构。

QGIS 对应的工具是"合并矢量图层"(Merge vector layers),同样建议先用一个简单的脚本统一字段名和类型,再合并。

另外还有一个容易混淆的工具是"融合"(Dissolve)。Merge 是把多个文件合成一个文件,Dissolve 是把一个图层里的多个要素按某个字段合并成一个要素。比如把 12 个区县面按"州名称=大理州"字段融合成一张全州大面。这个操作在做区域底色、简化数据时非常常用。

4.5 渔网分割:把区县 shp 切成规整格子

热搜词里有个"渔网分割 shp",用的是 ArcGIS 的"创建渔网"(Create Fishnet)工具。它的思路是:生成一个覆盖研究区域的网格面,再把网格面和区县边界做相交(Intersect),最终得到"按县域边界切割后的规整网格"。

这个操作在人口分布统计、空间抽样、农业区划可视化的场景里很常见。比如估算大理州各县在不同格网尺度下的数据量,就需要先有"每个格子归属哪个县"的空间判断结果。

流程可以简化为三步:

  1. 用 Create Fishnet 生成网格,网格范围设置为大理州 shp 的外包矩形。
  2. 用 Clip 或 Intersect 把网格裁剪到州界范围。
  3. 用"空间连接"把区县名称字段关联到每个网格上。

实际操作中,网格尺寸要根据你的研究尺度定,比如 1km x 1km 或 10km x 10km。网格太小会生成海量要素,电脑配置不够容易卡死;网格太大又失去统计意义。建议先小范围试跑一次,确认性能可以接受再全州执行。

5. 高频转换全流程:3dtiles、CAD、txt、KML 与 SketchUp 导入

shp 格式虽然通用,但下游环节经常需要其他格式,热门搜索词里最集中的就是 shp 转 3dtiles、shp 转 txt、批量 shp 转 CAD、SketchUp 导入 shp。这一章把这几条转换路径和各自的坑完整拆开讲。

5.1 shp 转 3dtiles:平面数据如何在三维地球里显示

3dtiles 是面向 Web 端三维场景的数据格式,Cesium 生态用得最多。把大理州各区县行政区划 shp 转成 3dtiles,核心思路并不是把矢量面变成三维模型,而是给平面矢量数据赋予一个"高度基准",让它在三维地球上有正确的落位。

我常用的工具是 CesiumLab,它提供免费的格式转换功能,界面操作也算友好。流程大概是这样:

  1. 先用 QGIS 把 shp 转成 GeoJSON,CesiumLab 对 GeoJSON 的兼容性更好,也可以直接拖入 shp,但偶尔会出现属性字段丢失。
  2. 打开 CesiumLab 的"矢量转 3dtiles"功能,输入 GeoJSON 或 shp。
  3. 选择高度模式。如果要让区县贴着地表显示,选"贴地"模式;如果要生成有厚度的立体柱状区域,选"拉伸",并设置拉伸高度,比如 500 米。
  4. 设置坐标系。数据如果是 WGS84 经纬度,CesiumLab 会自动识别;如果是投影坐标系,最好先转成 WGS84 再导入。
  5. 导出后得到一个 tileset.json 为主的文件目录,在 CesiumJS 中通过Cesium3DTileset加载即可。

这里有一个实战建议:做三维可视化前,先用 QGIS 的"简化"工具(Simplification)对区县边界做一次抽稀。简化容差设置为几十米,肉眼几乎看不出区别,但瓦片生成体积和加载性能会有天壤之别。我处理过大理州的数据,原始边界几万甚至几十万个节点,简化后能降到几千个节点,加载速度提升非常明显。

另外,如果你的开发栈是 ArcGIS Pro,Pro 3.x 之后也支持直接导出 3D Tiles,在"共享"面板里可以选择。这个方案对 ArcGIS 用户来说是最顺手的,不需要额外装工具。

5.2 批量把多个 shp 转成 CAD(DWG / DXF)

把 shp 转成 CAD 格式,在设计院、规划院的协作中非常常见。大家拿到的往往是 DWG 或者 DXF,而 GIS 侧输出最稳定的通常是 DXF。

ArcGIS 里的路径是"转换工具-转换为 CAD-导出为 CAD"。它可以同时选择多个 shp 文件,一次输出成一个 DWG 或 DXF。需要注意,CAD 里没有 GIS 的属性表概念,所以导出的图形只有几何轮廓和少量注记,区县名称能不能显示成文字,取决于是否需要设置注记映射。

如果数据量比较大,我更喜欢用 QGIS 配合命令行动手,因为可以批量跑。

安装 GDAL 之后,可以用命令行:

ogr2ogr -f DXF output.dxf input.shp

如果有多个 shp 文件,在 Python 里循环调用即可。

import subprocess from pathlib import Path for shp in Path('shp_dir').glob('*.shp'): subprocess.run([ 'ogr2ogr', '-f', 'DXF', f'cad_output/{shp.stem}.dxf', str(shp) ])

几个前车之鉴:

  • 输出 DXF 版本最好选择 2010 或以上,老版本 CAD 打开新版 DXF 有时会出现图元丢失。
  • 面要素转过去默认是闭合多段线,如果 CAD 里需要独立的面对象,可能要在 CAD 端再处理一次。
  • 中文注记经常出现乱码或丢失,这个和字体映射有关,建议导出前先确认 CAD 端的文字样式。

5.3 shp 转 txt:属性表和坐标提取

热搜词里有个"测定界 shp 转 txt 工具.tbx",这是测绘领域比较细分的需求,比如把界址点 shp 转成坐标点 txt 文件给全站仪或者平差软件用。更通用的场景是:拿到一个点数据 shp,需要把下面的点号、X 坐标、Y 坐标导出成 txt。

在 ArcGIS 中,可以先用"添加 XY 坐标"(Add XY Coordinates)工具给属性表增加 POINT_X 和 POINT_Y 字段,然后右键属性表-导出,选择 dBASE 表再转成 txt。但如果只是要一个简单文本,用 Python 更快:

import geopandas as gpd gdf = gpd.read_file('points.shp') with open('points.txt', 'w', encoding='utf-8') as f: for idx, row in gdf.iterrows(): x, y = row.geometry.x, row.geometry.y f.write(f"{row['point_id']},{x:.6f},{y:.6f}\n")

一个非常重要的细节是:导出坐标之前先确认坐标系。如果是经纬度(地理坐标系),输出的数是带小数位的度;如果是投影坐标系,输出单位是米。不同坐标系输出的坐标数值差距非常大,所以导出的 txt 一定在文件头注明坐标系,否则下游交换时很容易出错。

5.4 shp 转 KML / GeoJSON

这两个格式在 Web 可视化中很常遇到。KML 是 Google Earth 和 Google Maps 常用的格式,GeoJSON 则是各种前端地图库(Leaflet、Mapbox、OpenLayers)的标配。

QGIS 的操作非常直接:右键图层-导出-另存要素为,格式选 KML 或 GeoJSON。注意 KML 对字段名和长度有要求,属性字段最好用英文短名,中文长字段名会生成一层嵌套的 Schema,影响后续解析。

如果使用 geopandas,一行代码就能转 GeoJSON:

gdf.to_file('dali_county.geojson', driver='GeoJSON', encoding='utf-8')

转 KML 的话,geopandas 默认驱动可能不太好用,我一般还是用 QGIS 或者 GDAL:

ogr2ogr -f KML output.kml input.shp

还需要特别说明:KML 默认以 WGS84 经纬度存储。如果源数据是投影坐标系,转 KML 之前务必先重投影到 WGS84,否则 Google Earth 里的位置会错得离谱。

5.5 SketchUp 导入 shp 的曲线方案

热搜词里"su 怎么导入 shp"这个问题也很典型。SketchUp 本身不直接支持读取 shp,因为它是三维建模软件,不是 GIS 工具。但实际工作中经常要把地形范围、建筑用地边界从 GIS 数据搬进 SketchUp 做体块推敲。

我的做法是:先用上一节的方法把 shp 转成 DXF,然后在 SketchUp 里通过"文件-导入"选择 DXF 格式。导入成功后,面会变成 SketchUp 里的闭合边线和面,可以配合推拉工具生成三维体块。

如果不想走 DXF,还有一条路:用插件。SketchUp 有一些收费或免费的 GIS 导入插件,可以直接导入 shp 并保留部分属性,但安装和稳定性比较看运气,而且很多插件在较新版本的 SketchUp 里已经停止维护,所以最稳的方案还是 DXF 中转。

转换时有一点要注意:shp 里如果包含高程(比如地形等高线),DXF 导入 SketchUp 后会自动按 Z 值生成三维线。如果只有二维面,导入后就是平面图形,需要手动在 SketchUp 里拉高度。

6. 那些让我炸毛的坑:cpg、坐标系、字段名和文件大小

最后一章专门讲坑。这些坑我在处理大理州区县数据时几乎全踩过,有些坑甚至能让人昏天黑地排查一整天。把这几个问题讲透,比你多装十个软件都管用。

6.1 cpg 文件丢失导致的乱码问题

热搜词里有个问题问得很具体:"shp 文件导出的时候没有 cpg 文件是怎么回事"。这个问题背后的原因有三类:

第一,源数据本身就没有 cpg 文件。很多从老系统、专业仪器导出的数据压根没生成过 cpg。

第二,命令行工具导出时没有自动写 cpg。部分 GDAL 版本或第三方库默认不生成 cpg,导致输出的 shp 没有编码标识。

第三,ArcGIS 中文系统的默认编码是 GBK 或 GB2312,导出的 dbf 文件用 GBK 存储字符串,但如果不生成 cpg,其他软件读取时只能猜测编码,猜错就是乱码。

解决思路也简单,最直接的是手动创建 cpg 文件。比如你的文件是 dali_county.shp,就新建一个 dali_county.cpg,内容写UTF-8GBK,保存为无 BOM 的纯文本,放在同一目录下。

如果需要批量修复,用 Python 一行脚本就能搞定:

from pathlib import Path for shp in Path('.').glob('*.shp'): cpg = shp.with_suffix('.cpg') # 如果源数据是UTF-8就写UTF-8,是GBK就写GBK cpg.write_text('UTF-8', encoding='utf-8')

有一种更稳妥的方式:不要手动修改原始文件,而是用 QGIS 打开乱码数据,在"数据源编码"里切换正确编码,然后另存为一个新的 shp,并在保存时设置编码为 UTF-8。这样新的输出一定会带 cpg,以后不管在哪里打开都不会乱码。

6.2 坐标系错乱:为什么图形出现在大海里

坐标系问题在 shp 数据交换中最常见,而且报错方式极具迷惑性:图层能打开、属性表正常、图形形状也对,但位置就是不对,比如整个大理州跑到印度洋中间。

这种情况通常是两种原因叠加:

一是数据没有 .prj 文件,软件按默认的无坐标系或 WGS84 读取。如果这个数据的实际坐标是投影坐标(比如 CGCS2000 3 度带高斯投影),数值是 500 万级别的米数,被当成经纬度显示后,地图范围变成"东经 500 万度",当然不可能落在正确位置。

二是定义投影和投影转换搞混了。在 ArcGIS 中,"定义投影"(Define Projection)只是给数据"贴一个坐标系标签",不改变坐标数值;"投影"(Project)才是真正的坐标转换。很多人拿到无坐标系数据,直接用了 Project 工具,结果报错或者结果完全不对。正确顺序是先定义投影,再投影转换。

QGIS 里对应的操作是"重投影图层"(Reproject layer)。我一般会在重投影前先看一眼原始数据的坐标范围,如果单位是 6 位数级别的米数,多半是投影坐标系;如果是 90 到 105 之间的数,多半是经纬度坐标系。这一步可以帮你判断需要定义哪种投影。

另一个需要留意的点是互联网地图数据。从在线平台下载的 GeoJSON 往往带偏移(即非标准地理坐标),直接和测绘坐标系的 shp 叠加会出现几百米的偏移。这个在设计可视化方案时就要提前确认,不要等到数据已经接入系统才发现。

6.3 字段名和字段类型的限制

dbf 格式非常古老,字段限制很多:

  • 字段名最长 10 个字符。超过之后,不同软件处理方式不一样,有的直接截断,有的加序号后缀,导致字段名对不上。
  • 文本字段最长 254 个字符。如果你的属性里要存一段长描述,存到 dbf 会丢失。
  • 字段名建议只用英文、数字、下划线。中文字段名在部分转换工具里会出现不可预期的问题,尤其通过 ogr2ogr 转其他格式时,字段名编码混乱的概率极高。

所以写 shp 之前,我习惯先做一个"数据瘦身":只保留后续需要的字段,字段名改成英文短名,长文本抽出来单独放到外部表关联。这样后续所有转格式都顺畅很多。

6.4 大数据量和几何体问题

shapefile 单个文件有 2GB 的体积上限,这在实际中很少触发,但如果数据量极大(比如全要素的地形图、高精度道路网),还是可能碰到。应对方案是拆分区域,或者改用 GeoPackage 等格式。

几何体方面,shp 不支持复杂拓扑,所以面自相交、面重叠、空隙这些问题都无法自动识别和修复。QGIS 提供了一个"修复几何"(Fix geometries)工具,对自相交要素有较好的处理效果。我在处理一些从 CAD 转过来的 shp 时,经常需要先跑一遍修复,再做叠加分析。

还有一个值得注意的小点:有很多 shp 数据带有 M 值和 Z 值,通俗说就是每条线/面还附带测量值和高程值。这种数据在某些叠加分析工具里会报"不支持 M/Z 值"的错,解决办法是另存一份时把 M/Z 值去掉,或者用工具把几何类型转换一下。

老实说,shp 这种格式已经存在几十年了,技术上确实老态龙钟,但它依然是 GIS 数据交换的默认语言。与其抱怨它难用,不如把它的脾气摸透。

从我个人的经验来说,处理大理州这类行政区划 shp 数据,最值得养成的一个习惯是:拿到任何 shp 的第一时间,先看 crs 和 encoding,而不是迫不及待地打开地图画图。这两个元数据一旦确定没问题,后面所有流程基本就顺了。如果是从同事或第三方手里接过二手数据,开口先问一句"这个数据的坐标系和编码是什么",这句话能帮你省下可能是一整天的排查时间。

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

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

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

立即咨询