调用pyrosm.get_network获取驾驶路网时触发TypeError求助
问题描述
原本可通过pyrosm读取OpenStreetMap的PBF文件生成驾驶路网,但运行以下代码时突然失败:
osm = pyrosm.OSM(f) area = osm.get_network(network_type="driving")
触发TypeError,提示输入的MULTILINESTRING不是有效几何对象,完整报错栈如下:
TypeError Traceback (most recent call last) Cell In[17], line 38 35 os.makedirs(mypath_shp) 37 osm = pyrosm.OSM(f) ---> 38 area = osm.get_network(network_type="driving") 39 print("Created network - OK") 41 # subset = subsetting(area) 42 # print("Created subset - OK") 43 (...) 53 # subset.to_file(path_shape, driver="ESRI Shapefile") 54 # print(" Shapefile - OK") File ~\AppData\Local\miniconda3\envs\for_osm\lib\site-packages\pyrosm\pyrosm.py:205, in OSM.get_network(self, network_type, extra_attributes, nodes) 202 self._read_pbf() 204 # Filter network data with given filter ---> 205 edges, node_gdf = get_network_data( 206 self._node_coordinates, 207 self._way_records, 208 tags_as_columns, 209 network_filter, 210 self.bounding_box, 211 slice_to_segments=nodes, 212 ) 214 if edges is not None: 215 # Add metadata 216 edges._metadata.append(network_type) File ~\AppData\Local\miniconda3\envs\for_osm\lib\site-packages\pyrosm\networks.py:37, in get_network_data(node_coordinates, way_records, tags_as_columns, network_filter, bounding_box, slice_to_segments) 34 return None, None 36 # Prepare GeoDataFrame ---> 37 edges, nodes = prepare_geodataframe( 38 nodes, 39 node_coordinates, 40 ways, 41 relations, 42 relation_ways, 43 tags_as_columns, 44 bounding_box, 45 parse_network=True, 46 calculate_seg_lengths=slice_to_segments, 47 ) 49 return edges, nodes File ~\AppData\Local\miniconda3\envs\for_osm\lib\site-packages\pyrosm\frames.pyx:134, in pyrosm.frames.prepare_geodataframe() File ~\AppData\Local\miniconda3\envs\for_osm\lib\site-packages\pyrosm\frames.pyx:141, in pyrosm.frames.prepare_geodataframe() File ~\AppData\Local\miniconda3\envs\for_osm\lib\site-packages\pyrosm\frames.pyx:71, in pyrosm.frames.prepare_way_gdf() File ~\AppData\Local\miniconda3\envs\for_osm\lib\site-packages\geopandas\geodataframe.py:188, in GeoDataFrame.__init__(self, data, geometry, crs, *args, **kwargs) 180 if ( 181 hasattr(geometry, "crs") 182 and geometry.crs 183 and crs 184 and not geometry.crs == crs 185 ): 186 raise ValueError(crs_mismatch_error) ---> 188 self.set_geometry(geometry, inplace=True, crs=crs) 190 if geometry is None and crs: 191 raise ValueError( 192 "Assigning CRS to a GeoDataFrame without a geometry column is not " 193 "supported. Supply geometry using the 'geometry=' keyword argument, " 194 "or by providing a DataFrame with column name 'geometry'", 195 ) File ~\AppData\Local\miniconda3\envs\for_osm\lib\site-packages\geopandas\geodataframe.py:346, in GeoDataFrame.set_geometry(self, col, drop, inplace, crs) 343 level.crs = crs 345 # Check that we are using a listlike of geometries ---> 346 level = _ensure_geometry(level, crs=crs) 347 frame[geo_column_name] = level 348 frame._geometry_column_name = geo_column_name File ~\AppData\Local\miniconda3\envs\for_osm\lib\site-packages\geopandas\geodataframe.py:59, in _ensure_geometry(data, crs) 57 else: 58 if isinstance(data, Series): ---> 59 out = from_shapely(np.asarray(data), crs=crs) 60 return GeoSeries(out, index=data.index, name=data.name) 61 else: File ~\AppData\Local\miniconda3\envs\for_osm\lib\site-packages\geopandas\array.py:154, in from_shapely(data, crs) 138 def from_shapely(data, crs=None): 139 """ 140 Convert a list or array of shapely objects to a GeometryArray. 141 (...) 152 153 """ ---> 154 return GeometryArray(vectorized.from_shapely(data), crs=crs) File ~\AppData\Local\miniconda3\envs\for_osm\lib\site-packages\geopandas\_vectorized.py:145, in from_shapely(data) 143 out.append(None) 144 else: ---> 145 raise TypeError("Input must be valid geometry objects: {0}".format(geom)) 147 if compat.USE_PYGEOS: 148 return np.array(out, dtype=object) TypeError: Input must be valid geometry objects: MULTILINESTRING ((7.646 47.633, 7.646 47.633), (7.646 47.633, 7.646 47.633), (7.646 47.633, 7.646 47.633), (7.646 47.633, 7.646 47.633), (7.646 47.633, 7.647 47.633), (7.647 47.633, 7.647 47.633))
解决思路
- 清理无效几何对象:报错中的MULTILINESTRING包含零长度线段(重复坐标点组成的线段),属于无效几何。可以先读取原始数据再手动过滤修复:
import pyrosm from shapely.validation import make_valid import geopandas as gpd # 读取所有way数据,不做网络过滤 osm = pyrosm.OSM(f) ways = osm.get_ways() # 修复无效几何并过滤空值 ways['geometry'] = ways['geometry'].apply(lambda geom: make_valid(geom) if geom.is_valid else None) ways = ways.dropna(subset=['geometry']) # 手动筛选驾驶路网对应的OSM标签 driving_tags = ['motorway', 'trunk', 'primary', 'secondary', 'tertiary', 'unclassified', 'residential'] area = ways[ways['highway'].isin(driving_tags)] - 调整依赖库版本:该错误可能是pyrosm或geopandas版本更新后几何验证逻辑变严格导致的,回退到之前能正常运行的版本:
# 回退pyrosm到指定稳定版本 pip install pyrosm==0.7.0 # 或回退geopandas版本 pip install geopandas==0.12.0 - 预处理PBF文件:用osmium-tool提前过滤驾驶路网并清理无效要素:
# 提取驾驶路网并输出为新的PBF文件 osmium tags-filter input.osm.pbf w/highway=motorway,trunk,primary,secondary,tertiary,unclassified,residential -o filtered.osm.pbf - 临时修改pyrosm源码绕过验证:找到pyrosm安装目录下的
frames.pyx文件,在prepare_way_gdf函数中添加无效几何过滤逻辑,比如在生成几何列表后加入:# 过滤无效几何 valid_geometries = [] for geom in geometries: if geom.is_valid: valid_geometries.append(geom) else: valid_geometries.append(None) geometries = valid_geometries
内容的提问来源于stack exchange,提问作者i.i.k.
相关产品推荐
相关产品推荐

