Geopandas绘制路网时遇'MultiLineString不可迭代'错误求助
复现GitHub上的公交路线优化遗传算法项目时,已修复多数错误,但在绘制路线网络时触发TypeError: 'MultiLineString' object is not iterable错误,该错误指向代码中的self.roads.plot(ax=ax,color='black', linewidth=.5 )语句。打印self.roads.geometry显示其类型为LINESTRING,但仍出现上述错误。当前使用版本为geopandas=0.6.0、threadpoolctl=3.2.0,尝试过搜索该错误及相关方案,但均未解决问题。
相关代码
def plot(self): ''' OUTPUT: plot of routes ''' fig = plt.figure(figsize=(18,12)) for n in range(self.N): # Make LineString Objects route_a_line = self.route_line(self.route_list[n][1]) route_b_line = self.route_line(self.route_list[n][2]) # Make Geo DataFrames route_a_geo = self.transform_to_GeoDataFrame(route_a_line) route_b_geo = self.transform_to_GeoDataFrame(route_b_line) ax = fig.add_subplot(2,2,n+1) ax.set_aspect('equal') print(self.roads.geometry) self.roads.plot(ax=ax,color='black', linewidth=.5 ) self.region.plot(ax=ax, color='orange', alpha=.2) self.bus_stops_geo.plot(ax=ax, color='red',markersize=8) route_a_geo.plot(ax=ax, color='green', linewidth=1) route_b_geo.plot(ax=ax, color='blue', linewidth=1) ax.set_title('East Side Route Map {}\nFitness = {}'.format(n+1,self.route_list[n][0])) ax.set_ylim((self.miny, self.maxy)) ax.set_xlim((self.minx, self.maxx)) ax.set_xticks([]) ax.set_yticks([]) plt.subplots_adjust(left=None, bottom=None, right=None, top=None, wspace=.1, hspace=.1) plt.show()
输出及报错信息
40 LINESTRING (-97.68178 30.28154, -97.68239 30.2... 103 LINESTRING (-97.68271 30.28083, -97.68212 30.2... 145 LINESTRING (-97.69771 30.25189, -97.69632 30.2... 146 LINESTRING (-97.72316 30.28912, -97.72312 30.2... 157 LINESTRING (-97.73329 30.27056, -97.73312 30.2... ... 19248 LINESTRING (-97.71925 30.27073, -97.71828 30.2... 19374 LINESTRING (-97.73050 30.27595, -97.73046 30.2... 19393 LINESTRING (-97.73050 30.27595, -97.73046 30.2... 19416 LINESTRING (-97.71351 30.25043, -97.71343 30.2... 19553 LINESTRING (-97.73230 30.24584, -97.73143 30.2... Name: geometry, Length: 662, dtype: geometry TypeError Traceback (most recent call last) Cell In[16], line 1 ----> 1 model.plot() File ~\Downloads\Bus-Route-Optimization-master\RouteFinder1.0\RouteFinder.py:296, in RouteFinder.plot(self) 288 print(self.roads.geometry) 290 #if (self.roads.geometry.type == 'MultiLineString').any(): 291 # for line in self.roads.geometry: 292 # ax.plot(*line.xy, color='black', linewidth=.5) 293 #else: 294 # ax.plot(*self.roads.geometry.xy, color='black', linewidth=.5) --> 296 self.roads.plot(ax=ax,color='black', linewidth=.5 ) 297 self.region.plot(ax=ax, color='orange', alpha=.2) 298 self.bus_stops_geo.plot(ax=ax, color='red',markersize=8) File C:\ProgramData\Anaconda3\lib\site-packages\geopandas\geodataframe.py:606, in GeoDataFrame.plot(self, *args, **kwargs) 596 def plot(self, *args, **kwargs): 597 """Generate a plot of the geometries in the GeoDataFrame. 598 599 If the column parameter is given, colors plot according to values (...) 604 from there. 605 """ --> 606 return plot_dataframe(self, *args, **kwargs) File C:\ProgramData\Anaconda3\lib\site-packages\geopandas\plotting.py:495, in plot_dataframe(df, column, cmap, color, ax, cax, categorical, legend, scheme, k, vmin, vmax, markersize, figsize, legend_kwds, classification_kwds, **style_kwds) 492 markersize = df[markersize].values 494 if column is None: --> 495 return plot_series( 496 df.geometry, 497 cmap=cmap, 498 color=color, 499 ax=ax, 500 figsize=figsize, 501 markersize=markersize, 502 **style_kwds 503 ) 505 # To accept pd.Series and np.arrays as column 506 if isinstance(column, (np.ndarray, pd.Series)): File C:\ProgramData\Anaconda3\lib\site-packages\geopandas\plotting.py:348, in plot_series(s, cmap, color, ax, figsize, **style_kwds) 346 if not lines.empty: 347 values_ = values[line_idx] if cmap else None --> 348 plot_linestring_collection( 349 ax, lines, values_, color=color, cmap=cmap, **style_kwds 350 ) 352 # plot all Points in the same collection 353 points = s.geometry[point_idx] File C:\ProgramData\Anaconda3\lib\site-packages\geopandas\plotting.py:156, in plot_linestring_collection(ax, geoms, values, color, cmap, vmin, vmax, **kwargs) 129 """ 130 Plots a collection of LineString and MultiLineString geometries to ax 131 (...) 152 153 """ 154 from matplotlib.collections import LineCollection --> 156 geoms, multiindex = _flatten_multi_geoms(geoms, range(len(geoms))) 157 if values is not None: 158 values = np.take(values, multiindex) File C:\ProgramData\Anaconda3\lib\site-packages\geopandas\plotting.py:38, in _flatten_multi_geoms(geoms, colors) 36 for geom, color in zip(geoms, colors): 37 if geom.type.startswith("Multi"): ---> 38 for poly in geom: 39 components.append(poly) 40 # repeat same color for all components TypeError: 'MultiLineString' object is not iterable
解决思路与方案
根源定位:打印的
self.roads.geometry仅显示部分条目,实际数据中存在隐藏的MultiLineString对象。旧版geopandas(0.6.0)处理MultiLineString时的迭代逻辑存在bug,直接遍历MultiLineString对象会触发不可迭代错误。方案一:手动绘制几何对象(推荐)
替换原有的self.roads.plot()调用,手动处理所有几何类型,绕过geopandas的bug:# 替换原self.roads.plot(...)语句 for geom in self.roads.geometry: if geom.type == 'LineString': ax.plot(*geom.xy, color='black', linewidth=.5) elif geom.type == 'MultiLineString': # 正确遍历MultiLineString的子线段 for line in geom.geoms: ax.plot(*line.xy, color='black', linewidth=.5)方案二:升级geopandas版本
旧版geopandas(0.6.0)存在诸多已知bug,升级到1.0+版本可彻底解决这类几何对象迭代问题:pip install --upgrade geopandas注意:升级需同步检查依赖库(如shapely、pandas)的兼容性,确保项目其他代码正常运行。
方案三:预处理数据拆分MultiLineString
在加载self.roads数据时,提前将所有MultiLineString拆分为LineString:import geopandas as gpd # 拆分MultiLineString为单个LineString def explode_multilinestring(gdf): exploded = [] for idx, row in gdf.iterrows(): if row.geometry.type == 'MultiLineString': for line in row.geometry.geoms: new_row = row.drop('geometry').to_dict() new_row['geometry'] = line exploded.append(new_row) else: exploded.append(row.to_dict()) return gpd.GeoDataFrame(exploded, crs=gdf.crs) # 在初始化self.roads后调用 self.roads = explode_multilinestring(self.roads)预处理后
self.roads仅包含LineString,可直接使用原plot()方法。
内容的提问来源于stack exchange,提问作者Jiyuu

