You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

在Dash App中实现Plotly Scattermapbox地图点缩放聚类功能

在Dash的Scattermapbox中实现缩放动态聚类

要实现地图缩放时点自动聚类,可通过监听地图缩放层级,结合聚类算法动态生成单个点或聚合点,具体实现如下:

核心实现逻辑

  • 监听地图的relayoutData获取当前缩放层级(zoom)
  • 根据缩放层级动态调整聚类算法的邻域半径:缩放越大,邻域半径越小,聚类拆分越精细
  • 用DBSCAN算法对坐标点聚类,根据聚类结果分别渲染原始点或聚合点

修改后的完整代码

import dash
from dash import dcc, html, Input, Output, State
import pandas as pd
from sklearn.cluster import DBSCAN
import numpy as np

# 示例数据
df = pd.DataFrame({
    'x': [1, 2, 3, 37.774, 37.775],
    'Lat': [37.774322, 37.777035, 37.773033, 37.7745, 37.7746],
    'Long': [-122.489761, -122.485555, -122.491220, -122.4898, -122.4899]
})
MAPBOX_KEY = "你的Mapbox密钥"
cd_cols = ['x']  # 自定义数据列

app = dash.Dash(__name__)

app.layout = html.Div([
    dcc.Graph(id="map"),
    dcc.Input(id="inp")
])

@app.callback(
    Output('map','figure'),
    Input('inp','value'),
    Input('map', 'relayoutData'),
    State('map', 'figure')
)
def update_map(val, relayout_data, current_fig):
    # 获取当前缩放层级,初始默认设为13
    zoom = relayout_data.get('mapbox.zoom', 13) if relayout_data else 13
    
    # 根据缩放层级调整DBSCAN邻域半径(单位:度)
    eps = 0.001 / (2 ** (zoom - 13))
    
    # 执行聚类
    coords = df[['Lat', 'Long']].values
    db = DBSCAN(eps=eps, min_samples=1).fit(coords)
    df['cluster'] = db.labels_
    
    data = []
    # 遍历每个聚类结果
    for cluster_id in df['cluster'].unique():
        cluster_points = df[df['cluster'] == cluster_id]
        if len(cluster_points) == 1:
            # 单个点,保留原始标记样式
            data.append({
                "type": "scattermapbox",
                "lat": cluster_points["Lat"],
                "lon": cluster_points["Long"],
                "name": "Location",
                "showlegend": False,
                "hoverinfo": "text",
                "hovertext": f"x: {cluster_points['x'].iloc[0]}",
                "mode": "markers",
                "clickmode": "event+select",
                "customdata": cluster_points.loc[:, cd_cols].values,
                "marker": {
                    "symbol": "circle",
                    "size": 8,
                    "opacity": 0.7,
                    "color": "black"
                }
            })
        else:
            # 聚合点,用更大的红色标记展示,hover显示点数
            cluster_lat = cluster_points['Lat'].mean()
            cluster_lon = cluster_points['Long'].mean()
            data.append({
                "type": "scattermapbox",
                "lat": [cluster_lat],
                "lon": [cluster_lon],
                "name": "Cluster",
                "showlegend": False,
                "hoverinfo": "text",
                "hovertext": f"聚合点数: {len(cluster_points)}",
                "mode": "markers",
                "marker": {
                    "symbol": "circle",
                    "size": 12 + len(cluster_points)*2,
                    "opacity": 0.8,
                    "color": "red"
                }
            })
    
    # 保留当前地图中心,避免回调触发后位置重置
    if current_fig and 'layout' in current_fig and 'mapbox' in current_fig['layout']:
        center = current_fig['layout']['mapbox']['center']
    else:
        center = {"lat": df["Lat"].mean(), "lon": df["Long"].mean()}
    
    layout = {
        "autosize": True,
        "hovermode": "closest",
        "mapbox": {
            "accesstoken": MAPBOX_KEY,
            "bearing": 0,
            "center": center,
            "pitch": 0,
            "zoom": zoom,
            "style": "satellite-streets",
        },
    }

    return {'data': data, 'layout': layout}

if __name__ == '__main__':
    app.run_server(debug=True)

关键细节说明

  • 缩放监听:通过relayoutData捕获地图缩放变化,每次缩放都会触发回调更新聚类结果
  • 聚类参数调整:eps值随缩放层级动态变化,确保不同缩放比例下聚类效果符合预期
  • 视觉区分:单个点保持原有黑色小标记,聚合点用红色大标记,hover时展示聚合数量,便于识别
  • 地图状态保留:利用State获取当前地图中心位置,避免回调触发后地图跳回初始坐标

内容的提问来源于stack exchange,提问作者kms

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.03 20:31:05