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

如何用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.13 14:39:57