省级道路SHP数据包处理指南:从坐标转换到货车限行路径预筛选

📅 发布时间:2026/10/13 2:19:04
省级道路SHP数据包处理指南:从坐标转换到货车限行路径预筛选
简介这份2024年广东省道路矢量数据包面向交通规划、物流优化、应急建模与区域经济研究等场景的GIS从业者与研究人员提供开箱即用的全省路网底图。数据以标准Shapefile格式组织覆盖全省各地级市及下辖区县包含高速公路、国道、省道、县道、乡道与城市主干道等多级分类每条道路附带名称、等级、编号等属性字段总计超三十万条线要素坐标系统一采用WGS1984地理坐标系兼容ArcGIS、QGIS、SuperMap等主流平台。资源包共10个文件以shp、shx、dbf、prj、cpg等Shapefile核心组件为主另附py转换脚本、geojson示例与txt说明压缩包约202.29MB。包内shp2.py脚本可将数据快速转为GeoJSON便于Web地图开发与前端调用。目前已有53人学习下载适合需要高质量路网底图进行空间分析、路径计算与可视化呈现的读者直接取用。1. 拿到一份省级道路 SHP 数据包先别急着往 GIS 里拖上周有个做物流路径规划的朋友甩过来一个压缩包说“广东全省的路网高速国道省道县乡道全有WGS84 的你帮我看看能不能直接算货车限行”。我解压一看目录里躺着四个 SHP 文件外加 .dbf、.shx、.prj 一整套。这种“省级全量道路矢量数据包”在路网分析、导航电子地图底图、城乡配送范围测算里属于硬通货——它把高速、国道、省道、县乡道按行政等级拆成独立图层坐标系统一为 WGS84 地理坐标系意味着你拿到手就能跟 GPS 轨迹、POI 点位、行政区划面做空间叠加不用先折腾坐标转换。适合谁做交通流量模拟的、搞配送时效估算的、给乡镇网点做选址的以及需要一份干净路网底图做可视化的。但先别急着往 QGIS 或 ArcGIS 里拖SHP 这东西看着简单属性表字段、拓扑连通性、坐标系声明这三处但凡有一处没对齐后面空间连接出来的结果就是玄学。2. 拆开数据包四个 SHP 图层到底怎么分工2.1 高速、国道、省道、县乡道的分层逻辑拿到压缩包先别双击打开用命令行列一下文件清单确认每个图层的要素数量和字段结构。常见做法是高速和国道合并成一个“干线层”省道单独一层县乡道因为碎、断头路多单独处理。为什么要分层因为不同等级道路在路径分析里的通行成本、限速、单双向规则完全不同。你把高速和县乡道混在一个图层里做最短路径算法会默认它们权重一样结果就是导航让你从村道穿过去上高速匝道——血泪经验。# 列出目录下所有 shp 及其配套文件确认没有缺 .prj 或 .shx ls -lh *.shp *.dbf *.shx *.prj # 用 ogrinfo 快速看每个图层的要素数和字段GDAL 自带工具 ogrinfo -so -al gd_highway.shp ogrinfo -so -al gd_national.shp ogrinfo -so -al gd_provincial.shp ogrinfo -so -al gd_county_town.shpogrinfo -so输出的是摘要信息重点看Feature Count和Geometry类型。如果县乡道图层要素数明显偏少比如只有几千条大概率是只保留了主要乡道村道没进来做末端配送覆盖分析时要注意这个边界。-al会把所有要素的字段列出来重点确认有没有name、grade、length这类字段后面做筛选和统计全靠它们。2.2 WGS84 坐标在 .prj 文件里怎么确认WGS84 是个统称实际工程里至少有两种常见写法地理坐标系经纬度单位度和投影坐标系平面米比如 UTM 或 Web Mercator。这份数据包标题写的是 WGS84 坐标默认理解为地理坐标系 EPSG:4326。但别信标题直接看 .prj 文件内容。# 查看 .prj 文件确认坐标系定义 cat gd_highway.prj # 用 ogrinfo 直接读 SRS 信息更直观 ogrinfo -so gd_highway.shp gd_highway | grep -A 5 SRS如果 .prj 里出现GCS_WGS_1984且UNIT[Degree]那就是经纬度。如果出现PROJCS和UNIT[Meter]说明是投影后的平面坐标虽然基准还是 WGS84但单位是米。这两种在计算长度、面积时差别巨大经纬度下直接算长度得到的是度不是米必须用测地线算法或先投影。我一般会先跑一个字段计算看length字段的数值量级——如果一条高速的长度是 0.5 左右那单位是度如果是 50000 左右单位是米。提示如果 .prj 文件缺失SHP 会变成“无坐标系”状态拖进 GIS 后可能被默认当成 WGS84 或投影坐标后续所有空间操作都不可信。拿到数据先检查 .prj 是否存在。2.3 属性表字段与道路等级编码四个图层的属性表结构通常一致但字段名可能因生产单位不同而有差异。常见字段包括NAME道路名称、GRADE等级编码、TYPE类型、LENGTH长度、ONEWAY单双向。等级编码没有国标强制有的用 1/2/3/4 对应高速/国道/省道/县乡道有的用文字“高速”“国道”。拿到后先跑个频数统计。import geopandas as gpd # 读取县乡道图层查看等级字段的取值分布 gdf gpd.read_file(gd_county_town.shp) print(gdf.columns.tolist()) print(gdf[GRADE].value_counts() if GRADE in gdf.columns else 无 GRADE 字段) print(gdf.crs) # 确认坐标系gdf.crs输出EPSG:4326就说明 GeoPandas 正确识别了坐标系。如果输出None说明 .prj 没被读到需要手动指定gdf.set_crs(epsg4326, inplaceTrue)。value_counts()用来确认等级字段的编码方式后面做筛选时直接按这个编码过滤。如果字段名不是GRADE而是LEVEL或ROAD_TYPE改一下列名即可逻辑一样。3. 把 SHP 用起来从坐标转换到空间筛选的完整链路3.1 经纬度转投影坐标做长度和缓冲区WGS84 经纬度直接算长度得到的是度没有工程意义。正确做法是先投影到适合广东的平面坐标系。广东跨了 UTM 49N 和 50N 两个带全省范围建议用 Albers 等面积投影或 Web MercatorEPSG:3857。如果只是做长度统计和缓冲区Web Mercator 够用但高纬度会变形广东纬度低误差可接受。import geopandas as gpd # 读取高速图层 highway gpd.read_file(gd_highway.shp) # 确认原始坐标系为 WGS84 经纬度 assert highway.crs.to_epsg() 4326, 坐标系不是 EPSG:4326先检查 .prj # 投影到 Web Mercator单位变为米 highway_m highway.to_crs(epsg3857) # 计算每条高速的实际长度米并转成公里 highway_m[length_km] highway_m.geometry.length / 1000 # 按道路名称分组统计总里程 total_by_name highway_m.groupby(NAME)[length_km].sum().sort_values(ascendingFalse) print(total_by_name.head(10))to_crs(epsg3857)是原地投影转换不改变几何形状只改变坐标单位。geometry.length在投影坐标系下返回米除以 1000 得到公里。groupby(NAME)按道路名称聚合能快速看出哪条高速最长。如果NAME字段有重复或空值先做清洗highway_m highway_m[highway_m[NAME].notna()]。3.2 按等级筛选并导出独立图层四个图层分开存有好处但做全省路网分析时经常需要合并成一个带等级字段的总图层。合并前先给每个图层加一个road_class字段再pd.concat。import pandas as pd import geopandas as gpd # 分别读取四个图层统一加等级字段 highway gpd.read_file(gd_highway.shp).assign(road_class高速) national gpd.read_file(gd_national.shp).assign(road_class国道) provincial gpd.read_file(gd_provincial.shp).assign(road_class省道) county gpd.read_file(gd_county_town.shp).assign(road_class县乡道) # 合并为一个 GeoDataFrame all_roads pd.concat([highway, national, provincial, county], ignore_indexTrue) # 确认合并后坐标系一致 print(all_roads.crs) # 按等级筛选出高速和国道导出为新的 SHP trunk all_roads[all_roads[road_class].isin([高速, 国道])] trunk.to_file(gd_trunk_roads.shp, encodingutf-8)assign是 pandas 的链式赋值给每个子图层加一列常量。pd.concat合并时ignore_indexTrue重置索引避免重复索引导致后续筛选出错。to_file导出 SHP 时指定encodingutf-8防止中文道路名称乱码。注意 SHP 对字段名长度有限制最多 10 个字符road_class刚好 10 个字符再长会被截断。3.3 用空间连接把路网挂到行政区上做区域统计时需要知道每个区县内各等级道路的里程。思路是用行政区划面去跟路网线做空间连接再按区县和等级分组汇总。import geopandas as gpd # 读取行政区划面假设已有该数据坐标系需为 WGS84 districts gpd.read_file(gd_districts.shp).to_crs(epsg3857) # 路网也投影到同一坐标系 roads_m all_roads.to_crs(epsg3857) # 空间连接路网线与行政区面相交 joined gpd.sjoin(roads_m, districts, howinner, predicateintersects) # 按区县和道路等级统计里程 summary joined.groupby([district_name, road_class]).apply( lambda x: x.geometry.length.sum() / 1000 ).unstack(fill_value0) print(summary.head())sjoin的predicateintersects表示只要线与面有交集就关联。howinner只保留能匹配上的记录。groupby后apply对每组几何求长度和unstack把等级变成列方便看每个区县各等级道路的里程分布。如果某条路跨了两个区县会被拆成两段分别计入这是正确的——实际统计中跨区道路本来就要分段归属。注意空间连接前务必确认两个图层的坐标系一致。一个 WGS84 经纬度、一个 Web Mercator 米sjoin不会报错但结果全错这是最常见的翻车点。4. 避坑与排查SHP 处理里最容易翻车的五件事4.1 中文属性乱码现象是道路名称显示为问号或方块原因SHP 的 .dbf 文件默认编码是 Latin-1 或 GBK不同生产工具导出的编码不统一。用 GeoPandas 读取时如果没指定编码中文会乱码。解决读取时加encodinggbk或encodingutf-8试导出时统一用encodingutf-8。如果已经乱码用gdf[NAME].str.encode(latin1).str.decode(gbk)尝试修复。4.2 坐标系声明缺失现象是拖进 GIS 后位置飘到非洲原因压缩包里 .prj 文件丢失或被误删。SHP 本身不存储坐标系全靠 .prj 声明。解决如果确认数据是 WGS84 经纬度用gdf.set_crs(epsg4326, inplaceTrue)手动指定再投影。如果不知道原始坐标系看坐标数值范围——经纬度在 -180 到 180 之间投影坐标通常是几十万到几百万。4.3 拓扑断头路现象是最短路径算出来绕远路或走不通原因县乡道图层里很多路段首尾节点没有严格对齐存在微小缝隙。路径分析算法要求网络拓扑连通缝隙会导致断连。解决用shapely的snap做节点捕捉容差设 0.0001 度约 10 米或者用 PostGIS 的ST_Snap和ST_Node做拓扑重建。常见做法是先把路网导入 PostGIS用pgr_createTopology建拓扑。4.4 字段名截断现象是导出的 SHP 里字段名变成 NAME_1、GRADE_1原因SHP 格式对字段名限制 10 个字符超出部分被截断重名时自动加后缀。解决导出前把字段名改短比如road_class改成rclassdistrict_name改成dname。或者改用 GeoPackage.gpkg格式没有字段名长度限制。4.5 高速与匝道混淆现象是高速里程统计偏大原因高速图层里包含了匝道、服务区道路等附属线这些在属性表里可能没有单独标记。解决先按NAME或TYPE字段过滤排除名称含“匝道”“连接线”的要素再做里程统计。如果属性表没有区分字段只能靠长度阈值粗筛——匝道通常很短设个 500 米以下的过滤掉。5. 进阶用法用路网数据做货车限行路径的预筛选前面都是基础操作真正体现这份数据价值的是结合限行规则做路径预筛选。假设你要给某市做货车配送路径规划高速和国道大部分允许货车通行但县乡道有限重、限宽省道部分路段有城区限行。思路是给每条路打上“货车可通行”标签再在路径分析时过滤。import geopandas as gpd # 读取合并后的全路网 roads gpd.read_file(gd_all_roads.shp) # 初始化通行标签默认允许 roads[truck_ok] 1 # 县乡道默认不允许货车通行假设规则 roads.loc[roads[road_class] 县乡道, truck_ok] 0 # 省道中名称含“城区”或“街道”的标记为限行 roads.loc[ (roads[road_class] 省道) (roads[NAME].str.contains(城区|街道, naFalse)), truck_ok ] 0 # 导出可通行货车路网 truck_network roads[roads[truck_ok] 1] truck_network.to_file(gd_truck_network.shp, encodingutf-8) # 统计可通行里程占比 total_km roads.to_crs(epsg3857).geometry.length.sum() / 1000 truck_km truck_network.to_crs(epsg3857).geometry.length.sum() / 1000 print(f货车可通行里程占比{truck_km / total_km:.2%})str.contains(城区|街道, naFalse)用正则匹配名称中的关键词naFalse把空值当不匹配处理避免报错。truck_ok字段导出后可以直接被路径规划引擎读取作为边权重过滤条件。这个预筛选能把路网规模砍掉三到四成后续做大规模路径计算时速度提升明显。验证方法随机抽几条被标记为不可通行的省道用 QGIS 叠加卫星底图看一眼确认确实穿过城区。如果发现误判调整关键词列表。我一般会把这个筛选逻辑封装成一个函数参数化关键词和等级规则换一个城市只改配置不改代码。从那以后我每次拿到新的路网数据包都强制走一遍“查 .prj → 看字段 → 投影 → 拓扑检查”这四步不再直接拖进 GIS 就开算。希望帮到你。本文还有配套的精品资源点击获取