ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

合肥公交Shapefile数据处理与GeoPandas空间分析实践

2026/9/16 6:37:58 拓冰建站 浏览量
合肥公交Shapefile数据处理与GeoPandas空间分析实践 简介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风格WKTGEOGCS[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, encodingutf-8) stops gpd.read_file(合肥市公交_点.shp, encodingutf-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, geometrygeometry, crsEPSG:4326) gdf.to_file(导出站点.shp, encodingutf-8)Point()的第一个参数是经度第二个参数是纬度这个顺序不要调换。GeoDataFrame构造时直接指定crs确保输出带有.prj文件。如果最终成果要放到ArcMap里encoding可以改成gbk否则中文属性字段会乱码。把这段流程和前面读取公交线路对比本质上是一样的先有坐标点再赋坐标参考最后落盘成shp。理解这个链路后拿任何Excel经纬度表格生成矢量数据都没有障碍。4. 公交网络的空间统计与可视化叠加、覆盖与出图4.1 先选定投影再做600米步行接驳覆盖站点覆盖分析是公交数据最基础的应用。原始数据是WGS1984经纬度单位是度没法直接对buffer函数传“600米”。先把数据投影到以米为单位的坐标系再算缓冲区。分析目的建议EPSG使用说明全城尺度缓冲与密度EPSG:32650WGS84 UTM 50N覆盖合肥所在经度带与CGCS2000测绘成果叠加EPSG:4547CGCS2000 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, howinner, predicateintersects) line_stop_count joined.groupby(线路名称).size().reset_index(name站点数)30米缓冲用于消除站点和线路之间的微小偏移。现实中站点坐标经常落在路缘石或人行道线要素又在路中心两者不严格相交不加缓冲会把大量站点漏掉。predicateintersects是GeoPandas较新版本的写法老版本里用opintersects读代码时注意版本差异。拿到站点数后可以把线路的总距离字段合并进来估算平均站距merged line_stop_count.merge( lines[[线路名称, 总距离]], on线路名称, howleft, ) 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(axax, markersize1.2, color#e53e3e, alpha0.5) lines.plot(axax, linewidth0.8, color#2b6cb0, alpha0.7) ax.set_title(合肥公交站点与线路分布 2020) ax.set_axis_off() plt.savefig(合肥公交分布.png, dpi300, bbox_inchestight)绘图顺序很重要先画站点再画线路让线压点在站点上面。如果先画线再画点大量红色站点符号盖住蓝色线路图的层次直接乱掉。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), axis1)代码的逻辑是对比线首尾到“起点站/终点站”的距离之和判断现有几何走向和字段描述哪边更近。如果校验结果是“反向”说明线要素沿相反方向存储。遇到反向线路有两种处理方案二是做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, indexFalse, encodingutf-8-sig)CSV导出给Excel用时utf-8会显示乱码utf-8-sig是更稳妥的选择。OD列可以直接做value_counts找平行重复线路如果两条线路的OD标识完全一致说明它们在起终点和方向上高度重合这类线路呈现明显的平行共线特征是公交线网优化中最值得检查的对象。本文还有配套的精品资源点击获取