如何使用Geopandas和Contextily叠加聚类点与底图
解决Geopandas+Contextily聚类点与底图叠加问题
你遇到的核心问题是标准化后的坐标无法匹配底图的真实地理坐标,加上绘图流程不符合Geopandas+Contextily的规范,导致叠加失败。以下是具体修正方案:
- 保留/恢复原始经纬度:你的代码里用
StandardScaler直接覆盖了原始的longitude和latitude列,这会把地理坐标变成无量纲的标准化值,完全失去地理意义,根本无法和底图匹配。必须保留原始坐标列,或者用scaler反转换回去。 - 使用GeoDataFrame管理地理数据:Geopandas的GeoDataFrame是处理地理数据的标准结构,能和Contextily无缝配合。
- 统一坐标系:Contextily提供的底图默认使用Web墨卡托投影(EPSG:3857),需要将你的点数据转换到这个坐标系。
- 正确叠加底图:用
contextily.add_basemap()方法在已绘制的点图层上叠加底图。
完整修正代码
# 先保留原始经纬度,不要覆盖(假设你的原始数据列是longitude, latitude) from sklearn.preprocessing import StandardScaler from sklearn.cluster import KMeans import geopandas as gpd import matplotlib.pyplot as plt import contextily as ctx # 1. 标准化数据用于聚类,但保留原始坐标 scaler = StandardScaler() # 注意:标准化时先传latitude再longitude,反转换时也要对应 scaled_coords = scaler.fit_transform(mrt[['latitude', 'longitude']]) mrt['scaled_latitude'], mrt['scaled_longitude'] = scaled_coords.T # 2. 执行KMeans聚类 num_clusters = 5 kmeans = KMeans(n_clusters=num_clusters) mrt['cluster_labels'] = kmeans.fit_predict(mrt[['scaled_longitude', 'scaled_latitude']]) # 3. 转换为GeoDataFrame,使用原始经纬度构建几何对象 gdf = gpd.GeoDataFrame( mrt, geometry=gpd.points_from_xy(mrt['longitude'], mrt['latitude']), crs="EPSG:4326" # WGS84坐标系,对应常规GPS经纬度 ) # 4. 转换到Web墨卡托投影(和Contextily底图的坐标系匹配) gdf = gdf.to_crs(epsg=3857) # 5. 绘制聚类点并叠加底图 fig, ax = plt.subplots(figsize=(10, 10)) # 绘制聚类点,设置颜色、大小和透明度 gdf.plot(ax=ax, column='cluster_labels', cmap='viridis', markersize=50, alpha=0.7, legend=True) # 叠加OpenStreetMap底图 ctx.add_basemap(ax, source=ctx.providers.OpenStreetMap.Mapnik) # 优化显示效果 ax.set_title('新加坡MRT站点聚类(带底图)', fontsize=14) ax.set_axis_off() # 关闭坐标轴,让底图更清晰 plt.show()
补充说明
如果你的原始数据已经被覆盖,没有保留原始经纬度,可以用scaler反转换恢复:
# 反转换得到原始坐标 original_coords = scaler.inverse_transform(mrt[['scaled_latitude', 'scaled_longitude']]) mrt['latitude'], mrt['longitude'] = original_coords.T
另外,ctx.Place()适合快速查看某个区域,但不适合和自定义数据叠加,直接用add_basemap()更灵活可控。
内容的提问来源于stack exchange,提问作者Nat C
相关产品推荐
相关产品推荐

