简介:这份资源面向从事城市规划、交通管理、环境研究及区域经济分析的GIS从业者与科研人员,提供珠三角(粤港澳大湾区)各地级市的基础地理空间数据,可用于行政边界制图、空间叠加分析与区域发展研究。压缩包共8个文件,约30KB,以Shapefile标准格式组织,包含shp几何主文件、dbf属性表、shx索引、prj投影定义,以及sbn、sbx空间索引和xml元数据,另附一张jpg预览图,可直接在ArcGIS、QGIS等软件中加载使用。目前已有884人学习下载,说明该数据在区域研究场景中具有一定参考价值。借助这套数据,读者可快速搭建珠三角空间底图,开展城市扩张、交通网络、水系分布等专题分析,为规划决策与科研建模提供基础支撑。
1. 珠三角地级市 shp 文件到底装了什么:从一份 rar 说起
拿到「珠三角各地级市 shp 文件.rar」这种资源,第一反应不该是解压看热闹,而是先想清楚它到底能干什么。珠三角包含广州、深圳、佛山、东莞、中山、珠海、惠州、江门、肇庆九个城市,一份按地级市切分的 shp 文件,本质上是把行政边界做成了矢量面数据,每个面要素带一个城市名或行政代码字段。它能直接用于专题制图、空间统计、缓冲区分析、路网叠加,也是做城市级数据可视化最省事的基础底图。适合谁?做区域经济分析的研究生、要给客户出图的 GIS 从业者、想拿真实边界练手空间分析的开发者。但 rar 这个壳子会带来一串问题:解压后坐标系对不对、字段编码乱不乱、边界有没有拓扑错误,这些才是决定这份数据能不能用的关键,而不是文件本身有多大。
2. 解压之后先别急着加载:shp 文件组的构成与坐标系判断
2.1 shp 从来不是单个文件,少一个都打不开
很多人以为 shp 就是一个文件,解压完看到一堆同名不同后缀的文件就懵了。实际上 Shapefile 是 ESRI 定义的一套多文件格式,至少需要三个文件同时存在才能被正确读取:
| 后缀 | 作用 | 缺失后果 |
|---|---|---|
| .shp | 存储几何图形(点线面坐标) | 完全无法加载 |
| .shx | 几何图形的索引 | 部分软件报错或加载异常 |
| .dbf | 存储属性表(城市名、代码等) | 能显示图形但无属性 |
| .prj | 存储坐标系定义(WKT 文本) | 坐标未知,无法叠加 |
| .cpg | 指定 dbf 的字符编码 | 中文城市名可能乱码 |
从 rar 解压出来如果只有 .shp 一个文件,那这份数据基本是残的。我一般会先列一遍目录,确认每个城市都凑齐了这五个文件。珠三角九个城市,正常应该有九组,每组五个,共四十五个文件左右。如果发现某个城市缺 .prj,那它的坐标就是黑匣子,后面叠加分析必翻车。
2.2 坐标系不确认,后面所有分析都是白做
珠三角的行政边界数据,常见坐标系有三种:WGS84 地理坐标(EPSG:4326)、CGCS2000 地理坐标(EPSG:4490)、以及各种投影坐标如 CGCS2000 3度带高斯克吕格。判断方法很简单,用 Python 读一下 .prj 或者直接看坐标数值范围:
import geopandas as gpd # 读取一个城市的 shp,先看坐标系和范围 gdf = gpd.read_file("广州市.shp") print("CRS:", gdf.crs) # 看坐标系定义 print("Bounds:", gdf.total_bounds) # 看坐标范围 print("Columns:", gdf.columns.tolist()) # 看属性字段 print("Rows:", len(gdf)) # 看要素数量如果 total_bounds 输出的是 112 到 115、22 到 24 这种小数,说明是地理坐标(经纬度),单位是度。如果输出的是几十万到几百万的整数,说明是投影坐标,单位是米。这两种不能直接混用,地理坐标下算面积得到的是平方度,毫无意义。我一般会统一转成投影坐标再做面积和距离计算:
# 如果原始是地理坐标,转到适合珠三角的投影坐标系 # CGCS2000 3度带,中央经线114度对应 EPSG:4547 if gdf.crs and gdf.crs.is_geographic: gdf_proj = gdf.to_crs(epsg=4547) gdf_proj["area_km2"] = gdf_proj.geometry.area / 1e6 print(gdf_proj[["城市名", "area_km2"]])这里 EPSG:4547 是 CGCS2000 3度带中央经线114度E的投影,珠三角大部分落在这个带内,面积计算误差可以接受。如果你的数据本身已经是投影坐标,就不要重复转,先看 .prj 里的 PROJCS 字段确认。
2.3 属性表字段与编码:中文城市名乱码的根因
dbf 文件默认编码在不同软件里不一样,ArcGIS 老版本默认 GBK,QGIS 和 GDAL 默认 UTF-8。如果 .cpg 文件缺失或写错,打开后城市名会变成问号或乱码。检查方法:
# 指定编码读取,先试 UTF-8,不行再试 GBK try: gdf = gpd.read_file("广州市.shp", encoding="utf-8") except UnicodeDecodeError: gdf = gpd.read_file("广州市.shp", encoding="gbk") # 看属性表前几行,确认城市名字段是否正常 print(gdf.drop(columns="geometry").head())如果字段名本身也是乱码,那说明 dbf 的表头编码也有问题,这种情况用 GDAL 的 ogrinfo 命令行工具看一眼更直接:
ogrinfo -al -so 广州市.shp这条命令会输出图层名、几何类型、要素数、字段定义和坐标系,不加载图形,速度很快。确认字段名和编码没问题后,再决定是否需要重命名列或统一字段结构。
3. 把九个城市的 shp 合成一张珠三角底图
3.1 批量读取与合并的三种方式对比
拿到九个独立 shp,做区域分析时通常需要合并成一个图层。常见做法有三种:geopandas 的 concat、GDAL 的 merge、以及 QGIS 里手动合并。我一般用 geopandas,因为可控性最强:
import geopandas as gpd import pandas as pd from pathlib import Path # 假设九个城市的 shp 放在 data 目录下 data_dir = Path("data") shp_files = sorted(data_dir.glob("*.shp")) # 逐个读取,统一坐标系后合并 gdfs = [] for f in shp_files: g = gpd.read_file(f, encoding="utf-8") if g.crs and g.crs.is_geographic: g = g.to_crs(epsg=4547) g["source_file"] = f.stem # 记录来源,方便排查 gdfs.append(g) merged = gpd.GeoDataFrame(pd.concat(gdfs, ignore_index=True), crs=gdfs[0].crs) print("合并后要素数:", len(merged)) print("城市列表:", merged["source_file"].unique())这段代码的关键点:先统一坐标系再合并,否则不同坐标系的几何叠在一起会错位到离谱;加一个 source_file 字段记录来源,后面发现某个城市边界异常时能快速定位;合并后重新指定 crs,因为 concat 有时会丢失坐标系信息。
3.2 合并后必做的拓扑检查与修复
九个城市的边界拼在一起,最容易出现的问题是相邻城市之间有缝隙或重叠。缝隙会导致区域统计时面积偏小,重叠会导致同一块地被算两次。检查方法:
# 检查几何有效性 invalid = merged[~merged.geometry.is_valid] print("无效几何数量:", len(invalid)) # 如果有无效几何,尝试修复 if len(invalid) > 0: merged["geometry"] = merged.geometry.buffer(0) print("修复后无效数量:", len(merged[~merged.geometry.is_valid])) # 检查总面积是否合理(珠三角陆地面积约5.5万平方公里) total_area = merged.geometry.area.sum() / 1e6 print(f"总面积: {total_area:.0f} 平方公里")buffer(0) 是修复自相交多边形的经典手法,但它会轻微改变边界形状,对精度要求极高的场景要谨慎。总面积如果明显偏离五万多平方公里,说明要么坐标系不对,要么边界有严重重叠。我一般会再算一下各城市面积之和与合并后总面积的差值,差值超过百分之一就要查原因。
3.3 导出为 GeoJSON 和 GPKG 的取舍
合并完的成果,如果只在 GIS 软件里用,存成 GPKG 最省事,单文件、支持中文、保留坐标系。如果要给前端或非 GIS 同事用,GeoJSON 更通用但文件会大不少:
# 导出 GPKG,适合后续 GIS 分析 merged.to_file("珠三角_合并.gpkg", driver="GPKG") # 导出 GeoJSON,适合 Web 端或跨平台交换 merged.to_file("珠三角_合并.geojson", driver="GeoJSON") # 如果只需要城市名和几何,可以精简字段后再导出 merged[["source_file", "geometry"]].to_file("珠三角_精简.geojson", driver="GeoJSON")GPKG 的优势是单文件、支持空间索引、读写快,缺点是部分老版本 GIS 软件不认。GeoJSON 的优势是纯文本、任何语言都能解析,缺点是文件体积大、不支持空间索引。我一般两个都导,GPKG 自己用,GeoJSON 给别人。
4. 珠三角 shp 常见踩坑与排查记录
4.1 解压后中文文件名乱码导致读取失败
现象:解压出来的文件名显示为乱码,Python 的 glob 匹配不到 .shp 文件。原因:rar 压缩时用了非 UTF-8 的文件名编码,在部分系统上解压后文件名编码错乱。解决:用 7-Zip 或 Bandizip 解压,在设置里指定文件名编码为 GBK 或自动检测;如果已经解压乱了,用 Python 的 os.rename 批量重命名:
import os from pathlib import Path # 列出目录下所有文件,手动确认乱码规律后批量重命名 for f in Path("data").iterdir(): if f.suffix == ".shp" and "乱码特征" in f.name: new_name = f.name.replace("乱码特征", "正确名称") f.rename(f.with_name(new_name))4.2 坐标系定义缺失但坐标数值是投影坐标
现象:.prj 文件缺失,但坐标数值是几十万的大数,说明是投影坐标但不知道具体参数。原因:数据制作者导出时漏掉了 .prj,或者故意删除了。解决:根据坐标范围反推中央经线。珠三角经度约在 112°E 到 115°E,3度带中央经线可能是 114°E,对应 EPSG:4547;也可能是 6度带中央经线 117°E,对应 EPSG:4499。用 geopandas 试两个坐标系,看哪个的 total_bounds 换算回经纬度后落在珠三角范围内:
# 假设原始坐标数值在 500000 左右 for epsg in [4547, 4548, 4499]: try: g = gpd.read_file("未知坐标系.shp") g = g.set_crs(epsg=epsg, allow_override=True) g_wgs = g.to_crs(epsg=4326) bounds = g_wgs.total_bounds print(f"EPSG:{epsg} -> 经度{bounds[0]:.2f}~{bounds[2]:.2f}, 纬度{bounds[1]:.2f}~{bounds[3]:.2f}") except Exception as e: print(f"EPSG:{epsg} 失败: {e}")哪个输出的经纬度落在 112~115、22~24 之间,就是对的。
4.3 相邻城市边界不吻合导致面积统计偏差
现象:合并后总面积比官方公布的大或小百分之几。原因:各城市 shp 来自不同来源,边界采集精度不一致,或者坐标系转换时产生了偏移。解决:先确认所有城市用的是同一坐标系、同一精度级别;如果边界仍有缝隙,用 shapely 的 unary_union 做一次融合再重新切分,或者直接用融合后的外边界做区域统计,不依赖各城市独立面积之和。
4.4 dbf 字段名截断导致属性丢失
现象:读取后字段名变成 NAME_1、NAME_2 这种,或者中文城市名字段消失。原因:dbf 格式对字段名长度有限制(最多10个字符),导出时被截断。解决:在源头用 GPKG 或 GeoJSON 替代 dbf 存储属性,或者在读取后根据数据字典手动映射回正确字段名。如果字段值本身还在,只是名字被截断,用 rename 改回来即可。
4.5 用 rar 密码移除工具处理来源不明的压缩包
现象:下载的 rar 需要密码才能解压,网上搜到各种 rar 密码移除工具。原因:资源分享者加了密码防止随意传播。解决:优先联系分享者获取密码,而不是用破解工具。来源不明的压缩包本身就有安全风险,破解工具更是重灾区。如果实在拿不到密码,换一个公开的边界数据源,比如从开放平台获取标准行政边界,比冒险解压来路不明的文件靠谱得多。
5. 从珠三角底图到可复用的城市分析模板
把九个城市的 shp 合并成一张底图只是起点,真正省时间的是把它做成一个可复用的分析模板。我一般会封装一个函数,输入城市名列表,输出裁剪好的分析范围:
def get_prd_boundary(city_names=None, buffer_km=0): """ 获取珠三角指定城市的边界,可选外扩缓冲区 city_names: 城市名列表,None 表示全部九个城市 buffer_km: 外扩距离,单位公里,0 表示不扩 """ gdf = gpd.read_file("珠三角_合并.gpkg") if city_names: gdf = gdf[gdf["source_file"].isin(city_names)] if buffer_km > 0: # 投影坐标下 buffer 单位是米 gdf["geometry"] = gdf.geometry.buffer(buffer_km * 1000) return gdf # 用法:获取广州和佛山外扩5公里的范围 area = get_prd_boundary(["广州市", "佛山市"], buffer_km=5) print(area.total_bounds)这个模板的价值在于,后面做任何空间分析——比如统计某个区域内的人口、计算路网密度、做选址评估——都可以直接调用它拿到统一的分析范围,不用每次重新处理边界。缓冲区参数在投影坐标下单位是米,所以 buffer_km 要乘 1000,这个细节不注意就会得到一个大得离谱的范围。
另一个实用技巧是给合并后的底图加一个面积字段和中心点字段,方便后续做标注和排序:
merged["area_km2"] = merged.geometry.area / 1e6 merged["centroid"] = merged.geometry.centroid merged["cx"] = merged["centroid"].x merged["cy"] = merged["centroid"].y # 按面积排序看看哪个城市最大 print(merged[["source_file", "area_km2"]].sort_values("area_km2", ascending=False))centroid 在投影坐标下计算才准确,地理坐标下算出来的中心点会有偏差。这些字段导出后,在 QGIS 里可以直接用来做标注定位,不用再手动算。
最后说一个我踩过的坑:早期做珠三角分析时,我直接拿地理坐标的 shp 算面积,结果九个城市面积加起来只有零点几,还以为是数据有问题,排查了半天才发现是单位问题。从那以后,我养成了一个习惯——拿到任何 shp,先看坐标系,再看坐标范围,最后才动手分析。这个顺序不能反,反了就是白干。希望帮到你。
本文还有配套的精品资源,点击获取