
Overpass API 爬取上海中环geometry-CSDN博客 中的代码是有问题的:不在中环上的点,因为名字里也有中环,也被囊括进来了
0 导入库
python
from shapely.geometry import LineString, MultiLineString, Point
from shapely.ops import unary_union
from pyproj import Transformer
1 创建MultiLineString
python
mls = MultiLineString([LineString([(lon, lat) for lon, lat in seg]) for seg in lines])
mls
先把每条线段做成 LineString,再打包成 MultiLineString

2 零散线段融合
python
ring_lines = unary_union(mls)
ring_lines
把零散线段融合(会得到一个或多个连通折线)
- unary_union 会把端点相接的线段合成更长的折线,去掉重复重叠部分;结果可能是:
- 单条 LineString(如果全部连成一条)
- 多条 MultiLineString(如果有多段互不相连)

3 经纬度--米制坐标 互转
python
to_m = Transformer.from_crs(4326, 32651, always_xy=True)
to_deg = Transformer.from_crs(32651, 4326, always_xy=True)
- EPSG:4326 = WGS84(经纬度,单位是度)
- EPSG:32651 = UTM 51N(单位是米,上海附近用这个区带很合适)
- always_xy=True 强制按照 (x=lon, y=lat) 的次序转换,避免某些坐标系默认把纬度放前面导致的轴顺序混乱。
python
def geom_to_m(geom):
if geom.geom_type in ("LineString", "LinearRing"):
xs, ys = zip(*[to_m.transform(x, y) for x, y in geom.coords])
return LineString(list(zip(xs, ys)))
elif geom.geom_type == "MultiLineString":
return MultiLineString([geom_to_m(g) for g in geom.geoms])
else:
raise ValueError("unsupported geometry: " + geom.geom_type)
ring_m = geom_to_m(ring_lines)
在"度"单位下做缓冲会得到"度"为单位的距离,没有实际长度意义;所以先投影到"米"。
这里只处理了线类型(LineString / MultiLineString)
4 建立缓冲区
python
corridor_m = ring_m.buffer(400)
corridor_m

4.1 仅保留最大连通分量走廊
python
corridor_m = max(corridor_m.geoms, key=lambda g: g.area) if corridor_m.geom_type == "MultiPolygon" else corridor_m
corridor_m

- 这是以主线为中心线,双侧各 400m 的"带状区域",整体"带宽"约 800m。
- 如果主线是闭合环,缓冲后通常得到"甜甜圈"样式的多边形(Polygon,带内孔);如果主线不连通,会得到 MultiPolygon。
- 第二行只保留面积最大的连通块,避免主线之外某些零碎线段也生成小缓冲面,误把附近点识别成"好点"。
5 过滤点
python
good_points, bad_points = [], []
for (lat, lon) in points:
x, y = to_m.transform(lon, lat)
if corridor_m.contains(Point(x, y)):
good_points.append((lat, lon))
else:
bad_points.append((lat, lon))
- points 存的是 (lat, lon) 顺序,这里转换时写成 to_m.transform(lon, lat)
- 用 contains 判断点在不在多边形内部
- contains 不包括边界,落在边界上的点会被判为 False
- 如果想边界也算内,可以改成 corridor_m.covers(Point(x, y))
6 输出结果
python
print(f"原始点数: {len(points)}")
print(f"保留(走廊内): {len(good_points)}")
print(f"异常(走廊外): {len(bad_points)} ← 这些就是松江/杨浦等误判点")
'''
原始点数: 1369
保留(走廊内): 1327
异常(走廊外): 42 ← 这些就是松江/杨浦等误判点
'''
7 保存结果
python
import csv, json
from shapely.geometry import LineString, mapping
name = "sh_middle_ring"
with open(f"{name}.csv", "w", newline="", encoding="utf-8") as f:
w = csv.writer(f)
w.writerow(["lat", "lon"])
w.writerows(good_points)

8 求中环以内区域的geometry
python
from shapely.geometry import MultiPoint, mapping
import numpy as np, json
pts = [(lon, lat) for lat, lon in good_points]
'''
good_points 里存的是 (lat, lon)(纬度在前,经度在后)。
但是 Shapely 一律用 (x, y) = (lon, lat),也就是 (经度, 纬度)。
'''
poly = MultiPoint(pts).convex_hull
'''
MultiPoint(pts):把点列表变成一个 Shapely 的多点对象。
.convex_hull:求点集的凸包,即所有点都在里面或边界上的最小凸多边形。
'''
poly

8.1 保存poly
python
with open("sh_middle_ring_poly.wkt", "w", encoding="utf-8") as f:
f.write(poly.wkt)
9 polygon用folium可视化
python
import folium
# 建立底图(以上海市中心为例)
m = folium.Map(location=[31.23, 121.47], zoom_start=11)
# 提取 poly 的外边界坐标 (lon, lat) → (lat, lon)
coords = [(lat, lon) for lon, lat in poly.exterior.coords]
#folium 需要的是经纬度坐标 (lat, lon) 顺序,所以要从 poly.exterior.coords 里取 (lon, lat) 再转换回来
# 添加多边形
folium.Polygon(
locations=coords,
color="blue", # 边框颜色
weight=2, # 边框粗细
fill=True, # 是否填充
fill_color="blue", # 填充颜色
fill_opacity=0.3 # 填充透明度
).add_to(m)
# 保存为 html 文件
m
