简介:2020年合肥公交系统矢量数据,涵盖189条公交线路和4700余个公交站点,面向GIS开发者、城市规划人员及交通研究者,可用于空间分析、地图制作与公共交通网络评估。RAR压缩包共12个文件,包含shp几何、dbf属性、prj坐标等标准Shapefile组件,整体仅828KB,已有249人学习浏览。线路数据记录了线路名称、起止点、始发与停运时间、方向及总距离;站点数据包含站点名称、经纬度及所服务的公交线路,坐标遵循WGS1984。借助这些数据可进行公交覆盖范围计算、短路径分析、线路布局优化,并支撑电子地图更新与实时公交查询,为城市交通研究和规划提供可靠基础。
1. 合肥公交矢量数据包:先理文件,再动手分析
拿到“合肥市公交.rar”之后,先别急着往ArcMap里拖。压缩包里的.shp、.dbf、.sbn这些文件,每一个都不只是后缀:它们共同构成一份2020年合肥公交网络的矢量切片。线路层面有线路名称、起止点、始发/停运时间、上下行方向和线路总距离,站点层面有站点名称、WGS1984经纬度和所服务线路,整体覆盖189条公交线路、4700多条站点记录。对做城市规划、通勤OD分析或电子地图更新的人来说,这份数据能直接用来算站点覆盖率、找高重复度线路,也可以把Excel经纬度批量转成shp并落图。适合会用GIS或者geopandas的读者,后续从文件解析、字段清洗、空间计算到出图,一步步跑通一遍。
2. 拆包shapefile:文件结构、属性表与坐标参考
2.1 十几个文件里,哪些必须一起拷走
“合肥市公交_线路.shp”“合肥市公交_线路.shx”“合肥市公交_线路.dbf”这一类命名,说明数据是典型的ESRI Shapefile格式。Shapefile从来不是一个单文件格式,主文件名相同、后缀不同的一组文件共同构成一份完整数据。压缩包里列出十几个文件,其实都在发挥不同作用。
| 文件名 | 类型 | 作用 | 分析时需要 |
|---|---|---|---|
| 合肥市公交_线路.shp | 主文件 | 保存线路的几何信息 | 必须 |
| 合肥市公交_线路.shx | 索引文件 | 记录几何要素在.shp中的偏移量 | 必须 |
| 合肥市公交_线路.dbf | 属性表 | 保存线路名称、起止点、首末班时间、方向、总距离 | 必须 |
| 合肥市公交_线路.prj | 坐标参考文件 | 声明WGS1984坐标系 | 必须 |
| 合肥市公交_线路.sbn / .sbx | 空间索引 | ArcGIS生成的索引,QGIS不依赖 | 可选 |
| 合肥市公交_线路.shp.xml | 元数据 | 数据说明文档 | 可选 |
| 合肥市公交_点.* | 站点几何与属性 | 保存站点名称、经纬度、线路信息 | 必须 |
压缩包里的站点文件没有.sbn/.sbx,这不奇怪。SBN和SBX是ArcGIS对shp做空间索引时附带生成的,很多从QGIS导出的shp根本没有这两个文件。常见坑是文件传输过程中,网盘或邮件系统把.dbf当成表格文件单独拦截,最后只剩.shp和.shx,数据读出来只有几何没有属性。
所以拿到压缩包的第一步,是核对主文件的四个核心后缀是否齐全。缺.prj时,坐标参考信息丢失,数据叠加到其他图层可能直接偏到海里;缺.dbf时,字段全部消失,这份数据就退化成一张单纯的地图底图。
2.2 从dbf字段看数据能回答什么问题
Shapefile的属性表部分就是一份dBASE表。这份数据的两个dbf文件,线路和站点各承担不同分析任务。
| 属性表 | 原始字段方向 | 能得到的分析结果 |
|---|---|---|
| 合肥市公交_线路.dbf | 线路名称 | 运营线路数量、线路集合、按编号排序 |
| 起点 / 终点 | OD关系、跨区域线路识别 | |
| 始发时间 / 停运时间 | 运营时长、跨零点线路、首末班间隔 | |
| 方向 | 上下行拆分、双向覆盖判断 | |
| 总距离 | 平均站距、线网密度 | |
| 合肥市公交_点.dbf | 站点名称 | 站点去重、共站识别 |
| 经度 / 纬度 | 坐标定位、投影转换 | |
| 线路名称 | 站线关联、换乘查询 |
站点表不能直接按行数当成站点个数用。4700多条记录里存在大量同名同站、多线路共站的情况,同一个“大东门”站点可能对应十几条线路的记录。做站点密度分析前,必须按站点名称去除重复,或者明确保留每条线路的停靠记录。
字段名在shapefile里有实际限制,传统dBase格式通常最多十个字符。原始字段可能已经做过缩写,比如“始发时间”显示成“start_time”,“停运时间”显示为“end_time”。分析前先打印真实列名,不要凭直觉写字段。
2.3 PRJ文件与WGS1984坐标参考
线路和站点各自都有一个.prj文件,里面保存的是坐标参考信息。用文本编辑器打开线路的prj,能看到类似下面的ArcGIS风格WKT:
GEOGCS["GCS_WGS_1984", DATUM["D_WGS_1984", SPHEROID["WGS_1984",6378137.0,298.257223563]], PRIMEM["Greenwich",0.0], UNIT["Degree",0.0174532925199433]]这段WKT对应的就是EPSG:4326,也就是GPS设备直接采集的WGS1984经纬度坐标系。经纬度字段的单位是十进制度,不是度分秒。合肥市区经度在117度附近,纬度在31度附近,读取后看到坐标是这个量级就正常。
这里有个实际差异值得注意:WGS1984和CGCS2000的坐标差在1米左右,做公交站点缓冲覆盖、线路长度统计这类中观分析,直接叠加误差可以忽略。但如果拿这份数据和毫米级的规划审批数据叠加,就要统一到同一坐标参考系,最稳妥的是用CGCS2000参考框架做一次基准转换。
3. 用geopandas读入线路与站点,完成字段清洗与shp生成
3.1 读取并确认crs和列名
GeoPandas底层通过Fiona和GDAL读取shapefile,几行代码就能把线路和站点读进来。这里的关键不是读,而是读完后第一步要做什么。
import geopandas as gpd lines = gpd.read_file("合肥市公交_线路.shp", encoding="utf-8") stops = gpd.read_file("合肥市公交_点.shp", encoding="utf-8") print("线路CRS:", lines.crs) print("线路字段:", lines.columns.tolist()) print("站点字段:", stops.columns.tolist()) print("线路数量:", len(lines), "站点数量:", len(stops))输出中CRS应该显示为EPSG:4326。如果显示None,说明prj文件没被识别,需要手动补一句lines = lines.set_crs("EPSG:4326"),否则后面做投影和面积计算都会报错。encoding参数第一次可以传utf-8,如果中文出现“鍏氳”这类乱码,就改成gbk重新读。这里的核心是优先打印columns,而不是直接按示例列名操作。
字段名是这份数据最容易翻车的地方。实际文件字段可能已经被截断成“start_time”“end_time”或者“名称”“首站”这类别名,只有先看到真实列名,后续重命名才有依据。
3.2 时间字段与方向字段的规范化
线路表里的始发时间和停运时间,读进来大概率是字符串格式。要做运营时长计算,先把时间转成当天的分钟数。
def to_minutes(t): parts = str(t).split(":") return int(parts[0]) * 60 + int(parts[1]) lines["首班_min"] = lines["始发时间"].map(to_minutes) lines["末班_min"] = lines["停运时间"].map(to_minutes) lines["运营时长_min"] = lines["末班_min"] - lines["首班_min"]逻辑说明:"06:00"被拆成小时和分钟,再换算成360分钟。转成整数后可以做排序、求均值、找最早首班线路。时间格式如果不是纯HH:MM,比如带有“06:00:00”甚至“6:00”,函数需要先截断或者补齐。
注意:运营时长出现负值时,不要直接判定数据错误。比如首班05:30、末班23:50,两条线在当天同一周期内实际上不交叉,负值代表末班已过零点。这种线路要加1440分钟再处理。
方向字段可以映射成编码,方便后续按上下行拆分比较。合肥公交线路的方向值一般是“上行”和“下行”:
lines["方向编码"] = lines["方向"].map({"上行": 1, "下行": 2})线路方向不是非得和物理几何方向严格一致。“上行”通常指从起点站到终点站的方向,但shp里的线要素在被编辑保存时可能被翻转,导致实际几何走向反了。如何校验几何方向落在最后一章。
3.3 Excel经纬度批量生成shp:把技能迁移到自己的数据
这份合肥公交数据自带站点经纬度,可以直接做GIS分析。而更常见的需求是把一张Excel表格里的经纬度字段导入ArcMap并生成shp,这个流程同样可以在geopandas里完成。
import pandas as pd import geopandas as gpd from shapely.geometry import Point df = pd.read_excel("站点坐标.xlsx") geometry = [Point(x, y) for x, y in zip(df["经度"], df["纬度"])] gdf = gpd.GeoDataFrame(df, geometry=geometry, crs="EPSG:4326") gdf.to_file("导出站点.shp", encoding="utf-8")Point()的第一个参数是经度,第二个参数是纬度,这个顺序不要调换。GeoDataFrame构造时直接指定crs,确保输出带有.prj文件。如果最终成果要放到ArcMap里,encoding可以改成gbk,否则中文属性字段会乱码。
把这段流程和前面读取公交线路对比,本质上是一样的:先有坐标点,再赋坐标参考,最后落盘成shp。理解这个链路后,拿任何Excel经纬度表格生成矢量数据都没有障碍。
4. 公交网络的空间统计与可视化:叠加、覆盖与出图
4.1 先选定投影,再做600米步行接驳覆盖
站点覆盖分析是公交数据最基础的应用。原始数据是WGS1984经纬度,单位是度,没法直接对buffer函数传“600米”。先把数据投影到以米为单位的坐标系,再算缓冲区。
| 分析目的 | 建议EPSG | 使用说明 |
|---|---|---|
| 全城尺度缓冲与密度 | EPSG:32650 | WGS84 UTM 50N,覆盖合肥所在经度带 |
| 与CGCS2000测绘成果叠加 | EPSG:4547 | CGCS2000 3度带投影 |
| 只叠加不量算 | 保留EPSG:4326 | 经纬度直接用 |
以600米步行接驳半径为例:
stops_proj = stops.to_crs("EPSG:32650") buffer_geom = stops_proj.geometry.buffer(600) coverage = buffer_geom.union_all() print("站点数量:", len(stops_proj)) print("600米覆盖面积(km²):", round(coverage.area / 1e6, 2))buffer()的单位跟随当前坐标系,投影后果就是米。600米是步行接驳的常用阈值,400到800米在城市公交规划里都很常见。union_all()把全部站点缓冲面合成一个整体,消除重叠区域后再算面积,避免同一个小区的被三个站点覆盖算三遍。
如果使用的GeoPandas版本较老,union_all()不可用,可以改成:
from shapely.ops import unary_union coverage = unary_union(buffer_geom)算出来的面积只代表覆盖范围,不代表公交服务能力。站点密度很高的中心城区覆盖面积连成片,看起来数值很大,还要结合线路走向一起解释。
4.2 空间关联:把每条线路的站点计数补回去
站点表里已经有线路名称字段,直接用groupby做计数也能知道每条线路上有多少站点记录。但这个字段在数据录入时可能有遗漏,空间关联是更完整的一条验证路径:把站点缓冲成小面,与线路图层做空间连接。
from geopandas import sjoin stops_buff = stops_proj.copy() stops_buff["geometry"] = stops_buff.geometry.buffer(30) lines_proj = lines.to_crs("EPSG:32650") joined = sjoin(stops_buff, lines_proj, how="inner", predicate="intersects") line_stop_count = joined.groupby("线路名称").size().reset_index(name="站点数")30米缓冲用于消除站点和线路之间的微小偏移。现实中站点坐标经常落在路缘石或人行道,线要素又在路中心,两者不严格相交,不加缓冲会把大量站点漏掉。predicate="intersects"是GeoPandas较新版本的写法,老版本里用op="intersects",读代码时注意版本差异。
拿到站点数后,可以把线路的总距离字段合并进来,估算平均站距:
merged = line_stop_count.merge( lines[["线路名称", "总距离"]], on="线路名称", how="left", ) merged["平均站距米"] = merged["总距离"] * 1000 / (merged["站点数"] - 1) print(merged.sort_values("平均站距米").head())这里假设总距离字段的单位是公里。如果打印出来发现数值只有几十,说明单位本来就是米,不要再乘1000。用sort_values看头部线路,站距明显偏大的通常是城市外围的快速线或远郊线路,站距接近的是中心城区加密线。
4.3 可视化出图:点线叠加与密度热区
用matplotlib把站点和线路画在一张图上,只需要两个plot调用:
import matplotlib.pyplot as plt fig, ax = plt.subplots(figsize=(14, 10)) stops.plot(ax=ax, markersize=1.2, color="#e53e3e", alpha=0.5) lines.plot(ax=ax, linewidth=0.8, color="#2b6cb0", alpha=0.7) ax.set_title("合肥公交站点与线路分布 2020") ax.set_axis_off() plt.savefig("合肥公交分布.png", dpi=300, bbox_inches="tight")绘图顺序很重要:先画站点,再画线路,让线压点在站点上面。如果先画线再画点,大量红色站点符号盖住蓝色线路,图的层次直接乱掉。alpha透明度的作用是让密集区域的点叠色变深,直观看出站点聚集区。
中心城区的点云会在图上形成一片连续的红色高亮块,外围只能看到沿主干道分布的细线。要做更精确的密度判断,建议再叠加一张hexbin六边形分箱图,把点密度分布转成连续色带,输出效果比离散点更适合作汇报材料。
5. 进阶排错与组合应用:起止点校验、OD与运营字段合并
5.1 起点终点字段与几何方向的一致性校验
线路属性表提供“起点”和“终点”,但这不代表线几何方向就一定正确。shp在ArcGIS里被编辑、融合、翻转之后,几何走向可能和字段描述不一致。做OD方向分析前,先跑一遍一致性校验:
def direction_check(row, stop_gdf): geom_coords = list(row.geometry.coords) first_pt = Point(geom_coords[0]) last_pt = Point(geom_coords[-1]) start_stop = stop_gdf[stop_gdf["站点名称"] == row["起点"]] end_stop = stop_gdf[stop_gdf["站点名称"] == row["终点"]] if start_stop.empty or end_stop.empty: return "站点无匹配" d_first_start = start_stop.geometry.iloc[0].distance(first_pt) d_last_end = end_stop.geometry.iloc[0].distance(last_pt) d_first_end = end_stop.geometry.iloc[0].distance(first_pt) d_last_start = start_stop.geometry.iloc[0].distance(last_pt) return "正常" if (d_first_start + d_last_end) < (d_first_end + d_last_start) else "反向" stop_centroid = stops_proj lines["方向校验"] = lines.apply(lambda r: direction_check(r, stop_centroid), axis=1)代码的逻辑是:对比线首尾到“起点站/终点站”的距离之和,判断现有几何走向和字段描述哪边更近。如果校验结果是“反向”,说明线要素沿相反方向存储。遇到反向线路,有两种处理方案:二是做OD输出时不使用几何方向,直接按起点字段为准;二是把几何反转,改成和起点终点字段一致的方向。这个校验在公交仿真、客流建模前非常重要,一次批量翻转就能避免后续所有线路走向统计错一半。
5.2 OD组合与运营时段合并输出
把矢量数据还原成可查询的OD表和运营表,是这份数据最后一步常见的加工。筛选出关键字段,组合成一张窄表:
od_table = lines[["线路名称", "起点", "终点", "方向"]].copy() od_table["OD"] = od_table["起点"] + "->" + od_table["终点"] + "(" + od_table["方向"] + ")" od_table["运营时段"] = lines["始发时间"].astype(str) + "-" + lines["停运时间"].astype(str) od_table.to_csv("合肥公交OD对.csv", index=False, encoding="utf-8-sig")CSV导出给Excel用时,utf-8会显示乱码,utf-8-sig是更稳妥的选择。OD列可以直接做value_counts找平行重复线路:如果两条线路的OD标识完全一致,说明它们在起终点和方向上高度重合,这类线路呈现明显的平行共线特征,是公交线网优化中最值得检查的对象。
本文还有配套的精品资源,点击获取