简介:北京市行政区域SHP矢量数据包,采用Esri标准Shapefile格式,存储全市及各区县、街道边界,适合GIS从业者、城市规划师、科研人员与数据分析师使用,可满足区划底图、空间查询与专题制图等需求。压缩包共11个文件,包括.shp几何矢量数据、.dbf属性表数据、.shx空间索引、.prj投影坐标信息、.xml元数据描述及.lock临时锁文件,其中核心四类文件构成完整基础数据集,能在ArcGIS、QGIS等主流GIS软件中无缝打开,包体仅82KB,轻量高效。已有3675人学习/下载,应用可信度较高。解压即可获得北京市行政区划矢量图层,可快速用于区划可视化、人口统计、交通流分析、环境研究等领域,也可叠加人口密度、路网等数据层进行空间分析,为城市研究与规划决策提供可靠的基础地图支撑。
1. 北京市 shp 文件:做城市空间分析前,先跨过数据这关
做城市尺度分析或地图服务时,一份行政区划矢量数据是绕不开的底座,而讨论度最高的就是北京市 shp 文件。很多人以为拿到一个 .shp 就完事了,等真正打开才发现:dbf 属性表全是乱码,坐标系在 WGS84 和 CGCS2000 之间摇摆,边界线在街道级数据里还留着自相交。我按一线处理经验,从文件结构、坐标系校验、Python 读写到高频报错排查,完整讲一遍北京市 shp 文件从小白到能用的路径。适合刚接触 GIS 的开发者,也适合被数据清洗反复折磨的熟手,照着下面的顺序操作,一小时基本能把一份原始数据变成可交付的成果。
2. 拆开北京市 shp 文件:四类文件缺失与坐标系统校验
2.1 shp / shx / dbf / prj 各自负责什么,缺失会产生什么后果
ESRI Shapefile 不是单文件格式,而是多个文件组成的集合。很多新手从网上下载“北京市.shp”,实际收到的是一个压缩包,里面散着至少四个文件。文件之间的配套关系一旦被破坏,后续所有操作都会翻车。
| 扩展名 | 作用 | 缺失或损坏的后果 |
|---|---|---|
| .shp | 存储几何图形数据 | 软件直接无法识别,提示“无效的数据源” |
| .shx | 几何索引文件 | 部分软件仍能打开,但浏览和查询速度明显变慢 |
| .dbf | 属性表,字段名和字段值 | 几何还在但属性全丢,无法做筛选和区名关联 |
| .prj | 坐标系统描述文本 | 软件默认按 WGS84 打开,坐标容易偏移 |
| .cpg | 声明 dbf 的编码格式 | 缺失时中文属性大概率乱码 |
我处理北京市数据时,第一件事是检查压缩包里有没有这五个文件。如果只有 .shp 和 .dbf,还能补救;如果 .prj 缺失,就要从数据来源说明或对比已知控制点反向判断坐标基准,工作量大得多。建议在拿到数据后立刻用脚本做一次完整性检查,不要等加载报错再查。
2.2 WGS84、CGCS2000、BD09:北京市数据到底该用哪个坐标系
北京市 shp 文件最折磨人的不是几何本身,而是坐标系。市面上流通的边界数据常出现三种坐标系混用:WGS84、CGCS2000、BD09。它们之间差异很大,选错了轻则边界与底图错位,重则面积计算完全不可信。
WGS84 是目前互联网和 GPS 设备最通用的地理坐标基准,经纬度数值接近 CGCS2000,两者在全球范围内差值通常不超过 1 米,在北京市范围内的差值更小。CGCS2000 是法定测绘基准,近年官方发布的成果大多基于它。BD09 是商业地图平台使用的加密坐标系,同一个点在 BD09 和 WGS84 之间可能相差几百米甚至更多,只适合对接互联网地图底图,不适合作为行政边界数据的存储基准。
我的选择习惯是:存储和分析统一用 WGS84(EPSG:4326),计算面积和距离时再投影到适合北京的平面坐标系,发布到商业底图时单独做 BD09 转换。这样换环境、换工具代价最小。可以通过读取 .prj 文件来判断源数据基准,常见文本里会出现 “WGS 84”“CGCS2000”或“GCS_China_Geographic_Coordinate_System_2000”这类关键词。
注意:不要用 4326 的经纬度直接做面积和距离计算,单位是度而不是米,结果无法使用。要先 to_crs 投影到平面坐标。
2.3 用 ogrinfo 一条命令校验文件完整性与坐标系
拿到北京市 shp 文件后,我习惯用 GDAL 自带的 ogrinfo 做一次“体检”,它比直接打开 ArcGIS 或 QGIS 更快,也方便在服务器上无图形界面排查。命令如下:
ogrinfo -so -al 北京市.shp其中-so表示只输出概要信息,-al表示遍历所有图层,最后的路径指向 shp 文件。正常的输出会包含图层名称、要素数量、几何类型、坐标范围和坐标系信息。
如果只需要提取关键信息,可以配合 grep 过滤:
ogrinfo -so -al 北京市.shp | grep -E "Geometry|Feature Count|Extent|PROJCRS|GEOGCRS"这条命令的输出里,Geometry 会显示 Polygon 或是 MultiPolygon,很多边界数据导出时把面拆成了 MultiPolygon,这是正常的。Feature Count 对应行政单元数量,市级边界一般是 1,带区县的可能是 10 个以上。Extent 是数据范围,北京市的经纬度范围大致在北纬 39.4 到 41.1、东经 115.4 到 117.5 附近,如果坐标完全不符,说明坐标系可能被识别错了。Layer SRS 一行会显示具体坐标系,如果这里为空,说明缺 .prj,后面做坐标转换前必须先人工确认基准。
3. 用 Python 处理北京市 shp 文件:读取、筛选与导出
3.1 安装 geopandas 并读取北京市行政区边界数据
日常处理北京市 shp 文件,我基本不打开桌面 GIS,而是直接用 Python 的 geopandas 库。它把 pandas 的 DataFrame 和空间几何结合起来,读取、筛选、导出一套代码解决。
pip install geopandas读取数据的代码非常简单:
import geopandas as gpd # 注意 encoding 要跟 dbf 实际编码一致,源数据常见 GBK 或 UTF-8 gdf = gpd.read_file("北京市.shp", encoding="utf-8") print(gdf.head()) print(gdf.crs) print(gdf.geometry.type.unique())如果encoding="utf-8"读取后区名字段出现乱码,就改成encoding="gbk"。crs 属性如果返回 None,说明源数据缺 .prj;如果显示EPSG:4326,说明坐标基准大概率是 WGS84。打印 geometry.type 是为了确认几何类型,后续做面积和缓冲操作时,Polygon 和 MultiPolygon 的行为会略有差异。
3.2 按属性筛选北京市某几个区域,参数化过滤
拿到完整边界后,最常见的需求是只保留北京市下辖的某几个区。操作上没有难度,关键是要先确认属性字段名是什么,源数据可能叫name、NAME、区县名,也可能因为编码问题显示成乱码。先运行print(gdf.columns.tolist())看列名,再写筛选条件:
import geopandas as gpd gdf = gpd.read_file("北京市.shp", encoding="utf-8") # 需要保留的区名列表,实际名称以源数据字段值为准 district_list = ["东城区", "西城区", "海淀区"] # 假设区名存在 name 字段中 filtered = gdf[gdf["name"].isin(district_list)].copy() print(filtered.head()) filtered.plot()用isin而不是等值判断,是避免一个个拼接过滤条件的笨办法,也方便把区名列表抽出来做成配置文件。copy()一定要加,否则后续在新对象上改动几何或属性时,会触发 pandas 的链式赋值警告,有时还会污染原始数据。
3.3 导出 GeoJSON / 投影面积 / 缓冲区:常用参数的边界条件
处理完北京市 shp 数据后,要么导出给前端做可视化,要么计算面积做统计,要么做缓冲区分析。三个需求对应三个不同的参数设置。
导出 GeoJSON 给 Web 地图用,必须确保坐标系是 WGS84,因为大多数地图库只认经纬度:
import geopandas as gpd gdf = gpd.read_file("北京市.shp", encoding="utf-8") # 导出前统一转到 EPSG:4326 gdf_4326 = gdf.to_crs("EPSG:4326") gdf_4326.to_file("北京市.geojson", driver="GeoJSON", encoding="utf-8")计算面积时,把矢量数据投影到北京市所在区域的平面坐标系。我能确定且常用的一个选择是 WGS84 / UTM zone 50N,也就是 EPSG:32650,它覆盖北京经度范围,等同采用 UTM 投影。代码写法如下:
import geopandas as gpd gdf = gpd.read_file("北京市.shp", encoding="utf-8") # 转投影坐标,单位变成米,面积才能算成平方米 gdf_proj = gdf.to_crs("EPSG:32650") gdf_proj["area_m2"] = gdf_proj.geometry.area print(gdf_proj[["name", "area_m2"]])做缓冲区时同理,在投影坐标系下 buffer 参数才表示米:
gdf_buf = gdf_proj.geometry.buffer(1000) # 1000 米缓冲区注意:如果直接用 EPSG:4326 做 buffer(1000),含义是 1000 度,结果会是一个覆盖全北京的巨大椭圆,这个坑在初学者里出现频率极高。
4. 北京市 shp 文件处理避坑:四个最常翻车的问题
4.1 dbf 中文乱码:GBK 与 UTF-8 的修复
现象:读取北京市 shp 文件后,区名、统计指标等中文字段全部显示为“锟斤拷”“�”之类的字符,字段名也可能是乱码。
原因:源数据生产单位的编辑环境多为 Windows 简体中文环境,dbf 属性表按 GBK 编码写入,但读取时默认按 UTF-8 解析;或者反过来,源数据是 UTF-8,却历史遗留地按 GBK 读取。
解决:读取时显式指定编码,先试 UTF-8 再试 GBK:
import geopandas as gpd # 方案一 gdf = gpd.read_file("北京市.shp", encoding="utf-8") # 方案二:如果还乱码 gdf = gpd.read_file("北京市.shp", encoding="gbk")如果文件名编码也乱,可以先对路径做处理。最稳妥的做法是确认压缩包里是否存在 .cpg 文件,用文本编辑器打开,里面写的UTF-8或GBK基本就是正确编码。真遇到 dbf 内字段值混排编码的情况,我会用pandas的apply配合str.encode("latin1").decode("gbk")逐列修复,但这个场景极少,不必过度设计。
4.2 坐标偏差:坐标系混用导致的地图漂移
现象:把北京市 shp 文件叠加到在线底图时,边界整体向某个方向偏移少则几十米多则数百米;或者同一片区在多个来源的数据里无法重合。
原因:最常见的是把缺 .prj 的数据默认当成 WGS84,而它实际是 CGCS2000 或更老的坐标成果;另一种是把互联网底图的 BD09 加密坐标直接套在标准数据上,偏差最夸张能到几百米。
解决:先做基准统一。如果确认源数据是 CGCS2000,但目标底图是 WGS84,直接转换即可,差值不大,忽略对多数业务影响可接受;如果涉及 BD09,必须先转成标准坐标再分析,一般有公开的转换算法库可以做近似解算。我的处理顺序是:查 .prj 文本 → 确认来源 → 用 ogrinfo 看 Extent → 再决定转换路径。不要看不出偏移就盲目猜,先打印坐标范围和几个已知地标点的经纬度验证。
4.3 拓扑错误:自相交报错与修复
现象:对北京市某个区县边界做 dissolve 或 buffer 时,报错TopologyException: found non-noded intersection或Self-intersection at or near point。
原因:源数据是早期人工矢量化或多源拼接而成的,相邻图斑的边界不是严格共享同一条弧段,存在微小交叉、重叠或未闭合节点。这些几何在软件里能显示,但在空间计算时就会被底层几何库判定为非法。
解决:先用 buffer(0) 做基础修复:
import geopandas as gpd gdf = gpd.read_file("北京市.shp", encoding="utf-8") # 修复自相交和部分细碎无效几何 gdf.geometry = gdf.geometry.buffer(0) gdf = gdf[~gdf.geometry.is_empty] # 再执行后续分析 dissolved = gdf.dissolve(by="name")buffer(0) 是经典的几何修复手段,对大多数自相交和重复节点问题有效,但对特别复杂的拓扑错误不一定能一次解决。还不行时,我用 GDAL 的 ogr2ogr 加-skipfailures跳过坏要素导出,再用 QGIS 的v.clean工具做拓扑清理。生产级处理不要只跑一次,要对每个几何单独验证is_valid属性。
4.4 字段名被截断与类型推断异常
现象:导入属性表后,“行政区名称”这类字段名会变成“行政区名”或“行政区”“名称”这种截断结果;面积、人口等数值字段被读成了文本对象,无法做数学运算。
原因:dbf 格式对字段名有 10 字节长度限制,中文字段名一个字符占多个字节,很容易被截断;同时 dbf 字段类型声明不够严格,数值字段可能被写入字符串类型。
解决:读取后手动重命名列,并对数值列强制转换类型:
import geopandas as gpd gdf = gpd.read_file("北京市.shp", encoding="gbk") # 把乱码或截断的列名映射为标准列名 gdf = gdf.rename(columns={"行政区": "name", "人口": "population"}) # 强制数值类型,带转换失败时用 errors='coerce' 设置为 NaN gdf["population"] = pd.to_numeric(gdf["population"], errors="coerce")更省心的做法是在数据源头就规避:让提供方在导出 shp 时使用英文字段名,比如name、pop、area,配合一份字段注释表单独交付。如果只能拿到中文字段名的老数据,就在导入脚本里维护 rename 映射,一次性把所有表结构理顺,不要把临时改动的列名带到下次任务里。
5. 让北京市 shp 文件真正可用:三个进阶落地技巧
5.1 按行政区拆分成单个 shp:groupby + 遍历写出
做区域专题时,往往需要北京市下辖每个区独立一个文件,而不是手工在 GIS 里逐个选中导出。用 groupby 遍历再逐个写出:
import geopandas as gpd gdf = gpd.read_file("北京市.shp", encoding="utf-8") for name, group in gdf.groupby("name"): safe_name = str(name).replace("/", "_") group.to_file(f"{safe_name}.shp", encoding="utf-8") print(f"已导出 {name},要素数 {len(group)}")文件名里有斜杠或特殊字符会导致写出失败,所以先做replace清洗。这份拆分脚本适合任何行政边界,不只是北京,价值在于用属性字段值自动生成文件名,省去手动选要素的时间。
5.2 边界简化:simplify 容差与投影单位
前端做地图可视化时,几万甚至几十万个顶点的边界文件会让页面卡顿。用 simplify 降低顶点数,但要注意容差参数在经纬度和投影坐标系下含义不同。投影坐标系下的容差单位是米,语义清晰:
import geopandas as gpd gdf = gpd.read_file("北京市.shp", encoding="utf-8") # 先转投影再简化,容差 100 表示 100 米级别的形状损失 gdf_proj = gdf.to_crs("EPSG:32650") gdf_simple = gdf_proj.copy() gdf_simple["geometry"] = gdf_proj.geometry.simplify(tolerance=100) # 导出前转回 4326 给 Web 底图用 gdf_simple = gdf_simple.to_crs("EPSG:4326") gdf_simple.to_file("北京市_simplify.geojson", driver="GeoJSON", encoding="utf-8")tolerance 参数要根据展示层级调整:全市宏观展示用 100 米没问题,到街道级就会看到边界明显变形,需要降低到 20 以下。简化后一定要抽查一下关键边界点位,比如行政区域交界处是否出现锯齿或串位。
5.3 数据一致性自查清单:一张表收尾
交付或入库前,我会按下面这个清单核对数据,避免下游同事或系统被同一批数据坑第二遍。
| 检查项 | 预期结果 |
|---|---|
| Feature Count 与行政单元数量一致 | 市级为 1,区县级与公开行政区划数量一致 |
| 几何集合无空面、无空属性 | 无 geometry 为 None 的记录 |
| 属性无乱码 | 抽查区名字段显示正常 |
| 坐标系明确 | prj / crs 字段不等于 None |
| 面积与统计口径量级相符 | 市级面积量级在 1 万平方公里左右对应 10^10 平方米 |
| 边界范围合理 | Extent 在北京市经纬度合理范围内 |
这几项全过,数据基本可以放心给前端或者下游分析用。
最后说点经验:这些技巧我不是第一次面对北京市 shp 文件时才想起的,而是每次都被同一类问题反复提醒。早期我在某跨平台可视化项目里,因为漏了投影转换,一圈边界跑出来是一根横跨地图的长条,排查到凌晨才发现是 buffer 单位的问题。从那以后,我处理任何 shp 文件都先打印 crs、检查编码、验证 isValid,这三步几乎能挡掉 80% 的异常数据。希望这篇笔记里的排查顺序和参数选择,也能帮你少熬一个夜。
本文还有配套的精品资源,点击获取