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

如何用Folium的FastMarkerCluster实现自定义标记与动态图层控制

解决Folium大量标记渲染及聚类与子组关联问题

1. 自定义FastMarkerCluster回调函数(支持颜色、Tooltip、IFrame弹窗)

FastMarkerCluster通过JS回调动态生成标记,要实现自定义颜色、Tooltip和IFrame弹窗,需将每个标记的额外属性(颜色、Tooltip文本、弹窗HTML)存入点数据,再在回调中读取并生成对应Marker。

实现代码:

import folium
from folium.plugins import FastMarkerCluster, FeatureGroupSubGroup

# 替换为你的实际点数据格式:(纬度, 经度, 标记颜色, Tooltip文本, 弹窗HTML)
points = []
for i in range(12000):
    lat = 39.9 + i/10000
    lng = 116.4 + i/10000
    color = 'red' if i % 2 == 0 else 'blue'
    tooltip = f"标记{i}"
    # 替换为你的IFrame弹窗代码(示例为base64编码PNG的弹窗)
    popup_html = f"""
    <iframe srcdoc='<img src="data:image/png;base64,{your_base64_png}" width="300" height="200">' 
            width="320" height="220" frameborder="0"></iframe>
    """
    points.append((lat, lng, color, tooltip, popup_html))

# 初始化地图
m = folium.Map(location=[39.9, 116.4], zoom_start=12)

# 定义JS回调:接收点数据,生成带自定义样式和交互的Marker
marker_callback = folium.JsCode("""
function(row) {
    const [lat, lng, color, tooltip, popupHtml] = row;
    
    // 创建自定义圆形图标(匹配颜色需求)
    const icon = L.divIcon({
        className: 'custom-marker-icon',
        html: `<div style="background: ${color}; width: 12px; height: 12px; border-radius: 50%; border: 2px solid #fff;"></div>`,
        iconSize: [12, 12],
        iconAnchor: [6, 6]
    });
    
    // 创建Marker并绑定Tooltip、Popup
    const marker = L.marker([lat, lng], {icon});
    marker.bindTooltip(tooltip, {direction: 'top'});
    marker.bindPopup(popupHtml, {maxWidth: 350});
    
    return marker;
}
""")

# 创建FastMarkerCluster,保留原MarkerCluster核心功能
fast_cluster = FastMarkerCluster(
    data=[list(p) for p in points],
    callback=marker_callback,
    showCoverageOnHover=True,  # 鼠标悬停显示聚类覆盖范围
    zoomToBoundsOnClick=True,  # 点击聚类缩放至范围
    disableClusteringAtZoom=15  # 缩放至15级时取消聚类
)
fast_cluster.add_to(m)

2. 关联FeatureGroupSubGroups实现动态聚类更新

要让聚类随子组的选中/取消实时更新,需监听子组的显示状态变化,动态筛选并更新聚类的数据源。

实现代码:

# 按子组分类点数据(替换为你的实际分类逻辑)
group_red = [p for p in points if p[2] == 'red']
group_blue = [p for p in points if p[2] == 'blue']

# 创建主分组和子分组
main_group = folium.FeatureGroup(name="所有标记").add_to(m)
sub_red = FeatureGroupSubGroup(main_group, name="红色标记").add_to(m)
sub_blue = FeatureGroupSubGroup(main_group, name="蓝色标记").add_to(m)

# 添加图层控制
folium.LayerControl(collapsed=False).add_to(m)

# 给聚类图层设置唯一ID,方便JS获取
fast_cluster._id = 'marker-cluster-layer'

# 注入JS监听逻辑:监听子组显示状态,更新聚类数据源
update_script = folium.Element(f"""
<script>
// 存储各子组的点数据
const groupData = {{
    red: {str([list(p) for p in group_red]).replace("'", '"')},
    blue: {str([list(p) for p in group_blue]).replace("'", '"')}
}};

// 获取聚类图层实例
const clusterLayer = window.map._layers['{fast_cluster._id}'];
// 复用之前的标记生成回调
const createMarker = {marker_callback.render()};

// 监听图层显示/隐藏事件
window.map.on('layeradd layerremove', (e) => {{
    // 只处理子分组的状态变化
    if (![sub_red, sub_blue].includes(e.layer)) return;
    
    // 筛选当前可见的子组
    const visibleGroups = [];
    if (window.map.hasLayer(sub_red)) visibleGroups.push('red');
    if (window.map.hasLayer(sub_blue)) visibleGroups.push('blue');
    
    // 合并可见子组的点数据
    const visiblePoints = visibleGroups.flatMap(g => groupData[g]);
    
    // 更新聚类图层
    clusterLayer.clearLayers();
    visiblePoints.forEach(point => clusterLayer.addLayer(createMarker(point)));
    clusterLayer.refreshClusters();
}});
</script>
""")

# 将脚本添加到地图
m.get_root().html.add_child(update_script)

# 保存地图
m.save('large_markers_map.html')

关键提示:

  • 点数据序列化:将Python列表转为JS数组时,需确保引号转义正确,避免语法错误。
  • 图层ID匹配:fast_cluster._id必须与JS中获取图层时的ID完全一致。
  • 性能优化:批量更新聚类图层而非逐个添加标记,避免频繁DOM操作导致卡顿。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 20:23:15