
简介内蒙古自治区道路数据矢量包面向地理信息系统、城市规划、交通物流等领域的从业者与研究者提供了一套精确到乡道的最新分级道路网络数据。压缩包约39.26MB内含16类道路矢量数据覆盖城市一级至四级道路线、行政等级道路高速、国道、省道、县道、乡道以及OSM来源的铁路与道路图层既能满足从区域骨架到街道细部的多尺度制图需求也可用于道路密度分析、可达性计算、路径规划等空间操作。这些图层在内容上存在部分重叠又有相互补充便于对照校验与深入考究。数据采用通用矢量格式方便属性查询、符号化渲染及与其它专题数据叠加分析。目前已有216人学习下载适合需要基础路网底图进行科研建模或工程应用的用户可直接在ArcGIS、QGIS等主流平台中加载使用提升道路数据处理的效率与精度。1. 拿到内蒙古乡道矢量数据后第一步不是打开地图而是先做数据体检做交通规划或者物流路径分析的人拿到“内蒙古自治区道路数据最新分级精确到乡道级”这份成果时第一反应通常是直接拖进 GIS 软件里看看图层长什么样。这个动作本身没什么问题但如果你对这份数据背后的分类体系、坐标系、属性结构没有概念很容易在后续分析里翻车——尤其是当你需要把乡道和国道、省道混在一起做路网分析时不同层级的道路在数据里可能根本不在同一个坐标系下甚至属性编码都是乱的。这篇笔记我基于自己做过的同类省级道路矢量数据整理项目把数据包解压之后的检查流程、坐标处理、分级筛选、拓扑修复这几个环节一次讲透该避开的坑直接列给你。这份数据适合谁用做省级路网规划、物流配送路径优化、乡镇级可达性分析、以及国土空间规划里交通专题图的人都需要它。它本质上是一个分层的矢量数据包道路等级从高速公路一路细分到乡道精度和属性粒度都远超公开的在线地图数据。但越是精细的数据越需要你先花半小时把它的“脾气”摸清楚否则后面所有操作都是在盲人摸象。2. 解压与数据体检先搞清楚包内结构和坐标系底细再动手2.1 用命令行解压和浏览数据包避免图形界面解压的隐性问题我习惯第一件事不是双击解压而是先用命令行把压缩包的目录结构打出来。因为 .rar 格式在 Windows 资源管理器里解压偶尔会丢失长路径文件或者遇到文件名编码错乱的问题——内蒙古数据包的内部文件名经常带着中文和空格图形界面解压对这种文件名处理并不总是可靠。# 查看压缩包内的文件清单 unrar l 内蒙古自治区道路数据最新分级精确到乡道矢量数据.rar # 解压到目标目录保持原始目录结构 unrar x 内蒙古自治区道路数据最新分级精确到乡道矢量数据.rar ./roads_data/如果你的机器上还没有 unrar 命令Ubuntu/Debian 系用sudo apt install unrarCentOS/RHEL 系用sudo yum install unrar或者用7z x命令替代也行。参数说明l参数只列清单不实际解压适合先看文件构成x参数是解压并保留完整路径。解压完成后用ls -la roads_data/看一下目录层级确认是否是按道路等级分目录存放。如果出现ls报错或者文件名乱码用ls --show-control-chars强制显示非 ASCII 字符。2.2 文件构成识别shp、gdb 还是 kml决定你的工具链解压之后观察文件后缀分布。常见的省级道路矢量数据包文件格式基本是三种Shapefile.shp .dbf .prj .shx、File Geodatabase.gdb 文件夹、或者 GeoJSON。每种格式对应不同的处理方式和工具链我直接在 QGIS 里用浏览器面板拖进去看是最快的判断方法。如果解压出来的是 .shp 文件你要注意它不是一个单独的文件而是至少四件套。很多人只拷贝 .shp 单个文件发给同事结果对方打开后图层属性表全是空的这就是典型的缺 .dbf 的翻车现场。我一般会让数据在本地落地之后先跑一遍完整的属性表检查。# 用 geopandas 检查 shp 能否正常读取、属性表是否完整 import geopandas as gpd gdf gpd.read_file(roads_data/乡道.shp, encodingutf-8) print(gdf.shape) # 看行数和列数确认没有读成空表 print(gdf.columns) # 看字段名 print(gdf.head(3)) # 看前三条记录这里的关键参数是encoding。内蒙古的 GIS 数据属性表编码常见两种UTF-8 和 GBK。encoding参数设错读出来就是乱码。你先试utf-8如果中文字段名变成乱码换成gbk再读一次。如果两种都乱码那说明数据源可能用的是 GB18030再补一次encodinggb18030就行。3. 坐标系统一CGCS2000 与投影带的抉择是分析前的地基工程3.1 为什么内蒙古的道路数据最容易出现坐标系拼接错位内蒙古自治区东西跨度极大从东经 97° 到 126°跨了整整三个 3 度分带投影带带号约 25、26、27。这意味着省级数据打包时不同区域的图幅很可能是用不同投影带生产的。你如果打开软件后发现国道、省道图层能叠上一到乡道图层就整体偏移几百米先别怀疑数据错位十有八九是投影坐标和地理坐标混用了。判断方法很简单加载图层后查看图层的坐标参考系统。如果某个图层显示的是 WGS 84 经纬度而其他图层是 CGCS2000 3 度带投影那它们在地图上就会错位。另外一个常见问题是部分图层标注的是 CGCS2000但实际生产时用的可能是西安 80 或北京 54 的坐标骨架——这种情况用肉眼很难识破需要找一组已知坐标的控制点做精度校核。3.2 快速坐标系的识别与转换实操QGIS 和 GDAL 双路线我通常先在 QGIS 里完成第一步筛查再用 GDAL 做批量转换。QGIS 里加载 shp 后右键图层 → 属性 → 信息栏里能看到完整的坐标参考系统描述。如果发现坐标系不统一QGIS 的“导出 → 保存要素为”对话框里直接指定新的 CRS这种方式直观但效率低只适合一两个文件。数据多的时候用 GDAL 命令行做批量处理更现实# 批量把 Shapefile 从原始坐标系转成 CGCS2000 / 3度带投影 for f in roads_data/*.shp; do ogr2ogr -t_srs EPSG:4547 \ -overwrite \ ${f%.shp}_prj.shp \ $f done参数说明-t_srs指定目标坐标系EPSG:4547 是 CGCS2000 / 3-degree Gauss-Kruger zone 25中央经线 75°E如果你要处理的内蒙古东部区域数据zone 26EPSG:4548和 zone 27EPSG:4549才是对应的。-overwrite表示输出文件已存在时直接覆盖避免交互提问影响批处理。这里提醒一句不要盲目用 EPSG:4547 一把梭转完全部数据——如果原始数据是 4326 经纬度WGS 84转到 4547 投影没问题但如果原始数据已经是另一种投影了比如兰伯特投影或阿尔伯斯投影你需要先搞清楚原始投影的参数否则一转换又会在边界区域引入明显变形。4. 道路分级体系在数据里怎么组织国道、省道、县道、乡道的字段筛选逻辑4.1 分级数据的字段设计看懂“等级”字段到底存了什么内蒙古的乡道级矢量和公开地图数据的最大区别就是它带有详细的行政等级字段。常见的设计是属性表里有一个“等级”或“道路等级”字段值域范围包括“高速”“国道”“省道”“县道”“乡道”这样。但不同生产单位对字段的命名习惯差异很大有的叫ROAD_CLASS有的叫TYPE还有的用数字代码存1-高速2-国道3-省道4-县道5-乡道。拿到数据后先不做任何筛选全量读一遍属性表。我用下面这段 Python 脚本快速统计每个等级字段的值分布这比肉眼一行行看效率高得多import geopandas as gpd gdf gpd.read_file(roads_data/merged_roads.shp, encodingutf-8) # 假设等级字段叫 LEVEL print(gdf[LEVEL].value_counts(dropnaFalse))这里能看到字段里有多少空值、多少未知代码。如果出现大面积的空值先别急着填充检查是不是字段名搞错了——比如真实字段是ROAD_LEVEL而LEVEL是另一个无关字段。value_counts(dropnaFalse)这个参数一定要加否则空值那一类直接不显示你会误以为数据很干净。4.2 用领域驱动的方式筛选乡道SQL 表达式替代手动选择知道了等级字段的值域后下一步就是把乡道单独筛出来做分析。在 QGIS 里你可以用“按表达式选择要素”功能输入LEVEL 乡道或LEVEL 5取决于字段类型是字符串还是整型然后导出选中要素为新图层。这种方式对一次性操作没问题但如果后续更新数据或者你有多个 mxd/qgz 工程要同步用脚本更省心。# 筛选乡道图层并单独输出 import geopandas as gpd gdf gpd.read_file(roads_data/merged_roads.shp, encodingutf-8) township gdf[gdf[LEVEL].astype(str) 乡道] # 输出为新的 shapefile township.to_file(outputs/township_roads.shp, encodingutf-8, driverESRI Shapefile)注意astype(str)这一步很关键。如果原始字段是整型而你把筛选条件写成了字符串乡道判断会全部返回 False筛出来一个空图层这是新手最容易踩的坑。先print(township.shape)验证行数不为零再执行to_file导出。导出的编码参数encodingutf-8保证中文字段名在后续加载时不需要再折腾。筛选后的乡道数据建议先叠加一层内蒙古自治区行政边界做空间裁剪因为原始数据包里的道路要素偶尔会跨界出到区外几公里影响后续里程统计。裁剪操作用 QGIS 的“裁剪”工具或者gpd.clip(township, boundary)都能完成。5. 质量检查与避坑指南拓扑错误、字段污染和坐标系混淆的常见翻车点5.1 道路图层拓扑检查断头路和自相交的检测方法矢量道路数据和影像数据不一样它不仅有空间位置信息还隐含着拓扑关系。乡道数据在采集和处理过程中最常见的拓扑问题是悬挂点断头路和自相交。断头路会让路径分析时明明应该连通的路网断成两截自相交则会让里程计算重复计了一段路。QGIS 里用“拓扑检查器”面板可以快速发现这些问题。加载乡道图层后打开“矢量 → 拓扑检查器”规则选择“不能有悬挂节点”。容差设置为 0.001 度如果是经纬度坐标系或 1 米投影坐标系。跑一遍之后所有断头点都会列出来。如果你是命令行爱好者用 PostGIS 的ST_IsSimple()函数做自相交检测更彻底-- 检测乡道图层中是否存在自相交几何 SELECT gid, ST_IsSimple(geom) AS is_simple FROM township_roads WHERE ST_IsSimple(geom) false;这段 SQL 会返回所有不自相交的要素编号。如果结果为空说明几何质量没问题如果有记录把对应要素导出来在 QGIS 里定位检查看是采集错误还是数据接边时产生的碎线。处理自相交我一般用 QGIS 的“修复几何”工具批量处理避免手动一个个去改。5.2 属性字段污染里程字段是文本型数字引发的统计错误乡道数据的属性表里通常会有一个“里程”字段存这条路的长度信息。很多生产单位导出数据时这个字段被存成了文本类型比如12.5这种带引号的字符串。直接做统计分析时sum 聚合出来的结果会变成 0 或者报类型错误。import pandas as pd # 将里程字段统一转为数值类型 gdf[length_km] pd.to_numeric(gdf[length_km], errorscoerce) # 检查转换后是否存在 NaN原数据里有无法解析的文本 bad_rows gdf[gdf[length_km].isna()] print(f无法解析的记录数: {len(bad_rows)})参数说明errorscoerce会把无法解析成数字的值替换成 NaN这样你就可以定位到是哪一条记录出了问题而不至于让整个聚合操作因为一个脏数据而中止。转换完成后我还习惯检查一下里程值的量纲——有的数据里存的是米有的存的是公里混在一起做距离计算时结果会差 1000 倍这种错误在最终成果里很难被一眼发现。5.3 坐标系边界区域的拼接缝乡道数据在盟市交界处的离奇跳变处理内蒙古多盟市数据时你可能会遇到一个问题同一道路在两个盟市的数据拼接处出现几十米的错位。这不是数据本身错了而是不同盟市生产数据时用了不同的坐标基准。识别方法是在 QGIS 里把乡道图层的坐标显示切到经纬度然后沿着盟市边界线放大检查。解决方式一般是统一到 CGCS2000 坐标系然后用边界的缓冲区域做一次重新捕捉snap。具体操作是 QGIS 的“矢量 → 几何工具 → 捕捉几何至极点”距离设置为 10 米即可。如果错位超过 50 米先检查生产单位是否误用了 WGS 84 与 CGCS2000 的混合数据这种情况下需要重新从源数据提取接边区域而不是简单做捕捉。5.4 属性编码不统一同一县级市省道代码两种写法的噩梦最后一个是字段值域不统一的问题。有的图幅里“省道”写成“S”有的写成“省道”还有的写成数字“2”。这个问题在把不同区县的数据合并到一个总表时一定会冒出来。我一般是在合并前先跑一次获取所有唯一值看清楚到底有多少种写法再手工做映射# 查看等级字段的所有唯一值 print(gdf[LEVEL].unique()) # 标准化映射 mapping { S: 省道, 省道: 省道, 2: 省道, X: 县道, } gdf[LEVEL_NORM] gdf[LEVEL].astype(str).map(mapping)如果映射后的字段出现 NaN就说明还有没处理到的编码检查原始值再补进映射表里。这套操作做完数据才能进入后续的分析流程否则你辛苦统计出来的各级道路里程数可能偏得离谱。6. 进阶用法把乡道矢量数据变成路径规划可用的路网拓扑数据整理干净之后它能发挥的最大价值不是画几张专题图而是进入路网分析流程。比如你要做一个乡镇到最近高速路口的可达性分析或者设计一条物流配送线路这时候光有 shp 文件是不够的需要把线要素转成拓扑网络结构。PostgreSQL PostGIS pgRouting 是常见做法也适合乡道这种数据量较大的场景。-- 创建拓扑网络容差设为 0.00001 度 SELECT pgr_createTopology(township_roads, 0.00001, geom, gid); -- 为道路表添加起始点和终点编号 ALTER TABLE township_roads ADD COLUMN source INT; ALTER TABLE township_roads ADD COLUMN target INT; SELECT pgr_createTopology(township_roads, 0.00001, geom, gid, source, target);参数说明pgr_createTopology的核心参数是容差。如果路网数据已经做过节点捕捉这个值可以设小一点比如 0.00001约 1 米如果数据接边质量一般容差调到 0.0001 会更稳妥但可能把邻近但不相交的道路错误地连起来所以先跑一遍拓扑检查再决定容差更好。拓扑建好后就可以用pgr_dijkstra做最短路径分析。乡道数据里包含大量低等级道路这些道路的实际通行速度与国道、省道差距很大所以做路径规划时最好给不同等级道路配不同速度属性而不是单纯按几何距离计算。我把速度字段加在道路表里然后用时间作为成本函数这样算出来的路径才真正适合物流场景。我在这个环节吃过一次亏当时直接拿几何长度当成本跑完最短路径结果推荐路线穿过一段乡间土路实际通行时间反而比走省道多了一个小时。后来学聪明了先在属性表里给每种道路等级标了通行速度再用length_m / speed_kph算出每条道路的时间成本才让规划结果靠谱起来。这次处理数据的过程里还有一个深刻教训无论生产单位给的说明文档怎么写字段是标准的自己都要上手验证一遍。数据在多次转换、接边、合并之后属性结构和坐标系都可能被改得面目全非只有实际跑一遍检查脚本才能确认它真的可以用。希望帮到你去做路网分析时少走一段弯路。本文还有配套的精品资源点击获取