如何用Folium或Python库动画展示Google Earth Engine年度影像?
问题描述
我从Google Earth Engine获取了年度影像,希望将其制作为动画以便分析。我尝试使用带有滑块动画插件的Folium(即TimestampedGeoJson),代码如下:
import ee import folium import folium.plugins as fplugins """ Sources: - https://colab.research.google.com/github/giswqs/qgis-earthengine-examples/blob/master/Folium/ee-api-folium-setup.ipynb#scrollTo=3qB3W9rDfoma - https://python-visualization.github.io/folium/latest/user_guide/plugins/timestamped_geojson.html """ ee.Authenticate() ee.Initialize() dem = ee.Image('USGS/SRTMGL1_003') m = folium.Map( location=(chosen_lat, chosen_lon), zoom_start=9, height=300 ) features = [ { "type": "Feature", "geometry": dem.updateMask(dem.gt(0)).getMapId({}), "properties": {"times": ["2017-06-02T00:30:00", "2017-06-02T00:40:00"]} } ] fplugins.TimestampedGeoJson( data={ "type": "FeatureCollection", "features": features, }, period="PT1M", add_last_point=True, ).add_to(m) m
运行上述代码时,持续出现以下错误:
TypeError Traceback (most recent call last) hex_cell_0195d7ee-7e8b-7aa1-b548-572c1c39aad2.py in <module> 32 } 33 ] ---> 34 35 fplugins.TimestampedGeoJson( 36 data={ ~/.cache/pypoetry/virtualenvs/python-kernel-OtKFaj5M-py3.9/lib/python3.9/site-packages/folium/plugins/timestamped_geo_json.py in __init__(self, data, transition_time, loop, auto_play, add_last_point, period, min_speed, max_speed, loop_button, date_options, time_slider_drag_update, duration, speed_slider) 203 elif type(data) is dict: 204 self.embed = True ---> 205 self.data = json.dumps(data) 206 else: 207 self.embed = False /usr/local/lib/python3.9/json/__init__.py in dumps(obj, skipkeys, ensure_ascii, check_circular, allow_nan, cls, indent, separators, default, sort_keys, **kw) 229 cls is None and indent is None and separators is None and 230 default is None and not sort_keys and not kw): ---> 231 return _default_encoder.encode(obj) 232 if cls is None: 233 cls = JSONEncoder /usr/local/lib/python3.9/json/encoder.py in encode(self, o) 197 # exceptions aren't as detailed. The list call should be roughly 198 # equivalent to the PySequence_Fast that ''.join() would do. ---> 199 chunks = self.iterencode(o, _one_shot=True) 200 if not isinstance(chunks, (list, tuple)): 201 chunks = list(chunks) /usr/local/lib/python3.9/json/encoder.py in iterencode(self, o, _one_shot) 255 self.key_separator, self.item_separator, self.sort_keys, 256 self.skipkeys, _one_shot) ---> 257 return _iterencode(o, 0) 258 259 def _make_iterencode(markers, _default, _encoder, _indent, _floatstr, /usr/local/lib/python3.9/json/encoder.py in default(self, o) 177 178 """ ---> 179 raise TypeError(f'Object of type {o.__class__.__name__} ' 180 f'is not JSON serializable') 181 TypeError: Object of type TileFetcher is not JSON serializable
请问如何解决该错误?Folium是否可以实现影像动画?同时我也愿意尝试其他可实现该功能的Python库。
解决方案与替代方案
错误原因与Folium的局限性
你遇到的TileFetcher is not JSON serializable错误,核心原因是TimestampedGeoJson插件仅支持标准GeoJSON矢量数据,但你把GEE影像getMapId()返回的字典(包含不可序列化的TileFetcher对象)直接赋值给了geometry字段,这不符合插件的输入要求。
Folium本身可以加载GEE的栅格影像切片,但它的TimestampedGeoJson是为点、线、面等矢量要素的时间序列动画设计的,并不适合做栅格影像的动画。如果一定要用Folium实现,需要通过自定义JS逻辑动态切换影像图层,但操作繁琐且体验不佳。
更高效的替代方案
1. Google Earth Engine原生动画工具
GEE内置了影像时间序列动画生成功能,可直接导出为GIF或视频,无需额外依赖:
import ee ee.Authenticate() ee.Initialize() # 示例:构建年度NDVI影像集合 collection = ee.ImageCollection('MODIS/061/MOD13A1') \ .filterDate('2017-01-01', '2022-12-31') \ .select('NDVI') \ .map(lambda img: img.multiply(0.0001).set('system:time_start', img.get('system:time_start'))) # 定义动画参数 video_args = { 'dimensions': 720, 'region': ee.Geometry.Rectangle([-122.7286, 37.6325, -122.0241, 37.9592]), # 替换为你的研究区范围 'framesPerSecond': 2, 'min': 0, 'max': 0.8, 'palette': ['FFFFFF', 'CE7E45', 'DF923D', 'F1B555', 'FCD163', '99B718', '74A901', '66A000', '529400', '3E8601'] } # 获取动画预览URL animation_url = collection.getVideoThumbURL(video_args) print('动画预览URL:', animation_url) # 导出动画到Google Drive task = ee.batch.Export.video.toDrive( collection=collection, description='annual_ndvi_animation', dimensions=720, region=ee.Geometry.Rectangle([-122.7286, 37.6325, -122.0241, 37.9592]), framesPerSecond=2, crs='EPSG:3857' ) task.start()
2. 使用geemap库(推荐)
geemap是专为GEE打造的Python可视化库,内置时间滑块动画工具,完全适配GEE影像:
import geemap # 创建交互式地图 Map = geemap.Map() # 加载年度影像集合 collection = ee.ImageCollection('MODIS/061/MOD13A1') \ .filterDate('2017-01-01', '2022-12-31') \ .select('NDVI') \ .map(lambda img: img.multiply(0.0001)) # 添加时间滑块动画,自动切换年份影像 Map.add_time_slider(collection, labels=[str(year) for year in range(2017, 2023)], time_interval=1) # 显示地图 Map
该方案支持实时交互调整影像渲染参数,操作直观便捷。
3. 使用ipyleaflet(Jupyter环境)
ipyleaflet支持交互式地图操作,结合GEE影像切片服务可实现带时间滑块的动画:
import ee import ipyleaflet from ipyleaflet import Map, TileLayer, WidgetControl from ipywidgets import IntSlider, HTML ee.Authenticate() ee.Initialize() # 初始化地图 m = Map(center=(37.7749, -122.4194), zoom=9) # 定义年份范围与影像集合 years = range(2017, 2023) collection = ee.ImageCollection('MODIS/061/MOD13A1') \ .filterDate('2017-01-01', '2022-12-31') \ .select('NDVI') \ .map(lambda img: img.multiply(0.0001)) # 创建时间滑块与状态显示组件 slider = IntSlider(min=2017, max=2022, value=2017, description='年份:') html = HTML(value='<b>当前年份: 2017</b>') # 定义滑块更新逻辑 def update_layer(change): year = change['new'] html.value = f'<b>当前年份: {year}</b>' # 获取对应年份的影像 img = collection.filterDate(f'{year}-01-01', f'{year}-12-31').first() # 获取影像切片URL tile_url = img.getMapId({'min':0, 'max':0.8, 'palette': ['FFFFFF', '99B718']})['tile_fetcher'].url_format # 移除旧图层 for layer in m.layers: if hasattr(layer, 'name') and layer.name == 'yearly_ndvi': m.remove_layer(layer) # 添加新图层 m.add_layer(TileLayer(url=tile_url, name='yearly_ndvi')) slider.observe(update_layer, names='value') # 添加控件到地图 m.add_control(WidgetControl(widget=slider, position='topright')) m.add_control(WidgetControl(widget=html, position='bottomright')) # 显示地图 m
内容的提问来源于stack exchange,提问作者Fxs7576
相关产品推荐
相关产品推荐

