如何用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
相关产品推荐
相关产品推荐

