
简介重庆各级公路shp数据包以Shapefile矢量格式汇集了重庆高速公路及一、二、三级公路线要素并附带重庆各区县行政区划边界适合GIS、交通规划、城乡规划等领域的从业者与在校学生用于路网分析、区域交通可达性评估、密度计算和基础地图制图。压缩包共35个文件涵盖shp几何文件、dbf属性表、prj投影定义、shx几何索引等典型组成部分整体仅1.15MB轻量便捷可在ArcGIS、QGIS、MapInfo等主流GIS软件中直接加载与编辑。已有680人学习下载数据组织清晰尤其适合作为练习图层叠加、属性查询与专题符号化的实操素材。通过属性表可提取公路等级、走向等信息结合区县边界分区统计路网里程或进行专题制图能够为交通规划、应急响应、物流选址等研究提供基础数据支撑。1. 处理重庆各级公路shp文件先搞清楚数据和行政区划的关系在 GIS 和交通规划项目中“重庆各级公路shp文件”往往直接指向一套按行政等级分层的道路网矢量数据高速、国道、省道、县道、乡道和村道几何类型为线属性里带有路线编号和路面宽度。拿到这套文件后真正的工作通常不是画图而是清洗几何、统一坐标系、按区域拆库并把道路等级映射到业务字段。这篇内容写给需要把重庆道路数据纳进数据仓库、做路网分析和地图服务的工程师从识别 shp 文件入手到用 ogr2ogr 和 Python 修数、投影、做空间连接最后给出一个能批量验收数据的脚本框架。你可以不看 DEM我默认你已经在电脑上装了 GDAL、QGIS 或者 geopandasWindows 和 Linux 都适用。2. 重庆各级公路shp文件的获取、识别与字段结构分析在拿到任意一份重庆公路 shp 之后不要急着拖进 ArcMap。先做三个确认几何有没有被压平、坐标系是地理坐标还是投影坐标、属性表里是否只有 code 和 name。这三个点决定后续所有转换的成败。2.1 从文件形态判断重庆道路 shp 数据的完整性shp 是一组同名字文件的集合至少要有 .shp、.shx、.dbf。重庆公路数据如果是按区县分目录每个目录里还有 .prj 文件说明制作方至少保持了投影信息如果只有 .shp 和 .dbf则十有八九是早年用 MapGIS 转出来的需要先做字符集检查。$ ls -la /data/chongqing_road/ total 286944 -rw-r--r-- 1 user dev 1024000 2024-01-01 10:00 chongqing_highway.dbf -rw-r--r-- 1 user dev 184 2024-01-01 10:00 chongqing_highway.prj -rw-r--r-- 1 user dev 8388608 2024-01-01 10:00 chongqing_highway.shp -rw-r--r-- 1 user dev 1024 2024-01-01 10:00 chongqing_highway.shx文件大小是第一步信号。shp 的主文件大小和 dbf 的属性大小如果相差一个数量级以上往往代表几何非常碎。比如 8MB 的几何配 1MB 的属性表说明这条 shp 里包含了小线段而不是完整道路后面做线网合并和拓扑重建时要留意。不管数据来自重庆地理信息公共服务平台还是某年的全国道路 shp 年度数据集你需要先确认这套数据的出版年份。道路等级字段在不同年份会变化比如 2018 年之前的重庆高速编码还是渝高速 S 开头2020 年之后统一成国家高速 G85、G93 等。没有年份标记的数据强烈建议先做抽样定位把 gps 坐标落在重庆主城某条已知高速上再用属性表的 route_code 对比不然你会在清洗时把普通国道误判成村道。2.2 使用 ogrinfo 查看重庆各级公路 shp 的几何类型与字段GDAL 在标准安装下会提供 ogrinfo 命令。用它看元数据不需要启动任何图形界面适合在服务器上快速验证。下面是检查一个重庆公路 shp 的完整命令ogrinfo -ro -al -so /data/chongqing_road/chongqing_highway.shp加-al表示列出所有图层-so表示 summarize only只输出图层摘要。-ro表示只读避免误写源文件。执行后会输出类似下面的信息INFO: Open of /data/chongqing_road/chongqing_highway.shp using driver ESRI Shapefile successful. Layer name: chongqing_highway Geometry: Line String Feature Count: 524880 Extent: (105.77359, 28.09274) - (110.15881, 32.21435) Layer SRS: GEOGCS[WGS 84, ...] id: Integer64 route_code: String road_name: String road_level: String width: Real如果输出里的 Geometry 是 Polygon那就不是公路中线而是路政部门用路面边界做的地块数据。如果 route_code 和 road_name 都是空白则这份 shp 只是几何骨架后期挂接里程、养护信息会很困难。重庆的 shp 图层经常混有MultiLineString和LineString两种几何类型这在ogrinfo里不会直接提示但会影响你后面按段聚合的长度统计需要先用-nlt LINESTRING做一次拆解。2.3 shp 编码和中文属性乱码的识别与修复重庆本地产的数据常用 UTF-8但也有不少承建单位用 GBK / GB2312 输出。当你在 Windows 下用 Excel 打开 dbf 看到é«éå¬è·¯实际就是对中文做了错误解码。修复有两个层面一是修改 shp 读取时的编码参数二是把 dbf 转成 UTF-8 再覆盖回去。常见做法是用 QGIS 的“另存为”直接选 UTF-8但对命令行脚本不够友好。我一般用 GDAL 的ogr2ogr对编码做一次无几何变化的转换ogr2ogr -f ESRI Shapefile -lco ENCODINGUTF-8 \ /data/out/chongqing_road_utf8.shp \ /data/chongqing_road/chongqing_highway.shp \ -nlt LINESTRING这条命令里的-lco ENCODINGUTF-8是输出层的编码控制选项-nlt LINESTRING强制把 MultiLineString 拆成单线避免输出层出现空的 MultiLine。如果转换后属性仍是乱码说明源 dbf 的 codepage 标记本身是错的此时用iconv从 dbf 里抽出字段名再对照 shapefile 的官方 CPG 规则修正更直接的方式是在 Python 里强制指定 encoding 读取。import geopandas as gpd gdf gpd.read_file(/data/out/chongqing_road_utf8.shp, encodingutf-8) print(gdf[[route_code, road_name, road_level]].head())读出来以后先做一次唯一值统计尤其看 road_level 里是否混入了高速、高速公路、G1113这类不相同但表达同一等级的写法。如果不统一后文按等级拆分时结果会偏差。表重庆公路 shp 中常见的 road_level 取值字段值实际等级建议标准化值高速高速公路expressway国道普通国道nationalS302省道编号provincial县道县道county每次做转换前把 dbf 的时间字段也检查一下。部分 shp 里的 update_date 是字符串类型写的是20230102你要在入库前改成 date 类型否则后面按年份抽道路增量没法直接用 SQL 处理。用ogrinfo看字段类型是最快的因为 dbf 里很多Integer字段其实存的是文本读进来再做强制astype永远比在大数据平台里二次清洗省时间。3. 重庆各级公路shp文件的清理与几何修复如果把获取 shp 当成“素材采购”这一章就是“材料进场检查”。重庆多山道路 shp 常见的几何问题包括自相交、重复节点、短小碎线和 Z 值漂移。这些问题不会在建图时立刻报错但会直接把长度算错或在切片时产生不可预期的孔洞。3.1 检查并用 shapechecker 修复重庆公路 shp 的几何错误ShapeChecker 是 GIS 圈子里常用的免费检查工具专门处理 shp 文件的几何拓扑问题。下载后把重庆 shp 拖入工具窗口点击完整检查会列出三类典型错误self-intersection、duplicate segment、empty geometry。修复时不要一次性全量修尤其对于线数据优先修复 empty geometry一旦生成空几何后续空间连接就断链。命令行下的对策是使用 GDAL 内置的ogr2ogr -makevalid它会在输出层自动应用 OGR 的 MakeValid 规则把自相交的线切成多段并保持属性不丢失。对重庆各级公路这种几十万要素的线图层速度和稳定性都比 QGIS 的矢量修复要快。ogr2ogr -makevalid -skipfailures -overwrite \ /data/out/road_valid.shp \ /data/chongqing_road/chongqing_highway.shp参数里-skipfailures会让单条要素出错时跳过而不是终止整个转换。对于大批量数据建议加上-lco SPATIAL_INDEXYES输出端建立空间索引给后面的空间连接提速。验证修复结果最直观的方式是看要素数变化。修复前 524880 条修复后往往变成 523112 条因为部分自相交的线会被合并。如果修复后数量大幅减少说明原 shp 里存在大量重复线段这时要先对 route_code 做分组去重不要依赖几何修复。3.2 用 Python 处理重庆公路 shp 里的重复要素和空字段在批量场景中我更习惯用 geopandas 而不是打开 QGIS 手工翻。下面的脚本读取修复后的文件按 route_code 和 geometry 去重并丢弃没有任何属性信息的空要素。import geopandas as gpd gdf gpd.read_file(/data/out/road_valid.shp) gdf gdf[gdf.geometry.notna()] # 去掉空几何 gdf gdf.drop_duplicates(subset[route_code, geometry]) gdf[road_width] gdf[width].fillna(0) gdf gdf[gdf.geometry.length 10] # 单位取决于坐标系这段代码的关键是subset[route_code, geometry]。同样的 route_code 代表同一段路如果 geometry 也相同那一定是重复拷贝。geometry.length在经纬度坐标系下算出来的是十进制度数距离不是米所以这里只在要素长度 10 度以上时剔除不是严格阈值。更严谨的做法是先按 4.1 投影成高斯-克吕格平面坐标再计算长度并剔除小于 5 米的碎线。3.3 把重庆各级公路字段标准化到一套可入库的结构每个来源的 shp 字段命名都不同有的叫roadlevel有的叫road_type入库前要统一。我设计的规范结构如下表重庆道路 shp 标准化字段映射标准字段名来源字段示例类型说明route_coderoadcode / routestring路线编号如 G318road_nameroadname / namestring公路名称level_coderoad_level / typestringexpressway/national/...adm_codexzqhdmstring所属区县行政区划代码build_yearyear / update_yearinteger建成年份或统计年份length_km计算字段float按投影坐标系计算长度这一步没有统一脚本因为每个 shp 的原始字段不一样。常见做法是写一张字段映射表把同一个 shp 的源字段映射到标准字段再用 rename 完成rename_map { roadcode: route_code, roadname: road_name, roadlevel: level_code } gdf gdf.rename(columnsrename_map) gdf[length_km] gdf.geometry.length / 1000 gdf.to_file(/data/out/road_standard.gpkg, layerchongqing_roads, driverGPKG)导出用 GeoPackage 而不是 shp因为 GPKG 支持更长的字段名、内置空间索引也不会出现 dbf 字段截断问题。等最终要交给第三方系统时再转回 shp这样中间处理不会丢字段。字段里如果存在adm_code缺失可以先用gdf[adm_code].fillna(500000)暂存等后续空间连接行政边界后再回填比直接在源数据上改稳妥。4. 重庆各级公路shp文件的投影、空间分析与格式转换重庆各级公路的原始数据大多是 WGS84 地理坐标系在 ArcMap 里拖出来是“歪”的。重庆经度跨度约 105.8 到 110.2 度直接用经纬度算长度会产生百分之十几的误差。所以做路网长度统计、区县汇总、缓冲区分析之前必须转投影坐标系。4.1 把重庆公路 shp 从 WGS84 转 CGCS2000 高斯-克吕格投影重庆位于 3 度带常用中央经线为 105°E 或 108°E。全市覆盖跨度大需要按区县跨越度决定使用 105 度带还是 108 度带。若 shp 是其他基准面最好先统一到 CGCS2000。官方大典数据一般已用 CGCS2000 / 3-degree Gauss-Kruger zone 35 或 zone 36。关键 EPSG 代码列在这里表重庆常用 CGCS2000 投影参数EPSG中央经线适用区域4544102°E渝西边缘地区少用4545105°E重庆西部、主城局部4546108°E重庆主城、渝东北、渝东南用 ogr2ogr 转换到 EPSG:4546 的命令如下ogr2ogr -f ESRI Shapefile -t_srs EPSG:4546 \ /data/out/road_cgcs2000.shp \ /data/out/road_standard.gpkg-t_srs EPSG:4546是目标坐标系输入参数直接给 GeoPackage 文件GDAL 会自动读取第一个图层。如果转换后要素整体不在重庆范围内先检查原 shp 是否带动态坐标变换必要时用-t_srs projtmerc lat_00 lon_0108 k1 x_0500000 y_00 ellpsGRS80 unitsm no_defs这种直接参数避开 GEOGCS 版本造成的偏移问题。投影转换后用 QGIS 加载一张重庆市界底图肉眼确认道路与边界不脱节。如果部分道路落在长江以南却跑到了北边那就是数据自带的二维平面偏移不是投影问题需要在原始 shp 上做仿射纠正。4.2 用渔网分割和行政边界裁剪重庆公路 shp热词里经常有人问“渔网分割shp”和“shp 有没有办法只保留外边界线”。这两个操作本质是同一类用规则格网或行政边界对道路做裁剪生成瓦片级别的路网切片。在 QGIS 里导入矢量网格工具生成渔网但命令行更稳定的方式是 GDAL 的ogr2ogr -clipsrc。下面的命令把重庆道路 shp 按一个矩形范围裁剪矩形范围直接写在-spat参数里ogr2ogr -clipsrc -spat 106.5 29.0 110.0 30.5 \ -t_srs EPSG:4326 \ /data/out/road_clip.shp \ /data/out/road_cgcs2000.shp-clipsrc参数后面可以跟一个矢量文件也可以跟四个坐标值这里的106.5 29.0 110.0 30.5是裁剪矩形的 minx miny maxx maxy。如果只保留外边界线则用 shp 的线转面再取外环在 GDAL 里没有直接命令要先ogr2ogr -dialect sqlite -sql SELECT ST_ExteriorRing(geometry) ...或者直接在 QGIS 里用“线条转多边形”再删内部边。实际项目里我更推荐用重庆 38 个区县的行政区划 shp 做-clipsrc这样可以顺带输出到区县文件for code in yuzhong jiangbei wanzhou; do ogr2ogr -clipsrc data/out/行政区划_${code}.shp \ -f ESRI Shapefile \ data/out/roads_${code}.shp \ data/out/road_cgcs2000.shp done注意循环里的区县名需要替换成实际的行政区划代码中文路径在 sh 脚本里容易产生编码问题建议在文件名里统一用拼音代码内部用行政代码作为主键。4.3 计算重庆各级公路长度并按区县汇总长度计算必须在投影坐标系下完成。下面的 Python 代码读取 CGCS2000 投影后的重庆道路 shp按区县做空间连接并统计高速公路和普通国道的里程。import geopandas as gpd roads gpd.read_file(data/out/road_cgcs2000.shp) districts gpd.read_file(data/out/districts_cgcs2000.shp) joined gpd.sjoin(roads, districts, howinner, opintersects) joined[length_km] joined.geometry.length / 1000 summary joined.groupby([district_code, level_code])[length_km].sum().reset_index() print(summary.head())sjoin的opintersects在这里会重复计算跨越多个区县的路段每条路有多少个区县相交就出现在多少个区县里。如果要做精确归属应该把交叉路段的几何按区县切割或者用道路中心线的中心点做空间连接。这里的howinner表示只保留有明确区县归属的道路避免空值。如果你只是想快速导出道路坐标和属性列表给外业人员就需要用上“shp 转 txt”。通常我提取 shp 中每个要素的起点和终点坐标输出成路线编号,等级,起点x,起点y,终点x,终点y这样的 CSV。代码不复杂关键是要注意坐标系统一import geopandas as gpd import pandas as pd roads gpd.read_file(data/out/road_cgcs2000.shp) roads[coords] roads.geometry.apply(lambda geom: list(geom.coords)) export roads[[route_code, level_code]].join( roads[coords].apply(lambda cs: pd.Series({ x1: cs[0][0], y1: cs[0][1], x2: cs[-1][0], y2: cs[-1][1] })) ) export.to_csv(road_txt_export.csv, indexFalse)输出 txt 前我一般会顺手做一次roads.geometry.type.value_counts()如果混有 MultiLineString要在抛给外业人员之前把每条多线拆成单线否则他们解析不到中间拐点。5. 批量处理重庆各级公路shp文件时的自动化质检与输出最后用一个可复用脚本把前面几章的操作收敛成验收命令。这里不做通用框架只讲一个我常用的技巧用 Python 脚本代替手工点选把原始 shp、中间文件、最终输出三个目录横向对比输出一份 CSV 质检报告。5.1 用 Python 脚本批量检查重庆各级公路 shp 的“五查”清单脚本思路是逐个区县的 shp 做五查字段数、要素数、空几何数、坐标范围、拓扑错误数。把这些值累计成一行最终可沉淀成任务日志。核心代码如下def check_road_shp(file_path): gdf gpd.read_file(file_path) return { file: file_path, feature_count: len(gdf), field_count: len(gdf.columns), empty_geom: gdf.geometry.isna().sum(), extent: f{gdf.total_bounds.tolist()}, level_mismatch: (gdf[level_code].isna() | (gdf[level_code] )).sum() }在这个函数里level_mismatch统计的是没有等级的要素数量。重庆公路 shp 最常见的质量事故就是把某一条县道忘了维护等级字段导致后续按等级配图时整条路消失。total_bounds.tolist()返回的是[xmin, ymin, xmax, ymax]报告中要顺带输出gdf.crs否则 WGS84 和 CGCS2000 两套数据放在一起看不出单位差异。批量处理所有区县文件并生成报表import pandas as pd from glob import glob files glob(/data/out/roads_*.shp) report pd.DataFrame([check_road_shp(f) for f in files]) report.to_csv(road_quality_report.csv, indexFalse)五个检查项的判定阈值可以参考这张表检查项预警阈值处理动作feature_count为 0检查 shp 是否空文件empty_geom大于 0先执行 ogr2ogr -makevalidfield_count小于预期检查 dbf 是否有截断extent不在重庆经纬度范围内检查 prj 和投影编码level_mismatch大于 1%回源重新维护等级字段5.2 从 shp 到 3dtiles 和 txt 的两种输出检查方法经常有地图服务项目要把重庆公路 shp 转成 3dtiles这时不能直接拿 shp 转。shp 是矢量线数据3dtiles 需要带高度或至少是折叠成面的线管。常见做法是先转 GeoJSON再用CesiumGS/3d-tiles-tools的obj2tileset配合拉伸或者用 QGIS 的 QTiles3D 插件导出。但不管走哪条路验证的原则都是道路不能断。输出 3dtiles 前先跑一个“断链检测”把原始线要素按 route_code 分组检查同一 route_code 的前后线段节点是否首尾相连如果断链超过 1% 就要回上游检查 shp 制作时的捕捉半径设置。这个检查可以直接加进报告也可以做一个独立的is_connected布尔列追加到导出表里。最后送你一个我在验收时必用的兜底操作把质检报告里的 feature_count、extent、empty_geom 三列和原始 shp 的文件指纹一起归档用sha256sum生成校验值再写进同一天的日志目录。跑完上面的脚本把road_quality_report.csv和原始 shp 的文件指纹一并归档验收方拿到的就不是一张截图而是一份可追溯的质检日志。本文还有配套的精品资源点击获取