Dash Leaflet自定义标记图标:实现类Folium标记效果
需求与问题
我同时使用Folium和Dash Leaflet,希望二者的标记功能实现逻辑保持统一。目前已在Dash Leaflet中实现图片替换标记,但存在以下问题:
- 标记坐标点对准图片中心,缩放时始终保持中心对齐,需要实现普通标记底座对准坐标点的效果。
- 想要复刻Folium的标记能力:支持修改标记颜色,且能使用Font-Awesome 4.0、Bootstrap 3及Ionicons 1.5.2提供的图标。
现有Dash Leaflet实现代码:
import dash_leaflet as dl import dash_leaflet.express as dlx from dash import html, Dash from dash_extensions.javascript import assign import json locations = '''{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": {}, "geometry": { "type": "Point", "coordinates": [ -74.00570869445801, 40.713175131022695 ] } } ] }''' use_icon = assign("""function(feature, latlng){ const i = L.icon({iconUrl: `https://cdn4.iconfinder.com/data/icons/standard-free-icons/139/Checkin01-512.png`, iconSize: [40, 40]}); return L.marker(latlng, {icon: i}); }""") app = Dash() app.layout = html.Div(children=[ dl.Map(center=[39, -98], zoom=4, children=[ dl.TileLayer(), dl.GeoJSON(data=dlx.geojson_to_geobuf(json.loads(locations)), format='geobuf', options=dict(pointToLayer=use_icon))], style={'width': '100%', 'height': '100vh'})]) if __name__ == "__main__": app.run_server(debug=False)
Folium目标效果示例代码:
import folium m = folium.Map() folium.Marker((40.713175131022695, -74.00570869445801), icon=folium.Icon(color='black', icon='fire', prefix='fa')).add_to(m) m
注:需用这些标记实现数据过滤,不接受通过iframe嵌入Folium地图的方案。
解决方案
1. 修复图标锚点(底座对准坐标)
Leaflet的L.icon支持iconAnchor参数,可指定图标与地图坐标点对齐的位置。对于底座对准需求,只需将锚点设置为图标的底部中心:
function(feature, latlng){ const i = L.icon({ iconUrl: `https://cdn4.iconfinder.com/data/icons/standard-free-icons/139/Checkin01-512.png`, iconSize: [40, 40], iconAnchor: [20, 40] // x轴取中心,y轴取底部,实现底座对准坐标 }); return L.marker(latlng, {icon: i}); }
2. 实现字体图标与颜色自定义
要复刻Folium的字体图标功能,需加载对应字体库,并使用Leaflet的DivIcon通过CSS渲染图标,完整实现代码如下:
import dash_leaflet as dl import dash_leaflet.express as dlx from dash import html, Dash from dash_extensions.javascript import assign import json # 注入字体库资源 app = Dash(__name__) app.index_string = ''' <!DOCTYPE html> <html> <head> {%metas%} <title>{%title%}</title> {%favicon%} {%css%} <!-- 加载所需字体库 --> <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.0.3/css/font-awesome.min.css"> <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"> <link rel="stylesheet" href="https://code.ionicframework.com/ionicons/1.5.2/css/ionicons.min.css"> <style> /* 自定义图标基础样式 */ .custom-icon { display: flex; align-items: center; justify-content: center; font-size: 24px; width: 40px; height: 40px; border-radius: 50%; color: white; } </style> </head> <body> {%app_entry%} <footer> {%config%} {%scripts%} {%renderer%} </footer> </body> </html> ''' # 定义自定义图标生成逻辑,匹配Folium的参数格式 custom_icon_factory = assign("""function(feature, latlng){ const props = feature.properties || {}; // 从GeoJSON属性中读取配置,默认值与Folium对齐 const color = props.color || 'blue'; const icon = props.icon || 'info-sign'; const prefix = props.prefix || 'glyphicon'; // 构建字体图标类名 const iconClass = `${prefix} ${prefix}-${icon}`; // 创建DivIcon渲染字体图标 const divIcon = L.divIcon({ className: 'custom-icon', html: `<i class="${iconClass}"></i>`, iconSize: [40, 40], iconAnchor: [20, 40], // 底座对准坐标点 popupAnchor: [0, -40] // 调整弹窗位置避免遮挡 }); // 动态设置图标背景色 const marker = L.marker(latlng, {icon: divIcon}); marker._icon.style.backgroundColor = color; return marker; }""") # 带图标配置的GeoJSON数据(properties字段存储颜色、图标、前缀) locations = '''{ "type": "FeatureCollection", "features": [ { "type": "Feature", "properties": { "color": "black", "icon": "fire", "prefix": "fa" }, "geometry": { "type": "Point", "coordinates": [ -74.00570869445801, 40.713175131022695 ] } }, { "type": "Feature", "properties": { "color": "red", "icon": "home", "prefix": "glyphicon" }, "geometry": { "type": "Point", "coordinates": [ -122.4194, 37.7749 ] } }, { "type": "Feature", "properties": { "color": "green", "icon": "map-marker", "prefix": "ion" }, "geometry": { "type": "Point", "coordinates": [ -95.3698, 29.7604 ] } } ] }''' app.layout = html.Div(children=[ dl.Map(center=[39, -98], zoom=4, children=[ dl.TileLayer(), dl.GeoJSON( data=dlx.geojson_to_geobuf(json.loads(locations)), format='geobuf', options=dict(pointToLayer=custom_icon_factory) ) ], style={'width': '100%', 'height': '100vh'}) ]) if __name__ == "__main__": app.run_server(debug=False)
代码说明:
- 字体库加载:通过
index_string注入三个字体库资源,确保图标正常渲染。 - 参数对齐:通过GeoJSON的
properties字段传递color/icon/prefix参数,和Folium的Icon配置逻辑完全一致。 - 锚点设置:
iconAnchor固定为底部中心,实现和普通标记一致的坐标对齐效果。 - 数据过滤支持:基于Dash Leaflet原生组件,可直接通过回调监听标记交互事件,结合Dash状态管理实现数据过滤功能。
内容的提问来源于stack exchange,提问作者Robert Allan
相关产品推荐
相关产品推荐

