如何将overpy获取的OSM数据转为含多边形的GeoDataFrame
问题原因
OSM中行政边界是以relation类型存储的,其几何成员是构成边界的零散未闭合LineString。直接调用Overpass的geom()方法只会返回所有线成员的集合,不会自动执行环闭合、面组装逻辑,因此直接转换得到的GeoDataFrame中只会存在包含LineString的GeometryCollection,没有可用的Polygon/MultiPolygon类型几何。
解决方案
不需要调整Overpass查询逻辑,在拿到返回结果后,通过shapely完成线段拼接、面组装即可得到符合要求的GeoDataFrame,完整可运行代码如下:
import requests import geopandas as gpd from shapely.geometry import shape, MultiPolygon from shapely.ops import linemerge, polygonize url = "https://maps.mail.ru/osm/tools/overpass/api/interpreter" query = """[out:json]; area['boundary' = 'administrative']['name' = 'Москва'] -> .MSK; ( relation(area.MSK)['admin_level' = 8]['boundary' = 'administrative']['name'='Бескудниковский район'; relation(area.MSK)['admin_level' = 8]['boundary' = 'administrative']['name'='район Восточное Дегунино'; ); convert item ::=::,::geom=geom(),_osm_type=type(); out geom;""" response = requests.get(url, params={'data': query}) data = response.json() processed_records = [] for elem in data["elements"]: # 提取区域name属性 record = {"name": elem["tags"].get("name", "unnamed_area")} # 读取返回的原始几何集合,拼接零散边界线段 raw_geom = shape(elem["geometry"]) merged_boundary = linemerge(raw_geom) # 从闭合边界提取所有面 area_polygons = list(polygonize(merged_boundary)) # 组装为对应几何类型 record["geometry"] = area_polygons[0] if len(area_polygons) == 1 else MultiPolygon(area_polygons) processed_records.append(record) # 构建最终GeoDataFrame,指定OSM使用的WGS84坐标系 geo_df = gpd.GeoDataFrame(processed_records, crs="EPSG:4326")
运行后得到的geo_df从0开始索引,每行对应一个行政区,name列存区域名称,geometry列为带坐标的Polygon/MultiPolygon类型,可直接用于可视化。
注意事项
- 上述逻辑会自动识别边界中的闭合环,自动处理带飞地、内部孔洞的行政区几何
- 如果遇到个别边界线段端点存在微小间隙无法拼接,可以给
linemerge传入容差参数,例如linemerge(raw_geom, tolerance=1e-7),会自动拼接距离小于容差的线段端点 - 可通过执行
print(geo_df.geom_type)验证几何类型,输出结果不会再出现LineString或GeometryCollection类型
内容的提问来源于stack exchange,提问作者and_dlg
相关产品推荐
相关产品推荐

