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

使用Streamlit+Folium时地图移动后模糊的问题排查

问题:Streamlit + Folium地图移动后模糊,缓存尝试无效

我用Streamlit和Folium做交互式地图,每次移动地图后都会变得模糊。推测是重复处理导致的性能问题,试过用缓存只执行一次处理,但没改善,想确认是不是操作错了。

相关代码

import geopandas as gpd
import folium
import streamlit as st
from streamlit_folium import st_folium
import pandas as pd
import functools
from branca.colormap import LinearColormap

APP_TITLE = 'Agences du réseau'

done = False

def read_files():
    #df = pd.read_excel("data/data.xlsx")
    # For testing purpose use this generated dataframe
    df = pd.DataFrame({"Agence":["Ardèche", "Isère", "Savoie"], "Occupation":[3, 2, 1], "Commentaire": ["RAS", "RAS", "RAS"]})
    geo_data = gpd.read_file("data/departements.geojson")
    # Create a Folium map centered at the bounds
    m = folium.Map(location=[46.5, 2.5], zoom_start=6, tiles='CartoDB positron')

    # Create linear color scale from red to green
    color_scale = LinearColormap(['#FF0000', '#00FF00'],
                            vmin=df['Occupation'].min(), 
                            vmax=df['Occupation'].max())

    def style_function(feature):
        value = feature['properties']['Occupation']
        return {
            'fillColor': color_scale(value),
            'color': 'white',
            'weight': 1,
            'fillOpacity': 0.8
        }

    choropleth = folium.Choropleth(
        geo_data=geo_data,
        data=df,
        columns=["Agence", 'Occupation'],
        key_on='feature.properties.nom',
        line_opacity=0.8,
        highlight=False,
        fill_opacity=0.5,
        style_function=style_function  

    )


    choropleth.geojson.add_to(m)
    # Extract the boundaries of the GeoJSON data
    boundary_data = geo_data.boundary

    # Calculate the bounds of the boundary geometries
    bounds = boundary_data.total_bounds.tolist()


    # Add the boundaries to the map
    folium.GeoJson(boundary_data).add_to(m)



    return df, geo_data, m, choropleth

def display_map(geofile, df, geo_data, m, choropleth):

    
    # TEST
    df_indexed = df.set_index('Agence')
    global done
    if done == False:

        for feature in choropleth.geojson.data['features']:
            try:
                state_name = feature['properties']['nom']
                print(state_name)
                feature['properties']['Occupation'] = 'Population: ' + '{:,}'.format(df_indexed.loc[state_name, 'Occupation'][0]) if state_name in list(df_indexed.index) else ''
                feature['properties']['Commentaire'] = 'Reports/100K Population: ' + str(round(df_indexed.loc[state_name, 'Commentaire'][0])) if state_name in list(df_indexed.index) else ''
            except:
                pass
        done = True
    choropleth.geojson.add_child(
        folium.features.GeoJsonTooltip(['nom', 'Occupation', 'Commentaire'], labels=False)
    )
    
    # Display the map in Streamlit
    st_map = st_folium(m, width=1200, height=800)
    print(st_map)
    state_name = ''
    if st_map['last_active_drawing']:
        state_name = st_map['last_active_drawing']['properties']['nom']
    return state_name

def main():
    df, geo_data, m, choropleth = read_files()
    st.set_page_config(APP_TITLE)
    st.title(APP_TITLE)
    display_map("data/departements.geojson", df, geo_data, m, choropleth)

if __name__ == "__main__":
    main()

数据来源

使用的GeoJSON为法国省份数据,文件名为departements.geojson。

地图状态对比

  • 未模糊状态:
    未模糊状态
  • 模糊状态:
    模糊状态

问题分析与修复方案

核心问题

  1. 全局变量done无效:Streamlit每次交互都会重新运行整个脚本,全局变量done会被重置为False,导致每次地图交互都重复修改GeoJSON属性、添加Tooltip,引发渲染异常。
  2. 图层重复叠加:read_files每次运行都会添加边界GeoJSON,配合Streamlit的重运行机制,多层叠加导致地图模糊。
  3. Choropleth与Tooltip重复绑定:每次调用display_map都会给choropleth.geojson添加Tooltip,叠加元素进一步加重渲染混乱。

修复步骤

  1. 用Streamlit缓存替代全局变量:用@st.cache_data缓存地图初始化和数据读取操作,确保只执行一次。
  2. 提前整合数据与图层:在地图初始化阶段就完成GeoJSON属性修改、Tooltip绑定,避免后续重复操作。
  3. 合并图层逻辑:把边界样式整合到Choropleth的样式函数中,减少独立图层数量。

修复后的完整代码

import geopandas as gpd
import folium
import streamlit as st
from streamlit_folium import st_folium
import pandas as pd
from branca.colormap import LinearColormap

APP_TITLE = 'Agences du réseau'

@st.cache_data
def read_files():
    # 测试用数据
    df = pd.DataFrame({
        "Agence":["Ardèche", "Isère", "Savoie"], 
        "Occupation":[3, 2, 1], 
        "Commentaire": ["RAS", "RAS", "RAS"]
    })
    geo_data = gpd.read_file("data/departements.geojson")
    
    # 初始化地图
    m = folium.Map(location=[46.5, 2.5], zoom_start=6, tiles='CartoDB positron')

    # 创建颜色比例尺
    color_scale = LinearColormap(
        ['#FF0000', '#00FF00'],
        vmin=df['Occupation'].min(), 
        vmax=df['Occupation'].max()
    )

    # 提前合并业务数据到GeoJSON属性
    df_indexed = df.set_index('Agence')
    for feature in geo_data.to_dict('records'):
        nom = feature['nom']
        if nom in df_indexed.index:
            feature['Occupation'] = f"Population: {df_indexed.loc[nom, 'Occupation']:,}"
            feature['Commentaire'] = f"Reports/100K Population: {df_indexed.loc[nom, 'Commentaire']}"
        else:
            feature['Occupation'] = ""
            feature['Commentaire'] = ""

    # 定义统一的样式函数(包含边界样式)
    def style_function(feature):
        value = float(feature['Occupation'].split(': ')[1]) if feature['Occupation'] else 0
        return {
            'fillColor': color_scale(value),
            'color': 'white',
            'weight': 1,
            'fillOpacity': 0.8,
            'lineOpacity': 0.8
        }

    # 单一GeoJSON图层整合所有逻辑
    folium.GeoJson(
        geo_data,
        style_function=style_function,
        tooltip=folium.features.GeoJsonTooltip(
            ['nom', 'Occupation', 'Commentaire'], 
            labels=False
        )
    ).add_to(m)

    return df, geo_data, m

def display_map(m):
    # 仅负责渲染地图,不修改图层结构
    st_map = st_folium(m, width=1200, height=800)
    state_name = ''
    if st_map.get('last_active_drawing'):
        state_name = st_map['last_active_drawing']['properties']['nom']
    return state_name

def main():
    df, geo_data, m = read_files()
    st.set_page_config(page_title=APP_TITLE)
    st.title(APP_TITLE)
    display_map(m)

if __name__ == "__main__":
    main()

关键优化点

  • 缓存初始化流程:@st.cache_data确保地图和数据只在首次加载或内容变更时生成,避免每次交互重复执行。
  • 单一图层设计:把Choropleth着色、Tooltip、边界样式整合到一个GeoJson图层,消除图层叠加问题。
  • 提前数据绑定:在初始化阶段完成业务数据与GeoJSON的关联,避免后续重复修改属性引发的渲染异常。

内容的提问来源于stack exchange,提问作者Achille G

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.05 06:25:18