简介:武汉市路网矢量数据shp,是面向ArcGIS用户在空间分析与地图制图场景下的基础数据包。整套数据涵盖武汉市域道路网络、13个行政区边界及全市行政边界三个矢量图层,道路线要素与区划面要素分层存储、互不干扰,便于直接加载至工程文件进行路网密度分析、交通可达性计算或专题图制作;需注意该路网仅包含陆路交通线,不含地铁、铁路、水路、航空等要素。压缩包内共21个文件,总大小3.45MB,每个图层均配套shp、dbf、shx、sbn、sbx、prj及xml等文件,其中shp存储几何图形、dbf保存属性字段、prj记录坐标系参数,可保证在ArcGIS各版本中正常读取与显示。该包目前已有2928人学习下载。对于需要武汉市基础矢量底图的城乡规划、地理信息专业学生或研究人员而言,这份数据省去了自行描图与坐标纠偏的时间,解压后即可用于课堂练习、日常制图或科研底图叠加。
1. 武汉市路网矢量数据shp:一份能直接拖进ArcGIS的底图路网
做交通可达性分析、缓冲区计算和网格选址时,最耽误事的往往不是模型算法,而是没有一张能直接用的路网底图。武汉市路网矢量数据shp,就是一份ArcGIS能直接打开、直接参与分析的线要素shp,属性表里带道路名称和等级分类,不用再去在线地图里手工描路。它解决的是最实际的问题:把路网拖进分析窗口,马上开始做覆盖范围、路径规划和落位验证。适合城市规划、交通分析、物流选址,以及任何需要市域路网做底图的GIS场景。我拿到这类数据的第一个动作,永远是先查字段和坐标系,因为坐标系错一步,后面所有分析结论都得跟着翻车。
2. 字段与坐标系:这份路网shp的底细要搞清
拿到手先在ArcGIS里打开属性表,别急着缩放看图形,先把表头过一遍。shp的字段结构决定了你能做哪些分析、哪些事还得自己补字段。我拆过的这种市域路网shp,字段大同小异,但命名经常不统一,某份叫CLASS,另一份可能叫ROAD_CLASS或GRADE,所以第一步是确认字段含义,而不是默认某个字段名一定存在。
2.1 字段清单:哪些能直接用,哪些是摆设
下面这份字段对应关系是这类路网数据的常见结构,你可以拿自己的数据对照着看:
| 字段名 | 类型 | 说明 | 使用建议 |
|---|---|---|---|
| OBJECTID | 整型 | 要素唯一编号,ArcGIS内置 | 做连接、关联字段时用 |
| NAME | 文本 | 道路名称,部分含中文和拼音 | 可直接用于出图标注 |
| CLASS | 整型 | 道路等级:1高速、2快速路、3主干道、4次干道、5支路 | 按等级渲染、筛选的核心字段 |
| SHAPE_LENG | 双精度 | 线段长度,单位米 | 统计里程时直接求和 |
| SPEED | 整型 | 限速值(km/h) | 做网络分析时间成本时用 |
| LANES | 整型 | 车道数 | 部分线段为空,使用时先查缺失 |
这里头最容易翻车的是SPEED和LANES。市域路网往往只把主干路以上的道路补全了限速和车道数,支路字段是空的。如果你要在网络分析里用时间成本,最好先做一遍字段统计,看一下空值比例。空值超过30%就不要直接用,否则路径规划在支路上会算出异常时间。CLASS字段也不是永远连续的,我见过有的数据把高快速路、匝道全部揉在CLASS=1里,实际分层要自己在ArcGIS里重新聚合。
SHAPE_LENG的坑藏在单位里。理论上shp保存的是投影坐标下的米制长度,但有些生产流程会把单位写成千米甚至度,直接求和会出大问题。我拿到shp后的第一个验证动作,就是抽一条已知长度的主干道,对照SHAPE_LENG看看量级对不对。一个简单办法:随便框选一段两三公里的快速路,看属性表里长度是不是两三千的量级。如果显示的是几十或者0.几,单位就有问题。
2.2 坐标系:从prj文件里看门道
shp的坐标系不写在属性表里,而是藏在同名的.prj文件里。很多人下载完shp直接拖进ArcGIS,图形能显示,就跟别的图层套不上,大概率是坐标系没搞清楚。我手头这份武汉市路网数据,用的是CGCS2000高斯克吕格投影,3度分带,中央经线114°E。这类投影坐标在国内基础地理数据里很常见,ArcGIS Pro里一般能从图层属性—源—坐标系直接识别出来。
如果你拿到的是WGS84经纬度坐标系,prj文件里会出现GCS_WGS_1984,点坐标范围是经度113°多、纬度30°多,而不是几十万几百米的大数字。判断方法很简单:打开图层属性看坐标单位——单位是Meters,是投影坐标;单位显示Decimal Degrees或角度单位,就是地理坐标。两者能不能混用?短时间做可视化底图可以凑合,但要算长度、缓冲区和面积,就必须统一到投影坐标,否则计算结果是度数,没法直接用。
注意:CGCS2000和WGS84在平面上差几十米是正常现象,两个坐标系在多数城市范围内偏移量并非完全相同,别再纠结到底哪个绝对正确,关键是在一个工程里所有图层统一用同一种坐标。
2.3 几何细节:路网断没断开,决定分析上限
几何层面的“底细”比字段更隐蔽。路网shp的属性表再漂亮,如果线段在交叉口没有打断,做网络分析就是白搭。所谓打断,指的是两条路十字相交时,双方都在交点处有端点,而不是一条路从上到下穿过、另一条路搭在上面。ArcGIS的网络分析构网时,只有端点重合的线才能连通。我见过一份城区路网,环线居然是一条完整闭合线,所有放射路叠在上面但彼此不连通,路网画得好看,一算路径就死。
验证打断情况有两个办法。一是缩放到大交叉口,肉眼观察节点处有没有端点符号;二是用ArcToolbox里的“在相交处打断线”(Split Line at Intersections)工具,输入线图层跑一遍,看输出的要素数量是不是比原来多了不少。如果打断后数量明显增加,说明原始数据里大量道路是贯穿线,直接拿去做网络分析会绕路、断头,必须先打断再建网络数据集。
另一个几何细节是路线是否双线。城市快速路有的用中心线表达,有的用上下行双线表达。中心线数据适合底图和宏观分析,双线数据对车道级路径规划有帮助,但处理和拓扑复杂度更高。这份武汉市路网数据用的是单线中心线,对做缓冲区、可达性、路网密度统计来说,单线反而更省事,不用处理双线之间的面状间隙。
3. ArcGIS实操:加载、符号化、按等级重算字段
把这份shp用起来并不难,难的是用对。下面这套流程是我处理同类路网数据的标准动作,走完一遍之后,无论是自己分析还是出图汇报,都够用。
3.1 加载后先做三件事
把shp拖进ArcGIS之后,我一般按顺序做三件事:看坐标系、看字段结构、看要素范围。第一步在图层属性—源—坐标系里确认投影;第二步打开属性表,检查字段是否齐全;第三步右键图层—缩放至图层,看范围是否与武汉市中心城区吻合。这三步做完,这份数据能不能用心里就有谱了。
这里特别提醒,要素范围检查别只盯着地图中心点。武汉的行政区划跨长江、汉江,路网数据如果覆盖全境,范围包含整个市域;如果只覆盖中心城区,范围会明显偏小。你拉到图层范围后对比一下边界,能判断这份数据取的是全量还是局部,后面做分析时才能避免“以为自己有全市路网,结果郊区全是空白”的误判。
3.2 按CLASS字段符号化:一张能直接汇报的图
路网数据默认加载进来是一堆黑色单线,没法看。第一件事是右键图层—符号系统,把“单一符号”改成“唯一值”,字段选CLASS。这样高速、快速路、主干道、次干道、支路会分开颜色。我习惯用绿—深黄—橙—浅黄的色带,高速用绿色,支路用浅灰色,这样地图视觉重心自然落在高等级路网。
符号化不只是好看,还能反过来验证字段质量。如果符号系统里某一类出现几千条记录,另一类只有几十条,大概率是字段归并不符合实际情况,比如匝道被划进高速类。这时候我一般回到属性表,用按属性选择,把该等级要素全部选中,对比一下影像看是否存在明显误分。符号化这一步做得越细,后面出图越省事。
分级渲染还有一个用途:快速识别断头路和异常飞线。缩放到全市范围,如果看到某一等级的道路线条突兀地飞出建成区,往往是数据合并时的拓扑错误。先记下来,后面用“修复几何”工具统一处理。
3.3 字段计算器重分类和长度统计
有些分析场景不需要五个等级,只要“高快速路”和“普通道路”两档。这时就用字段计算器,对CLASS做一次重分类。在ArcGIS字段计算器里,选择Python解析器,在Pre-Logic Script Code里写一个函数,然后在表达式框里调用:
# 字段计算器:按CLASS重新编码为两组 def reclass(cls): if cls in (1, 2): # 高速 + 快速路 return 1 if cls == 3: # 主干道 return 2 if cls in (4, 5): # 次干道 + 支路 return 3 return 0 # 在表达式框里填: # reclass(!CLASS!)逻辑说明:把原字段CLASS里的五个等级压缩成三个,减少后续分析里的分类噪声。参数说明:cls in (1, 2)是Python的写法,等价于cls == 1 or cls == 2;如果原字段里含有空值或0,函数会走到最后的return 0,这样看得见异常值,不至于静默丢掉。
长度统计我一般不用SHAPE_LENG直接求和,而是用字段计算器另建一个LENGTH_M双精度字段,表达式填!shape.length!。在投影坐标下,!shape.length!是ArcGIS实时计算的几何长度,单位是米,不受原始存储字段的影响。SHAPE_LENG留作对账,两边数值一对比,能发现原始数据是否做过投影变换或单位换算。如果两个字段结果差很多,说明原始长度字段不可信,统计里程时改用!shape.length!。
4. 避坑:坐标系、乱码、碎线是三个最常翻车的点
这份路网数据我实际用过三轮,每一轮都在不同环节踩过坑。下面四条是出现频率最高、影响最恶劣的问题,按“现象—原因—解决”拆清楚。
4.1 坐标系认错,全线白搭
现象:shp拖进ArcGIS之后,路网和影像底图叠不上,差出几百米甚至更远;或者路网图层单独看着没问题,一跟其他数据叠加就横跨整个地图。
原因:大多数情况是.prj文件缺失,ArcGIS无法识别坐标系,默认按未知坐标处理;也有情况是数据生产时用了WGS84,但.prj里写的是CGCS2000,两个球体差异导致平面偏移。
解决:先用图层属性查看坐标系。如果是“未知”,做法是打开ArcToolbox—数据管理—投影与变换—定义投影,手动指定坐标系。指定之前你得判断这份路的实际投影——看坐标范围,东经113到115、北纬29到31之间,是WGS84经纬度;坐标是几十万米、几百万米的量级,再结合prj里的CGCS2000、Gauss-Kruger关键词,大概率是CGCS2000高斯投影。定义完投影再用“投影”工具,把它转成你要用的坐标系。我一般统一转成CGCS2000 3度分带投影,跟影像、国土数据都好套。
注意:别用空间校正工具去“硬掰”路网位置,那是治标不治本,会把几何形状拉歪,后续长度统计全部失真。
4.2 属性表中文乱码
现象:NAME字段和部分字段名显示成乱码,常见的是一堆“锟斤拷”或问号,属性表里道路名完全不可读。
原因:shp的属性表本质是dbf文件,国内数据生产常用GBK/GB2312编码存储中文。ArcGIS Pro新版本默认按UTF-8读取,读老数据就会乱码;ArcMap 10.x则相反,有时默认读GBK,换软件打开就乱。
解决:有两个可靠路子。一是数据量小时,在ArcGIS里新建要素类,把乱码字段重新赋值过去,麻烦但直接;二是用Python读,pyshp读取时指定编码encoding="gbk",把中文正常读出来再另存一份UTF-8编码的shp。我在后面的Python章节会给完整脚本。处理完乱码后,记得把NAME字段重新检查一遍,道路名缺失或为空的线段,对做道路标注和地名核对影响很大。
4.3 交叉口没打断,路径分析绕路
现象:做路径规划时,明明两条路十字相交,导航结果却绕了一个大圈才从A到B;做服务区分析,缓冲区范围外出现诡异的断口。
原因:原始路网中,相交道路并没有在交点处打断,线要素只是视觉上交叠,几何上各走各的,网络数据集无法在这两个端点之间建立连通关系。
解决:用“在相交处打断线”(Split Line at Intersections)工具,输入这份路网,容差设成默认或根据数据精度设1米,跑完得到打断后的新线。打断后要素数量会增加,这是正常现象。然后重新加载,再建网络数据集,路径规划立刻就通了。这个操作不会改变线段的平面位置,只会在交点处切分几何,放心用。打断完还要跑一遍“修复几何”,把潜在的重复节点和自相交清理掉。
4.4 新区缺路网,边角空白
现象:某新区地块内的路网是空白,或者只有规划路没有已建路;老城区则一切正常。
原因:数据生产时点早于这些道路建成时间,shp里的路网停留在上一次更新状态。这不算数据错误,但容易被忽略。
解决:拿到shp先别急着迷信它。用在线影像底图做参照,把地图中心切到在建或近期开发片区,扫描一两分钟,凡是影像上有路但shp空白的地方,记录下来。如果这些区域正好落在你的研究范围内,需要手动补充道路。我一般做法是先把现有路网导出备用,按影像描路后合并,再做拓扑检查。若只是做全市宏观密度分析,局部缺口影响有限,不必追求每条路都补齐。
5. Python拆数据:读取、裁剪、导出CSV的完整写法
ArcGIS能处理大多数工作,但遇到批量操作、给非GIS同事出数据,还是Python脚本省心。这里用pyshp库操作shp,它轻量,不依赖ArcGIS环境,任何一台装了Python的机器都能跑。
5.1 用pyshp一次读完整字段和几何
先安装依赖,命令行执行pip install pyshp,然后用下面的脚本读取字段和记录:
import shapefile # 注意带上gbk编码,否则中文NAME字段会乱码 sf = shapefile.Reader("wuhan_road.shp", encoding="gbk") # sf.fields[0] 是内部标记字段,跳过 for field in sf.fields[1:]: print(field) # 打印前5条记录,核对字段内容 for i, rec in enumerate(sf.records()): if i >= 5: break print(rec["NAME"], rec["CLASS"], rec["SHAPE_LENG"])逻辑说明:shapefile.Reader是pyshp的文件读取入口,第二个参数encoding决定dbf字段按什么字符集解码,这里指定gbk才能正确显示中文。字段循环里跳过fields[0],因为这是dbf文件自带的删除标记位,不是业务字段。打印记录时通过字段名访问,pyshp的records()返回有序字典,支持rec["NAME"]这种写法。
参数说明:如果读取报编码错误,把gbk换成cp936,两者在Windows上是同一套编码;如果属性表是UTF-8存储的,则改用utf-8。读出来的字段结构是元组,包含字段名、类型、长度、精度,后面建新shp时要用到。
5.2 按矩形范围裁剪出路网子集
做片区分析时,不必每次加载全市几万条路,按坐标范围裁剪一个子集效率高得多。用pyshp手写一个粗裁剪脚本:
import shapefile sf = shapefile.Reader("wuhan_road.shp", encoding="gbk") # 研究范围左下角、右上角坐标,单位与shp坐标系一致 xmin, ymin = 400000.0, 3350000.0 xmax, ymax = 410000.0, 3360000.0 w = shapefile.Writer("road_clip.shp", shapeType=shapefile.POLYLINE) # 复制字段结构 for field in sf.fields[1:]: w.field(field[0], field[1], size=field[2], decimal=field[3]) count = 0 for shp, rec in zip(sf.shapes(), sf.records()): # 用线段的质心判断,快但不精确 pts = shp.points cx = sum(p[0] for p in pts) / len(pts) cy = sum(p[1] for p in pts) / len(pts) if xmin <= cx <= xmax and ymin <= cy <= ymax: w.shape(shp) w.record(*rec) count += 1 w.close() print(f"裁剪出 {count} 条道路")逻辑说明:遍历所有线要素,用所有顶点的均值作为质心,判断质心是否落在目标矩形内,落在里面就写入新文件。这个写法适合数据量中等、范围不要求精确到边界的场景,比如按指定区域粗略切出路网,再进GIS精修。
参数说明:shapeType=shapefile.POLYLINE定义输出是线要素。w.field逐字段复制原定义,注意size是字符型字段最大长度,decimal是浮点字段小数位,这两个参数如果漏了,输出shp的字段类型会退化。裁剪出来的结果没有处理路网在边界处的打断,边界线可能延伸到矩形外,这是粗裁的局限。要精确裁剪,还是用ArcGIS的Clip工具,或者改用geopandas的clip函数。这段代码适合快速出数据,不适合做最终分析底图。
5.3 导出CSV给不做GIS的同事
很多需求方只要Excel表格和路网清单,不想碰shp。把字段和几何一起倒成CSV,是最通用的交付格式:
import shapefile sf = shapefile.Reader("wuhan_road.shp", encoding="gbk") def line_wkt(shp): # 把一条线要素转成WKT文本 coord_str = ", ".join(f"{x} {y}" for x, y in shp.points) return f"LINESTRING ({coord_str})" with open("road_export.csv", "w", encoding="utf-8-sig") as f: f.write("name,class,length,geometry\n") for shp, rec in zip(sf.shapes(), sf.records()): f.write(f"{rec['NAME']},{rec['CLASS']},{rec['SHAPE_LENG']},{line_wkt(shp)}\n") print("导出完成,共", len(sf), "条道路")逻辑说明:utf-8-sig编码是给Excel准备的,带BOM头,双击打开CSV不会乱码。line_wkt函数把pyshp的points坐标拼成WKT格式的线字符串,这是GIS通用交换格式,导入QGIS、PostGIS都能直接解析。len(sf)返回shp的记录总数,作为校验用。
参数说明:字段名和值直接用f-string拼接,如果NAME里有引号或逗号,预先替换掉,否则CSV表头会错位。路径规划或网络分析专用字段(比如SPEED)需要保留时,照抄一行写进CSV即可。用这段脚本生成的CSV,配合Excel筛选排序,能满足大部分数据核对的场景。
6. 数据质量验证:长度、重复线、影像套合三关
6.1 各等级里程统计
拿到shp后的第一道质量关,是按等级统计总里程。这能同时验证字段完整性和长度字段的可靠性。用pyshp循环累加每个CLASS的总长度:
import shapefile from collections import defaultdict sf = shapefile.Reader("wuhan_road.shp", encoding="gbk") total_by_class = defaultdict(float) for rec in sf.records(): total_by_class[rec["CLASS"]] += rec["SHAPE_LENG"] for cls, length in sorted(total_by_class.items()): print(f"CLASS {cls}: {length / 1000:.1f} km")逻辑说明:defaultdict(float)避免键不存在时报错。按CLASS分组,累加各等级总里程。除以1000把米换成公里,输出更直观。参数说明:如果某个等级输出为0,说明该等级在数据里不存在;如果支路里程远小于主干道里程,要怀疑字段分类是否合理——实际上任何城市的支路里程都应该远高于高等级路,这样反直觉的结果说明数据可能漏了大片支路网。
6.2 重复线检查
路网数据在合并过程中经常出现重复线,同一条道路在文件里保存了两遍或多遍,直接统计里程会翻倍。用脚本按顶点序列找重复:
import shapefile from collections import defaultdict sf = shapefile.Reader("wuhan_road.shp", encoding="gbk") index_map = defaultdict(list) for i, shp in enumerate(sf.shapes()): key = tuple(shp.points) # 顶点顺序完全一致视为重复 index_map[key].append(i) dup_count = 0 for key, idxs in index_map.items(): if len(idxs) > 1: dup_count += len(idxs) - 1 print("重复要素编号:", idxs) print("重复线数量:", dup_count)逻辑说明:把每条线的顶点列表转成元组,作为字典键,完全相同的顶点序列会被分到同一组里。组内有多条记录说明数据重复。输出重复要素编号,方便在ArcGIS里按OBJECTID定位后人工确认。参数说明:这个判断是“完全重复”,方向相反的线(A到B和B到A)不会命中,需要额外处理。做法很简单,检查时把shp.points反转后再比一次。重复线清理是里程统计和网络分析的前置条件,省略这一步,后面的分析结论都会带着系统性误差。
6.3 影像套合:最后一道肉眼关
脚本能找出结构性错误,但路网跟实际道路位置是否吻合,必须靠肉眼做最后校验。我一般把路网叠加在线影像上,把比例尺调整到1:5000左右,从中心城区开始,按环形扫描方式逐片推进。重点看三个地方:跨江桥梁、高架与地面道路重叠段、新建片区道路。
高架和地面道路在shp里往往是两条平行线,如果在影像上只看到高架柱墩而没有地面辅路,却存在两条线,说明数据把同一路段画重了;反过来,影像上已经有路的区域shp里空白,则是时点问题。跨江桥梁需要注意线是否连续跨过江面,有的数据在江边断开,需要补线。这一关没有脚本能替代,但也不用全图扫完,中心城区加三四个新区关键片区就够。
从那以后,我拿到任何一份shp,都不会直接拖进ArcGIS里看效果,而是先跑一遍长度统计和重复线检查脚本,确认字段和几何没有明显问题再进分析流程。这份武汉市路网数据我已经按上面这些方法做过一轮验证,字段和几何结构可以直接用。希望帮到你。
本文还有配套的精品资源,点击获取