如何保存ipyleaflet自定义Marker列表并后续加载?
问题
我需要保存ipyleaflet的自定义标记列表,后续加载到另一幅地图中。我自定义了MarkerCustom类,为标记添加了duration属性,该类可正常工作。由于后续要将标记拆分到不同集群,目前不想把它们保存为单个集群。
自定义类代码:
from traitlets import List import ipyleaflet from ipywidgets import HTML class MarkerCustom(ipyleaflet.Marker): duration = List().tag(sync=True, o=True)
标记存储在长列表mark中,示例如下:
mark[0] = MarkerCustom(location=[df.loc[index]['latitude'], df.loc[0]['longitude']], icon=icon, draggable=False, title=tooltip, duration=duration_list, popup=HTML('custom text0')) mark[1] = MarkerCustom(location=[df.loc[1]['latitude'], df.loc[1]['longitude']], icon=icon, draggable=False, title=tooltip, duration=duration_list, popup=HTML('custom text1'))
尝试过shelve、pickle、json方案均失败:
- 使用pickle时出现
TypeError: cannot pickle '_hashlib.HMAC' object错误 - 尝试用geojson库并实现
__geo_interface__属性时,出现TypeError: Object of type set is not JSON serializable错误
希望能直接保存整个mark列表,而非逐个处理单个标记。
解决方案
由于ipyleaflet的Marker是Widget子类,包含大量不可序列化的交互属性(如内部trait状态、HMAC对象),无法直接序列化保存。可行方案是提取标记的核心可序列化数据,保存后再重建标记对象。
步骤1:提取并保存标记核心数据
import json from ipyleaflet import Marker from ipywidgets import HTML # 遍历标记列表,提取可序列化的核心信息 mark_data = [] for marker in mark: # 提取HTML弹窗内容 popup_content = marker.popup.value if isinstance(marker.popup, HTML) else None # 提取图标属性(支持Icon/FontAwesomeIcon等) icon_data = {} if marker.icon: # 记录图标类名,用于后续重建 icon_data['class'] = marker.icon.__class__.__name__ # 提取所有可序列化的图标属性 for attr in dir(marker.icon): if not attr.startswith('_') and isinstance(getattr(marker.icon, attr), (str, int, float, list, dict, bool)): icon_data[attr] = getattr(marker.icon, attr) # 整理当前标记的所有核心数据 marker_info = { 'location': marker.location, 'duration': marker.duration, 'title': marker.title, 'draggable': marker.draggable, 'popup_content': popup_content, 'icon_data': icon_data } mark_data.append(marker_info) # 保存到JSON文件(通用且易读) with open('markers_data.json', 'w', encoding='utf-8') as f: json.dump(mark_data, f, indent=2)
步骤2:加载数据并重建标记列表
import json from ipyleaflet import Marker, Icon, FontAwesomeIcon from ipywidgets import HTML from traitlets import List # 重新定义自定义标记类 class MarkerCustom(Marker): duration = List().tag(sync=True, o=True) # 加载保存的标记数据 with open('markers_data.json', 'r', encoding='utf-8') as f: mark_data = json.load(f) # 重建标记列表 mark = [] for data in mark_data: # 重建图标对象 icon = None if data['icon_data']: # 根据保存的类名获取图标类 icon_class = globals()[data['icon_data']['class']] # 移除类名字段,剩余参数用于实例化图标 icon_kwargs = {k: v for k, v in data['icon_data'].items() if k != 'class'} icon = icon_class(**icon_kwargs) # 重建弹窗 popup = HTML(data['popup_content']) if data['popup_content'] else None # 创建自定义标记实例 marker = MarkerCustom( location=data['location'], duration=data['duration'], title=data['title'], draggable=data['draggable'], icon=icon, popup=popup ) mark.append(marker)
说明
- 该方案通过提取标记的
location、duration、title等核心属性,以及弹窗、图标的可序列化数据,避开了Widget类中不可序列化的部分。 - JSON格式保存的数据兼容性强,可跨环境读取;如果需要更高的存储效率,也可以将
json.dump/json.load替换为pickle.dump/pickle.load。
内容的提问来源于stack exchange,提问作者JoeMount
相关产品推荐
相关产品推荐

