简介粤港澳大湾区各街道各镇shp文件是一套精确到乡镇街道级别的行政区划矢量数据覆盖广州、深圳、珠海、佛山、惠州、东莞、中山、江门、肇庆9个内地城市及香港、澳门面向GIS开发者、规划分析师与科研人员可用于地图绘制、空间统计、区域规划与人口分布研究。压缩包共36个文件约4.36MB以shp几何数据、dbf属性数据、prj坐标系信息和shx索引文件为主体另有sbn/sbx空间索引、XML元数据及一张示意图片可直接在ArcGIS、QGIS中打开和编辑。目前已有1179人学习下载足见其在大湾区研究中的实用价值。数据内含街道镇边界与区县等多个图层支持叠加交通、GDP等属性进行空间分析也可用于商业选址、环境影响评估等场景同时提供坐标参考信息便于在CGCS2000与WGS84之间转换以匹配不同数据集。这套文件省去了多方搜集、格式整理和坐标校正的麻烦能为精细化的湾区空间分析直接提供可靠基础。1. 直接拿大湾区街镇SHP做分析前先把这三个问题想清楚做县域经济对比、医院选址、商业网点网格化评估这类活儿时最尴尬的不是缺方法而是手里只有一张市界SHP——分析结果粗糙到只能写进PPT当背景图根本没法落进业务决策。粤港澳大湾区各街道各镇SHP文件要解决的正是这个颗粒度问题把大湾区珠三角九市范围内街道、镇两级行政边界拿到手让分析落得到“哪个镇、哪条街”这一层。这篇文章写给三类人一是做城市规划、人口分布、产业分析的一线GIS工程师二是要在业务系统里接入边界底图的开发三是刚接触SHP格式、被“下载了文件却打不开或坐标系乱飞”折磨过的研究者。我会把数据源选型、清洗加工、坐标统一这些必经环节拆开讲重点放在能直接复现的处理流程和参数设置上。先说结论大湾区街镇SHP文件本身不值钱值钱的是你拿到手之后怎么验证边界完整性、统一坐标系、整理属性字段这三步。这三步没做后面画的每张图、算的每个指标都可能翻车。2. 大湾区街道镇SHP的数据源选型我在哪里找、各有什么坑2.1 四个常见获取路径按推荐程度排序做项目这些年我拿大湾区街镇边界SHP的路径主要有四条按省心程度排序如下。第一条是单位采购或项目已有的商业GIS数据包。比如测绘院、规划院出品的“全国行政区划一张图”里面行政区划代码、边界精度、属性字段都校对过街道和镇的层级关系是完整的。这类数据基本不存在“打开是废的”这种问题缺点是得走采购流程而且部分老批次数据只更新到某个年份新设立的街道得自己补。第二条是从国家或省级地理信息公共服务平台下载公开版。常见做法是去标准地图服务里找审图通过的行政区划界线拿到后按区县范围裁切、合并成湾区九市两区的街镇图层。优点是合规性最好缺点是公开版比例尺偏小边界精度到不了详查级别而且很多平台给的是GeoJSON或图片需要一次格式转换。第三条是自己从开放数据社区或高校GIS数据资源共享站点找。网上确实有整理好的“粤港澳大湾区乡镇街道级行政边界SHP打包”这类资源文件结构往往是.shp/.shx/.dbf/.prj齐全。这类资源适合做练习和技术验证拿来做正式的汇报材料或对外发布成果前务必确认审批要求和审图号——这一点放到第四章避坑部分专门说。第四条是从统计年鉴或人口普查资料附带的行政区划简表反推。把街镇名称、行政区划代码抓下来然后跟一份基础地图做属性关联用代码字段Geometry按代码挂接。这个方法能救急但代码到图形的对应关系偶尔有错位批量挂接后必须抽检。2.2 为什么我不建议“解压即用”三个必须正视的问题很多人从网上下载了“大湾区街镇SHP”解压看到.shp、.dbf几个文件齐了就认为完事了。我曾经也这么干过——结果用Table Join按行政区划代码关联人口数据时一个街道的边界里套着另一街道的飞地画出来的专题图直接闹笑话。“解压即用”会踩的第一个问题是时效性。大湾区的街道调整相当频繁前几年还叫“某某新区”的片区可能已经拆成三个街道或者并进了隔壁镇。SHP文件里图形边界是旧的属性表里名字是新的这种“图属不一致”很隐蔽不逐镇核对根本发现不了。第二个问题是边界精度来源混杂。同一个“大湾区街镇SHP”文件里可能是A区来自测绘院详查、B区来自公版地理数据、C区是别人的项目数据转手出来的。肉眼看着都是闭合面叠到一起做面积统计时临界的镇与镇之间不是重叠就是留缝面积汇总能差出百分之几。做人口密度计算时这个错差直接放大到结论上。第三个问题是坐标系标注不可信。不少下载包里的.prj虽然写着“GCS_WGS_1984”但数据实际是从在线地图工具里导出、偏移过坐标系的。要识别这一点技术手段是拿同一位置的已知点做比对误差在百米量级就是有问题。这个问题很典型放到第四章第3个坑单独展开。注意任何来源的大湾区街镇SHP拿回来后先用本章2.1里的路径问一遍自己——数据是怎么来的、边界精度是否满足分析尺度、是否允许对外发布。这三问过了再谈清洗。3. 用Python把大湾区街镇SHP清洗成可入库底图从原始文件到能用3.1 数据体检读入、看字段、查坐标系拿到任何一份大湾区街镇SHP文件我的第一步永远不是急着叠加分析而是先体检。体检看四件事几何类型对不对必须是Polygon不是Polyline或Point不是MultiPatch、坐标系是什么、属性表里有没有GID/行政区划代码这类关键字段、要素数量跟官方街镇数量对不对得上。体检用Python的geopandas最顺手。下面这段代码做了三件事读入SHP、打印坐标系、统计要素数并按字段预览前五行。import geopandas as gpd # 读入SHP文件注意路径不要带中文避免部分环境读取失败 gdf gpd.read_file(D:/gis_data/gba_street/gba_street.shp, encodingutf-8) # 体检四项几何类型、坐标系、要素数、字段列表 print(几何类型:, gdf.geom_type.unique()) print(坐标系:, gdf.crs) print(要素数:, len(gdf)) print(字段:, gdf.columns.tolist()) # 重点看名称字段和区划代码字段判断字段名规范程度 print(gdf[[name, code]].head())这段代码的逻辑说明geopandas读SHP时最重要的参数是encoding因为DBF属性表的历史编码问题常见有utf-8和gbk两种读成乱码就换另一个试。geom_type.unique()能直接暴露出数据是纯面还是混入了线或点混入的会在后续合并操作里报奇奇怪怪的错。要素数要心里有数珠三角九市加上街道镇数量大概是千级如果你读出来只有几十个那八成是数据不完整。3.2 统一坐标系WGS84、GCJ-02和投影坐标必须分清体检完紧接着是坐标系的统一。大湾区街镇SHP最常见的三种坐标系情况WGS84地理坐标、GCJ-02火星坐标、以及CGCS2000或WGS84 UTM投影坐标。你要做的判断是这份数据到底在哪个坐标系里然后统一转成你后续分析需要的那个。WGS84和GCJ-02的问题最磨人。GCJ-02是中国国测局发布的加密坐标系统在线地图的轨迹导出来基本都是它。如果一份SHP里.prj标注是WGS84但点位落在真实位置的东偏约几百米那它基本就是GCJ-02或类似偏移坐标。我的处理逻辑如下import geopandas as gpd gdf gpd.read_file(D:/gis_data/gba_street/gba_street.shp, encodingutf-8) # 假设通过抽点比对确认这份数据是GCJ-02坐标先转成WGS84再继续用 # 用第三方库coord_convert做火星坐标转WGS84这一步不做后面所有面积、距离都会出错 try: from coord_convert.transform import gcj2wgs except ImportError: print(缺少coord_convert库先安装pip install coord_convert) def gcj_to_wgs(row): # 回调里逐点转换几何坐标 import shapely.geometry if row.geometry.geom_type Polygon: new_coords [ [gcj2wgs(x, y) for x, y in ring.coords] for ring in row.geometry.interiors [row.geometry.exterior] ] return shapely.geometry.Polygon(new_coords[0], new_coords[1:]) else: return row.geometry # 应用到所有行并把坐标系声明为WGS84 gdf[geometry] gdf.apply(gcj_to_wgs, axis1) gdf gdf.set_crs(epsg4326)参数说明gcj2wgs函数接收经纬度坐标返回的是修正后的WGS84坐标。处理Polygon时不能只转换外环内环岛、飞地之类的洞也要一起转否则图形会变形。转换完务必set_crs(epsg4326)声明坐标系这一步属于“后悔药”——你不在元数据里写清楚下一个接手的人就不知道数据处在什么状态。如果你要做面积计算、距离量算、或者跟栅格数据叠加建议统一再转一次投影坐标系。广州、深圳、东莞这一带属于UTM 49N或者CGCS2000 3度带用地理坐标算面积会出现平方米量级的偏差看似不大但在统计汇总里容易跟别人差出一个版本。3.3 字段规范化行政区划代码、名称和层级的一次性整理坐标系干净了接下来整理属性表。大湾区街镇SHP数据最常出现的属性表问题是字段名五花八门有的是NAME有的是XZQMC还有英文学的ST_NAME。以及行政区划代码在DBF里被读成了浮点数——12位代码变成科学计数法后面的“0”全丢了。我一般会把字段重命名成一套固定规范code存行政区划代码、name存名称、level存街道或镇的类别然后按代码长度统一字符串格式。实测代码import geopandas as gpd gdf gpd.read_file(D:/gis_data/gba_street/gba_street.shp, encodingutf-8) # 字段重命名先看有哪些列再做映射 gdf gdf.rename(columns{ XZQHDM: code, XZQMC: name, XZJD: level }) # 行政区划代码必须补零成字符串避免被当成数字截断 gdf[code] gdf[code].astype(str).str.zfill(12) # 街道和镇的类型区分通常代码第4-6位是区县再往后是乡镇街道编码 gdf[level] gdf[code].str[6:9] gdf[level] gdf[level].map({ 100: 街道, 200: 镇, }) # 检查是否有未匹配的类型 print(gdf[level].value_counts(dropnaFalse))这里的关键是str.zfill(12)行政区划代码是12位字符前6位省市区县、中间3位乡镇街道、后3位村居。SHP的DBF字段存数字会丢前导零所以在读入后第一时间转字符串。level的判断逻辑是编码规则100代表街道、200代表镇如果出现其他值说明数据来源不太正规需要回去核对。3.4 几何检查空几何、自相交、以及跨区飞地字段整理完继续做几何层面的质检。这个环节最容易暴露数据源拼接的问题。import geopandas as gpd from shapely.validation import make_valid gdf gpd.read_file(D:/gis_data/gba_street/gba_street.shp, encodingutf-8) # 1. 空几何检查有几行geometry是空的直接删除或标记 empty_mask gdf.geometry.is_empty | gdf.geometry.isna() print(空几何数量:, empty_mask.sum()) # 2. 自相交检查无效几何会引起面积计算异常和拓扑报错 invalid_mask ~gdf.geometry.is_valid print(无效几何数量:, invalid_mask.sum()) # 3. 自相交修复用make_valid把自相交面拆成MultiPolygon gdf[geometry] gdf.geometry.apply(lambda geom: make_valid(geom) if not geom.is_valid else geom)参数说明make_valid来自Shapely 2.x能把自相交的环自动修复为有效几何这是最常见的“翻车后第一刀”。但它可能把单个Polygon变成MultiPolygon后续用area计算没问题导到PostGIS或GeoPackage里要注意类型转换。飞地的检查不能靠代码一步到位要靠空间关系判断把每个镇的核心面积算出来如果某个要素的面积跟真实情况差太多或者它的质心落在另一个区县的边界范围内就要人工拉出来排查。湾区几个镇级飞地是历史遗留问题的结果数据里保留着但容易引起误读。分析前你得决定保留还是剔除并在文档里写清楚。3.5 合并与导出把碎片SHP拼成一个完整湾区文件最后一步是合并和导出。如果你手里的SHP是每个区一个文件先把它们读进一个列表再合并import glob import geopandas as gpd # 读入某个目录下所有区县街镇SHP file_list glob.glob(D:/gis_data/gba_by_district/*.shp) frames [] for file in file_list: # 注意每个文件单独指定编码否则GBK编码的文件会读乱 frames.append(gpd.read_file(file, encodingutf-8)) gdf_all gpd.concat(frames, ignore_indexTrue) # 导出为GeoPackage替代SHP多文件结构避免DBF字段名截断和文件大小限制 gdf_all.to_file(D:/gis_data/gba_street.gpkg, layerstreet, driverGPKG, encodingutf-8)这里我推荐导出为.gpkg而不是.shp。SHP格式有单文件2GB上限、字段名10字符限制、属性编码混乱的老毛病GeoPackage没有这些历史包袱体积小还支持空间索引。如果你的下游系统只收SHP那导出时记得同时保留同名字的.prj文件否则坐标系信息会丢别人打开全是乱位。注意合并前务必确认各分片数据源的坐标系一致且经历过上一节的处理。不一致的坐标系直接concat结果是全区图形错位且难以追溯。4. 街镇SHP处理中常见的六个坑现象、原因、解决4.1 行政区划代码从数字变成科学计数法现象打开属性表12位行政区划代码显示成“440112000000”变成了“4.40112E11”关联数据时匹配不上。原因DBF属性表字段类型比较老整型数字存不了长整型或者你在Excel里打开过SHP的DBF并把列设成了“常规”前导零被吃掉。解决别用Excel直接编辑SHP的DBF。在geopandas里读入后立刻astype(str).str.zfill(12)补零。如果数据已经坏了从原始SHP重新读不要拷已经打开过的DBF。4.2 街道与街道之间出现缝隙或重叠现象把湾区各区的街镇SHP拼到一起放大看到相邻镇边界没有完美贴合有些地方年久失修似的一条裂缝有些地方互相重了。原因相邻行政区来自不同测绘批次各自独立修测边界线没有做拓扑一致性处理。这是拼图式SHP的通病。解决在ArcGIS或QGIS里做拓扑检查或者用Python的overlay操作提取重叠部分。当重叠面积小于该镇面积的5%时我一般直接按“以面积更大的那个要素为准”处理把重叠区域归属过去。缝隙则用相邻要素的边界线做贴合或放弃修复、在图上标注“示意边界”。4.3 坐标系标注是WGS84实际是GCJ-02现象SHP的.prj文件里写着GCS_WGS_1984但把数据叠加到同一份WGS84底图上所有街镇整体往东南方向偏移三四百米。原因数据是从在线地图平台导出的平台自带火星坐标偏移但导出工具把元数据写成了WGS84。解决先抽三个已知坐标点比如政府驻地、地标建筑跟影像底图对比误差超100米基本就是GCJ-02。用gcj2wgs批量转回来再set_crs(epsg4326)。这个坑几乎每三个下载包里一个早发现比晚发现省事得多。4.4 中文路径导致SHP打不开或属性乱码现象把SHP放在桌面“大湾区数据”文件夹里用代码读取报“file not recognized”或者打开后字段名全是乱码。原因部分GDAL/OGR版本对中文路径支持不好DBF的编码声明历史上也混乱常见有GBK、UTF-8、ANSI三种。解决路径统一改成英文数字组合比如D:/gba_data/street_shp。编码方面读入时不指定encoding让geopandas用默认的utf-8试一次乱码就改成gbk。导出的文件同理指定encodingutf-8并保留.cpg文件。4.5 要素数超10万或文件超2GBSHP扛不住现象合并整个大湾区的街镇SHP并做了大量属性字段关联后保存时GDAL报layer has no features或者写出的SHP在ArcGIS里打不开。原因SHP包含多个子文件.dbf的单文件上限是2GB字段记录数也受历史格式约束要素过多或字段过长会突破结构限制。解决换输出格式。GeoPackage是首选PostGIS更好如果业务系统强依赖SHP按地市拆分存储、通过空间索引查询拼接。4.6 业务系统加载失败字段名超过10个字符现象做GIS二次开发时用代码加载SHP进Web端地图服务前端一直报解析错误但桌面GIS打开一切正常。原因SHP的DBF字段名字段名限制是10字节数据源里有admin_district_code这种超长字段名时某些实现会拒绝或者截断。解决在清洗阶段把所有字段名统一改成短英文code、name、level中文字段名不要出现在SHP里。做二次开发时属性字段名越短越不容易踩序列化异常。5. 从SHP到能出图的分析成果一个可复现的最小工作流5.1 叠加人口或POI数据用一个具体指标演示清洗完的大湾区街镇SHP最典型的用途是算“每平方公里人口密度”或者“每万人商业设施数量”。我这里演示一套完整的最小工作流给街镇面接上人口数算出密度并做简单可视化。人口数据你可以用手头的七普分镇数据或统计年鉴数据。import geopandas as gpd import matplotlib.pyplot as plt # 读清洗后的街镇底图 gdf gpd.read_file(D:/gis_data/gba_street.gpkg, layerstreet) # 读人口数据CSV按行政区划代码关联 import pandas as pd pop_df pd.read_csv(D:/gis_data/gba_street_pop.csv, dtype{code: str}) gdf gdf.merge(pop_df, oncode, howleft, validate1:1) # 确保坐标系为投影坐标系再算面积这里用Azimuthal Equidistant近似 gdf_proj gdf.to_crs(projaea lat_120 lat_223 lat_00 lon_0114 datumWGS84) gdf_proj[area_km2] gdf_proj.geometry.area / 1e6 gdf_proj[pop_density] gdf_proj[pop] / gdf_proj[area_km2] # 简单出图不要在意美观先看分布是否合理 fig, ax plt.subplots(figsize(10, 10)) gdf_proj.plot(columnpop_density, cmapOrRd, legendTrue, axax, edgecolorgray, linewidth0.2) plt.axis(off) plt.savefig(D:/gis_data/gba_density.png, dpi150)代码逻辑说明先按code关联人口数据validate1:1是防止一对多匹配出重复行这是关联最容易翻车的点。面积计算前必须转投影坐标系我用的是Albers等积投影参数按大湾区纬度范围设的中心经度114。用地理坐标的area出来的值不对这是新手最容易忽略的一步。5.2 SHP转GeoJSON或3D Tiles给前端和三维场景用底图做完了业务系统里通常还要转格式。最常见的两个场景WebGIS用GeoJSON三维可视化用3D Tiles。转GeoJSON很简单geopandas一行搞定gdf_proj.to_file(D:/gis_data/gba_street.geojson, driverGeoJSON, encodingutf-8)注意GeoJSON默认基于WGS84导出前如果gdf_proj还是投影坐标系里面存的坐标会是一堆很大的数值前端加载不识别。所以要再转回4326再导出gdf_proj.to_crs(epsg4326).to_file(D:/gis_data/gba_street.geojson, driverGeoJSON, encodingutf-8)三维场景用的3D Tiles我用得比较多的是通过CesiumLab或开源工具把SHP/GeoJSON转成b3dm输入前同样要求要素带高度字段或者按行政区名称做拉伸。街镇SHP本身是平面边界转3D Tiles时需要给高度属性用于做“按人口密度拉伸”这类可视化。这个过程有个常见坑SHP里的MultiPolygon在转换工具里会丢失部分面或报格式错误通常先把MultiPolygon拆成单个Polygon再带父子关系转换。拆多边形的代码很简单gdf gdf_proj.explode(index_partsTrue)explode会把每个MultiPolygon拆成独立行代价是属性重复一遍但三维切片工具处理起来就顺了。5.3 验证结果一套自己跟自己过不去的检查清单我觉得一个SHP成果交付前至少要过这三关。第一关是数量关输出的街镇要素数跟你按官方行政区划统计的数量误差在±5个以内。误差多了要么是边界问题要么是属性匹配时把街道和社区搞混了。第二关是边界关拿AAAA级景区或重要地标做定位抽查把SHP边界叠加在线影像底图上肉眼确认街镇边界走向与地形地物基本一致。这个检查不费多少时间但对坐标偏移类问题一票否决。第三关是数值关用投影坐标系算出每个镇的陆地面积跟统计年鉴里的行政区域面积对比差的超过10%就说明底图里混入了飞地或边界严重不准。这三年我用这个检查清单卡掉了不少看起来能用的数据。上个月同事发来一份“大湾区街镇SHP”让我直接出图我说先跑一遍三关结果在数量关就发现少了一个新设街道后来补上再出图分布结论完全变了。数据驱动的前提是数据真的可靠。希望你把自己的SHP底图也当成一个待交付的系统来做不是下载完就结束了。把本章的清洗流程跑一遍、避坑清单过一遍、验证三关走一遍大湾区街镇SHP这活儿基本就不会出篓子。本文还有配套的精品资源点击获取