ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

北京市道路数据预处理:从RAR到乡道级矢量路网

北京市道路数据预处理:从RAR到乡道级矢量路网 简介北京市道路矢量数据包涵盖城市一至四级道路、高速、国道、省道、县道、乡道等行政等级道路以及OSM来源的铁路、轻轨、窄轨、地铁、有轨电车等轨道交通和主干道、次干道、支道、人行道等街道数据共16种道路类型可为城市规划、交通分析、GIS教学与科研提供基础地理底图。压缩包共97个文件以shp矢量主文件为核心配套dbf属性表、prj投影文件、shx索引文件及xml元数据说明整体约25.48MB数据组织规范、层级清晰。目前已有841人学习下载读者可直接导入ArcGIS、QGIS等主流GIS平台进行路网可视化、缓冲区分析、可达性计算等操作。数据中既有不同等级道路的独立图层也有OSM补充的细类道路各图层间重复中互为验证、互补中趋于完整非常适合从事地理信息数据处理、交通规划及城市研究的人员作为基础参考数据使用。1. 拿到“北京市道路数据最新分级精确到乡道矢量数据.rar”先把它当成一个待考古的原始数据集这个文件名对做 GIS 和位置服务的人很有诱惑力最新分级、精确到乡道、矢量数据三个词几乎直接回答了“这东西能不能用来做路网分析和路径计算”的预审。但现实往往是.rar 里装的是没写清楚坐标系、字段名五花八门、甚至几何类型都未必是线要素的一套散装 Shapefile。要把它变成能进 PostGIS、能被路由引擎读取、能和在线底图对齐的道路图层链路并不短。这篇文章按我处理这类数据的习惯顺序来写先不急着解压先看压缩包清单然后用 unrar 解开并处理编码问题再用 ogrinfo 和 GeoPandas 摸清字段和坐标系最后把道路等级筛选、几何修复和覆盖验证做完导出成能实际用的地图数据。2. 道路分级与乡道这套“北京市道路数据”到底应该是什么结构2.1 行政分级与城市道路分级是两套并行的语义拿到“北京市道路数据”后首先要确认的是“分级”两个字用的是哪套体系。北京市的道路数据里通常同时存在两套分级字段一套是行政等级也就是国家高速公路、普通国道、省道、县道、乡道、村道编码规则常见为 G 字头、S 字头、X 字头、Y 字头另一套是城市道路技术等级即快速路、主干路、次干路、支路。两者在市中心区域重叠度很高到了延庆、怀柔、密云这些远郊区行政等级才是主导属性。把这两套体系区分开直接决定了你后续怎么写筛选条件。常见错误是把class字段里的“高速公路”直接等同于“G 字头”结果丢掉了北京市内相当一部分属于国道但已经市政化的路段。可靠的判断方法是先看字段注释再看编号列gb、road_code、route_number这类字段通常保存 G/S/X/Y 编号而kind、type、class才是等级名称。两份信息对不上时以编号列为准做行政等级筛选以类型列为准做驾驶体验分级。2.2 精确到乡道在空间上意味着什么“精确到乡道”不是一句营销话术它改变了数据的可用边界。只覆盖城市主干路和快速路的道路数据做地图渲染没问题但做乡镇物流路径规划、山区道路巡检、公交线路优化时会出现大量“有起点没通路”的断头结果。乡道级别的数据必须把连接乡镇与村庄的道路线全部包含进来并且这些路的几何形态通常更复杂连续弯道多、单车道占比高、部分路段是尽头路。识别一份数据是否真到乡道级别不看字段声明看空间分布。把数据加载后按区县做长度统计延庆、门头沟、怀柔等区的道路里程如果和中心城区不在一个合理比例大概率只是城区的路网裁剪结果。另一种验证是检查道路网格的平均边长乡道密集区域的路段平均长度在 300 到 800 米之间如果平均边长普遍超过 2 公里说明只画到了县道。2.3 压缩包先行用unrar l建立文件清单我处理这类 .rar 的第一步不是解压而是先列清单。原因很现实压缩包里可能装着一个完整的 Shapefile 集合也可能装着按区县切分的十几个子目录甚至可能混入了 .gdb 文件夹。不做预检直接解压到同一目录轻则文件互相覆盖重则把整个数据目录结构弄乱。unrar l 北京市道路数据最新分级精确到乡道矢量数据.rar # 或者用 7-Zip 的等价命令 7z l 北京市道路数据最新分级精确到乡道矢量数据.rarl参数是 list 的缩写只读取压缩包内文件列表不解压任何内容。输出里会显示每个文件的路径、原始大小和压缩后大小。这一步的观察重点是文件后缀组合和目录层级而不是文件大小。见到完整的.shp、.shx、.dbf、.prj四件套说明是标准 Shapefile只有.shp没有.prj意味着坐标系信息丢失后面对齐底图时得多做一步猜测。压缩包内出现的文件含义后续处理.shp/.shx/.dbf/.prj四件套标准 Shapefile检查.prj内容是否为空决定坐标系处理方案.cpg文件属性字段编码声明常见为 UTF-8 或 GBK决定读入时的 encoding 参数.gdb目录File Geodatabase不能直接拖入 WebGIS用ogr2ogr批量转出多个按区县命名的.shp分区存储先用ogrmerge合并再统一处理字段.txt/说明.pdf数据字典或坐标系说明优先阅读比任何猜测都可靠如果压缩包里有说明文件无论多小先解压出来读。很多“最新分级”数据的字段含义、坐标系来源、更新日期都写在这个不起眼的文本里比对着属性表猜字段省一小时。3. 把 rar 解开并读出道路线从压缩包列表到 GeoPandas3.1 解压并处理中文文件名乱码北京市道路数据这类由本地单位发布的压缩包文件名经常是 GBK 编码。在 Windows 上用 WinRAR 解压没问题换到 Linux 服务器上就会解出一堆乱码目录名。乱码不影响 Shapefile 内部数据读取但会让后续脚本按路径通配符查找文件时踩坑。优先使用unar它对中文压缩包名的识别比unrar和7z更智能unar -o ./data 北京市道路数据最新分级精确到乡道矢量数据.rar-o指定输出目录工具会自动创建。如果服务器上没有unar用 7-Zip 解压后配合convmv修正文件名编码7z x -o./data 北京市道路数据最新分级精确到乡道矢量数据.rar convmv -f GBK -t UTF-8 --notest -r ./data第二行命令的-f GBK -t UTF-8表示从 GBK 转换到 UTF-8--notest是真正执行改动不加它只会打印预览结果-r递归处理子目录。注意convmv只重命名文件名不改文件内容.dbf内部的属性字段编码需要另一套逻辑处理后面读入时会遇到。3.2 ogrinfo 快速检查要素类型与空间参考解压完成后用ogrinfo把所有 Shapefile 的图层元数据扫一遍避免一个一个拖进 QGIS 才发现问题。这一步特别适合同时有多个.shp文件的压缩包一条 for 循环全部覆盖for f in data/*.shp; do name$(basename $f .shp) ogrinfo -so $f $name | grep -E Layer name|Geometry|Feature Count|Extent|GEOGCS|PROJCS done-so参数是 summary only只读取图层头部信息不遍历全部要素所以速度很快。Geometry一行如果显示Line String或Multi Line String说明数据是可用的道路中心线如果显示Polygon则这份矢量数据画的是道路面做路径计算前需要先提取中心线。输出里有GEOGCS或PROJCS字样表示带了坐标系定义什么都没显示则多半.prj文件为空或缺失。3.3 GeoPandas 读入与首轮字段扫描元数据检查没问题后用 GeoPandas 真正把数据读进内存。这里最常见的坑是中文属性乱码读出来全是类似鍖椾含的文本。处理方式是指定编码import geopandas as gpd # 优先尝试 GBK如果字段值仍有乱码改回 utf-8 gdf gpd.read_file(data/beijing_road.shp, encodinggbk) # 打印字段名列表 print(gdf.columns.tolist()) # 打印几何类型确认全部是线要素 print(gdf.geom_type.value_counts()) # 打印坐标系信息可能是 None print(gdf.crs) # 打印前 3 行快速看字段值风格 print(gdf.head(3).to_string(max_colwidth20))encodinggbk对应国内容器属性字段最常见的编码方式如果解压包里的.cpg文件标明是 UTF-8则改成encodingutf-8。gdf.crs输出None不要慌这在上海、北京这类非正规发布的数据里很常见后面统一处理。字段列表里如果出现highway、class、rank、gb、code、oneway这类命名基本可以判定数据来源于跨平台汇总不同字段映射到不同来源的原始分类。4. 坐标系与几何修复北京市道路矢量数据的两个大坑4.1 三种坐标系并存别信后缀名读到的 WGS84北京市道路数据最常见的问题是坐标系混乱。理想情况下一份数据只用一个坐标系但实际从网盘、各区县交换站、第三方采集商手里汇总来的数据经常混着三套坐标系。坐标系典型 EPSG特征常见来源WGS84 地理坐标EPSG:4326经纬度北京市范围约 115.7°E 到 117.4°EGPS 采集、OpenStreetMap 导出CGCS2000 高斯投影CGCS2000 / 3-degree GK CM 117E即 EPSG:4544以米为单位适合计算道路长度和面积自然资源与国土空间数据GCJ-02 火星坐标非公开标准无固定 EPSG与真实坐标偏差约 300 到 700 米互联网地图 API 抓取的数据判断数据属于哪套坐标系不能只看.prj文件。.prj写着GCS_WGS_1984只能说明发布方创建时选了 WGS84不能证明坐标值没有经过加密偏移。把路网叠加到在线卫星影像上如果整体位置偏了几百米特别是明显朝西南方向偏移那就是抓取的高德或百度数据需要对坐标做纠偏处理后再使用。4.2 投影到 CGCS2000按米重新计算路长在经纬度坐标系下直接计算geometry.length得到的是度不是米。做道路里程统计和缓冲区分析前必须先投影到以米为单位的坐标系。北京市常用的是 CGCS2000 3 度带高斯投影中央经线 117Eimport geopandas as gpd gdf gpd.read_file(data/beijing_road.shp, encodinggbk) # 如果坐标系未声明先按 WGS84 设置 if gdf.crs is None: gdf gdf.set_crs(EPSG:4326) # 投影到 CGCS2000 3 度分带中央经线 117E gdf_proj gdf.to_crs(EPSG:4544) # 重新计算道路长度单位是米 gdf_proj[length_m] gdf_proj.geometry.length print(gdf_proj[length_m].sum())关于 EPSG:4544 要做个说明它是 CGCS2000 / 3-degree Gauss-Kruger CM 117E 的代号北京大部分地区落在以 117E 为中央经线的投影带内。如果当前环境里的 pyproj 数据库较旧to_crs(EPSG:4544)可能报错这时可以用等效的 proj 字符串替代关键是lon_0117和unitsm两项参数保持正确。投影后算出来的length_m字段才是后续做乡镇覆盖率统计的真实依据。4.3 跨越乡道的几何缺陷处理道路矢量数据常见三类几何问题重复节点、自相交线段、以及两个相邻路段中间有微小缺口。这些在渲染时看不出来但做路径规划时会让路网无法连通。from shapely.geometry import LineString, MultiLineString from shapely.ops import linemerge from shapely.validation import make_valid def clean_road_geometry(geom): if geom.is_empty: return None # 先修复自相交等无效几何 if not geom.is_valid: geom make_valid(geom) # 再合并可连接线段并去掉重复节点 if isinstance(geom, (LineString, MultiLineString)): geom linemerge(geom) return geom # 模数容差用于去除重复节点经纬度下 1e-7 约等于 1 厘米 gdf[geometry] gdf.geometry.apply(clean_road_geometry)make_valid会把自相交的环拆成多个有效几何linemerge则把首尾相连的短线段合并成一条长线这两步能显著减少进入路由引擎后的线段数量。如果数据是投影后的米制坐标建议把去除重复节点的容差放到0.01即 1 厘米在经纬度坐标下用1e-7。反过来不要在几何清理前就做拓扑构建否则中间的空洞会被当成独立节点处理增加无意义的断点。5. 从字段语义到乡道范围怎么验证它真的覆盖到乡道5.1 找出等级字段处理三种值域从第 3 章的字段扫描结果入手先写一段通用代码把可能代表等级的字段全部统计一遍for col in [highway, class, rank, level, type, kind, gb]: if col in gdf.columns: print(----, col, ----) print(gdf[col].value_counts(dropnaFalse).head(20))这一步的目的是识别等级字段的值域风格。北京市道路数据里最常出现三种写法第一种是 OSM 体系值域是motorway、trunk、primary、secondary、tertiary、unclassified第二种是中文行政等级直接写“高速、国道、省道、县道、乡道”第三种是数字等级如 1 到 7每个数字对应一级。三种值域之间没有统一映射标准必须靠字段名和说明文档推断。如果同时存在highway和gb两个字段通常gb是行政等级highway是物理等级两者不要混用。5.2 按“行政等级 城市道路等级”分支筛选知道字段值域后写筛选逻辑就要做两个分支远郊区看行政等级中心城区看城市道路等级。不能用一条条件覆盖全程否则会丢掉城区内部一大票没有 Y 字头的市政道路。# 行政等级关键词保留乡道及以上 admin_levels [高速, 国道, 省道, 县道, 乡道] # 城市道路等级保留支路及以上 urban_levels [快速路, 主干路, 次干路, 支路] # 把可能混入的数字类型转成字符串去掉首尾空格 gdf[level_clean] gdf[gb].astype(str).str.strip() # 两个分支取并集 selected gdf[ gdf[level_clean].isin(admin_levels) | gdf[level_clean].isin(urban_levels) ] print(selected.shape) print(selected[level_clean].value_counts())astype(str).str.strip()是必须要做的一步因为.dbf文件里的中文字段经常带前后空格或者被识别成object之外的类型不清洗直接isin会导致匹配结果为空。筛选后的selected才是后续做覆盖率和导出分析的基础数据集原始gdf不要动保留一份原始数据用于回溯对比。5.3 用乡镇边界核对覆盖率判断“精确到乡道”是否属实最直接的办法是把乡镇边界拿来和筛选后的路网做空间叠加统计每个乡镇的道路里程。低于阈值的乡镇就是可疑区域。# 读取乡镇边界注意编码 towns gpd.read_file(data/beijing_town.shp, encodinggbk) # 统一坐标系避免空间运算报错 towns towns.to_crs(selected.crs) # 对每个乡镇裁剪路网并累加长度 for _, row in towns.iterrows(): mask selected.intersects(row.geometry) if mask.sum() 0: print(无道路覆盖:, row[name]) continue clipped selected.loc[mask].geometry.intersection(row.geometry) # length 在投影坐标系下是米 total_m clipped.length.sum() print(row[name], round(total_m, 1))这里用intersects初筛再用intersection裁剪目的是避免跨越边界的道路被重复计算进两个乡镇。如果某些乡镇面积很大但计算里程不足几公里要么是数据确实缺了乡道要么是乡镇边界坐标系和路网没有对齐先回去检查.prj再做判断不要急着下结论。这个步骤结束后如果每个乡镇都有合理的里程数这份数据才算真正达到了“精确到乡道”的可用状态。6. 面向地图发布与路径计算的最终导出给“矢量数据”收口6.1 导出成不丢等级的 GeoJSON 和 FlatGeobuf清洗和筛选完成后的数据建议转成两个格式GeoJSON 用于 Web 端预览和调试FlatGeobuf 用于本地高性能读取。GeoJSON 对字段名称有字符限制中文属性建议先重命名为英文避免后续编码问题。ogr2ogr -f GeoJSON beijing_roads_select.geojson beijing_roads_select.shp -t_srs EPSG:4326 ogr2ogr -f FlatGeobuf beijing_roads_select.fgb beijing_roads_select.shp-t_srs EPSG:4326把输出转换到经纬度坐标这是 Web 地图渲染的通用要求。FlatGeobuf 格式不需要额外服务器解析PostGIS 开启postgis_flatgeobuf扩展后可以直接查询特别适合需要频繁读取但不想每次全量加载的场景。6.2 用 tippecanoe 控制乡道路网的显示体量乡道数据往往有数万条线段直接以 GeoJSON 形式丢给前端会造成明显卡顿。用 tippecanoe 生成矢量瓦片是当前比较稳妥的方案tippecanoe -zg -o beijing_roads.mbtiles beijing_roads_select.geojson --drop-densest-as-needed-zg让工具自动根据数据密度决定每个缩放级别的显示范围--drop-densest-as-needed在低缩放级别时优先丢弃密集区域的要素保留稀疏区域的道路这样乡镇路网在缩小视野时不会糊成一团。拿到这份 mbtiles 后后续往 pgRouting 里导时多一步几何清理阶段如果没做linemerge到数据库后用pgr_nodeNetwork把相交处打断成网络节点再用pgr_createTopology建立拓扑关系这样“北京市道路数据最新分级精确到乡道”的原始线图层才算真正变成了可计算路径的路网。本文还有配套的精品资源点击获取
返回列表