Streamlit做地图应用?组件如何嵌入?

GIS基础理论
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

如果你正在做一个轻量级 GIS 数据展示或内部分析工具,常会遇到一个问题:Streamlit做地图应用?组件如何嵌入? Streamlit 上手很快,但地图应用往往不只是显示一个点,还涉及底图、矢量图层、交互选择、弹窗、绘制、空间查询结果展示等组件嵌入。本文以 GIS 读者最常见的场景为主线,讲清楚 Streamlit 做地图应用时该选哪种地图组件、如何嵌入 Folium / Leaflet / Pydeck 地图,以及常见坑怎么排查。

引言:Streamlit做地图应用适合哪些 GIS 场景

Streamlit 的优势是“用 Python 快速做可交互 Web 应用”。对于 GIS 学生、空间数据分析师和初级 GIS 工程师来说,它很适合做以下类型的地图应用:

  • 展示 CSV、GeoJSON、Shapefile 转换后的点线面数据。
  • 做内部用的空间数据质检看板。
  • 展示 GeoPandas 分析结果,例如缓冲区、叠加分析、空间连接结果。
  • 快速制作 PostGIS 查询结果的地图预览。
  • 做一个简单的 WebGIS 原型,用于汇报或方案验证。

但需要注意,Streamlit 不是完整的 WebGIS 框架。它更适合“快速交互式数据应用”,而不是替代 OpenLayers、Leaflet、Cesium 这类前端地图框架做复杂生产系统。

Streamlit做地图应用组件嵌入与Streamlit嵌入Folium地图流程
Streamlit 地图应用常见组件嵌入流程:数据处理、地图渲染组件、Streamlit 页面展示。

背景:为什么 Streamlit 地图组件嵌入会让 GIS 用户困惑

很多 GIS 用户刚开始用 Streamlit 做地图应用时,通常会遇到三个困惑:

  • 不知道用哪个地图组件:Streamlit 自带地图、Folium、Pydeck、Leaflet、Kepler.gl 都能显示地图,但适用场景不同。
  • 不知道如何嵌入:有些地图对象不能直接用 st.write() 显示,需要专门的 Streamlit 组件。
  • 交互结果拿不回来:例如点击地图、框选要素、绘制范围后,想把坐标或属性传回 Python,却发现普通 HTML 嵌入无法直接回传状态。

因此,做 Streamlit 地图应用时,核心不是“能不能显示地图”,而是要判断:你需要的是静态展示、交互浏览,还是需要地图交互结果参与后续分析。

原理:Streamlit 地图应用的几种嵌入方式

Streamlit 做地图应用通常有四类嵌入方式。理解它们的差异,后面选型会简单很多。

1. 使用 Streamlit 原生地图组件

Streamlit 提供了 st.map()st.pydeck_chart()。其中 st.map() 适合快速展示经纬度点数据,代码少,但可定制能力有限。st.pydeck_chart() 基于 Pydeck,适合做大量点、热力图、柱状图、弧线图等可视化。

2. 使用 streamlit-folium 嵌入 Folium 地图

Folium 是 Python GIS 用户非常熟悉的 Leaflet 封装库。它适合加载 GeoJSON、点线面图层、弹窗、图层控制器、简单专题图。要在 Streamlit 中嵌入 Folium,一般使用 streamlit-folium 组件。

3. 使用 HTML 方式嵌入地图

对于一些已生成的 HTML 地图,例如 Folium 导出的 HTML、Kepler.gl 导出的 HTML,可以通过 Streamlit 的 HTML 组件嵌入。但这种方式更偏“展示”,交互状态通常不容易回传给 Python。

4. 使用自定义 Streamlit Component

如果你要把复杂 Leaflet、Mapbox GL、OpenLayers 或 Cesium 组件嵌入 Streamlit,并且需要前后端交互,就需要开发 Streamlit 自定义组件。这种方式灵活,但开发成本最高。

步骤:Streamlit嵌入Folium地图的实用做法

下面用一个最常见的 GIS 场景演示:读取点位数据,在 Streamlit 页面中嵌入 Folium 地图,并显示点位弹窗。

步骤 1:安装依赖

pip install streamlit folium streamlit-folium pandas geopandas

如果只是显示经纬度 CSV 点数据,geopandas 不是必需的;如果你要读取 Shapefile、GeoPackage、GeoJSON,建议安装。

步骤 2:准备一个点位数据

假设有一个 points.csv,字段如下:

name,lon,lat,type
点位A,116.391,39.907,监测点
点位B,116.405,39.915,采样点
点位C,116.382,39.899,巡查点

注意:Folium 和 Leaflet 默认使用 WGS84 经纬度,也就是 EPSG:4326。字段顺序常见写法是经度 lon、纬度 lat,但 Folium 的位置参数通常是 [lat, lon]

步骤 3:编写 Streamlit 地图应用

import streamlit as st
import pandas as pd
import folium
from streamlit_folium import st_folium

st.set_page_config(page_title="Streamlit 地图应用", layout="wide")

st.title("Streamlit 地图应用示例")

df = pd.read_csv("points.csv")

st.sidebar.header("筛选条件")
selected_type = st.sidebar.multiselect(
    "选择点位类型",
    options=df["type"].unique(),
    default=list(df["type"].unique())
)

filtered = df[df["type"].isin(selected_type)]

center_lat = filtered["lat"].mean()
center_lon = filtered["lon"].mean()

m = folium.Map(
    location=[center_lat, center_lon],
    zoom_start=13,
    tiles="OpenStreetMap"
)

for _, row in filtered.iterrows():
    folium.Marker(
        location=[row["lat"], row["lon"]],
        popup=f"{row['name']} - {row['type']}",
        tooltip=row["name"]
    ).add_to(m)

map_data = st_folium(
    m,
    width=1000,
    height=600
)

st.write("当前筛选点位数量:", len(filtered))
st.write("地图交互返回信息:")
st.write(map_data)

运行应用:

streamlit run app.py

这就是一个最小可用的 Streamlit 地图应用。它包含侧边栏筛选、地图嵌入、点位弹窗和地图交互信息返回。

步骤 4:读取 GeoJSON 并显示面图层

如果你的数据是 GeoJSON,可以用 GeoPandas 读取,然后转成 Folium 可识别的 GeoJSON 图层。

import streamlit as st
import geopandas as gpd
import folium
from streamlit_folium import st_folium

gdf = gpd.read_file("districts.geojson")

if gdf.crs is not None and gdf.crs.to_epsg() != 4326:
    gdf = gdf.to_crs(epsg=4326)

center = gdf.geometry.centroid
center_lat = center.y.mean()
center_lon = center.x.mean()

m = folium.Map(location=[center_lat, center_lon], zoom_start=10)

folium.GeoJson(
    gdf.to_json(),
    name="行政区",
    tooltip=folium.GeoJsonTooltip(fields=["name"])
).add_to(m)

folium.LayerControl().add_to(m)

st_folium(m, width=1000, height=650)

这里最重要的一步是坐标系转换。Web 地图展示通常要求经纬度数据。如果你的 Shapefile 是投影坐标,例如 CGCS2000 高斯投影、UTM 或地方坐标,直接显示可能会偏移或完全看不到。

步骤:使用 st.pydeck_chart 做高性能点数据地图

如果你要显示几万甚至更多点位,Folium 可能会变慢。此时可以考虑 Pydeck。它更适合大量点数据可视化,例如散点、热力、聚合、三维柱状图。

import streamlit as st
import pandas as pd
import pydeck as pdk

df = pd.read_csv("points.csv")

layer = pdk.Layer(
    "ScatterplotLayer",
    data=df,
    get_position="[lon, lat]",
    get_radius=80,
    get_fill_color=[255, 80, 80, 160],
    pickable=True
)

view_state = pdk.ViewState(
    latitude=df["lat"].mean(),
    longitude=df["lon"].mean(),
    zoom=12,
    pitch=0
)

deck = pdk.Deck(
    layers=[layer],
    initial_view_state=view_state,
    tooltip={"text": "{name}n{type}"}
)

st.pydeck_chart(deck)

对于 Streamlit 做地图应用来说,Pydeck 的优势是渲染性能和可视化效果,缺点是传统 GIS 图层控制、编辑绘制、复杂弹窗等能力不如 Leaflet 生态直观。

常见坑:Streamlit地图组件嵌入失败如何排查

1. 地图空白或点位不显示

  • 检查经纬度是否写反。Folium 的 location[lat, lon],而 GeoJSON 坐标通常是 [lon, lat]
  • 检查数据是否为 EPSG:4326。不是经纬度坐标时,需要先用 GeoPandas 的 to_crs(epsg=4326) 转换。
  • 检查是否存在空几何、空经纬度、异常坐标,例如经度大于 180 或纬度大于 90。

2. Streamlit嵌入Folium地图后不能交互

如果只是用 HTML 方式嵌入 Folium,地图可以缩放和平移,但很多交互结果不会返回给 Streamlit。建议使用 streamlit-foliumst_folium(),它可以返回部分点击、缩放、边界等状态信息。

3. GeoJSON 文件太大导致页面卡顿

  • 先在 QGIS 或 GeoPandas 中简化几何,减少节点数量。
  • 只加载当前业务需要的字段,删除无关属性。
  • 大规模矢量切片场景不建议直接用 Streamlit + Folium 承载。
  • 如果是大量点数据,优先考虑 Pydeck 或后端聚合。

4. 底图无法加载

底图无法加载通常不是 Streamlit 代码问题,而是瓦片服务访问问题。常见原因包括网络限制、瓦片服务被屏蔽、服务地址失效、需要 Token、浏览器混合内容限制等。正式部署时应使用稳定、合规、可访问的底图服务。

5. 部署后本地文件路径失效

Streamlit 部署到服务器后,读取 C:dataxxx.shp 这类本地路径通常会失败。建议将数据放在项目相对路径中,或改用数据库、对象存储、HTTP 地址等方式管理数据。

方法比较:Streamlit地图组件应该怎么选

方案 适合场景 优点 限制
st.map() 快速显示经纬度点 代码最少,上手最快 样式和交互能力有限
st.pydeck_chart() 大量点数据、热力图、三维可视化 渲染效果好,适合数据可视化 传统 GIS 图层管理能力较弱
streamlit-folium Leaflet/Folium 地图、GeoJSON、弹窗、图层控制 Python GIS 用户友好,生态丰富 大数据量时容易卡顿
HTML 嵌入 展示已生成的地图页面 简单,适合嵌入现成 HTML 交互状态不易回传 Python
自定义 Streamlit Component 复杂 Leaflet、OpenLayers、Cesium 组件 灵活度最高 需要前端开发能力

简单判断:如果你只是做点位展示,用 st.map();如果要做 Folium 地图和 GeoJSON 图层,用 streamlit-folium;如果要做大量点数据可视化,用 st.pydeck_chart();如果要做复杂 WebGIS,建议认真评估是否直接使用 Leaflet、OpenLayers 或 Cesium 开发前端。

检查清单:发布前确认 Streamlit 地图应用是否可靠

  • 数据坐标系是否已经转换为 EPSG:4326。
  • 经纬度字段是否没有写反。
  • 是否处理了空值、无效几何和异常坐标。
  • GeoJSON 或 Shapefile 是否过大,是否需要简化或抽稀。
  • 底图服务是否稳定、合法、可在部署环境访问。
  • 地图交互结果是否真的需要回传 Python,如果需要,是否选择了支持交互返回的组件。
  • 文件路径是否使用相对路径或可部署的数据源。
  • 是否通过侧边栏筛选减少一次性加载的数据量。
  • 是否在部署环境测试过依赖安装和数据读取。

FAQ:Streamlit做地图应用常见问题

Streamlit 可以做真正的 WebGIS 系统吗?

可以做轻量级 WebGIS 原型、内部工具和分析看板,但不建议把它当作完整 WebGIS 前端框架。复杂图层管理、空间编辑、瓦片服务、权限系统、前端路由和大规模并发场景,通常更适合用 OpenLayers、Leaflet、Mapbox GL 或 Cesium 搭配后端服务实现。

Streamlit嵌入Folium地图推荐用什么库?

推荐使用 streamlit-folium。它比简单 HTML 嵌入更适合 Streamlit 应用,可以把 Folium 地图放入页面,并返回部分地图交互状态。

为什么 Folium 地图中的点位偏移很远?

最常见原因是坐标系不对或经纬度顺序写反。Web 地图通常需要 WGS84 经纬度数据。对于 Shapefile、GeoPackage 等数据,建议先用 QGIS 查看图层 CRS,再用 GeoPandas 转换到 EPSG:4326。

Streamlit 地图应用可以读取 Shapefile 吗?

可以。通常使用 GeoPandas 的 read_file() 读取 Shapefile。需要注意 Shapefile 由多个文件组成,至少包括 .shp.shx.dbf,坐标系文件 .prj 也很重要。部署时不要只上传一个 .shp 文件。

大量 GeoJSON 在 Streamlit 中加载很慢怎么办?

先简化几何、减少字段、按范围或条件筛选数据。如果数据量仍然很大,不建议直接把完整 GeoJSON 塞进前端。可以考虑 PostGIS 后端查询、矢量切片、数据聚合,或改用更适合大规模可视化的方案。

Streamlit 地图点击后能获取坐标吗?

使用合适的组件可以获取部分交互结果。例如 streamlit-folium 的返回值中可以包含地图点击位置、边界范围、缩放级别等信息。具体支持哪些交互,需要看你使用的组件接口。

结论:先明确交互需求,再选择嵌入方式

Streamlit做地图应用的关键,不是把地图“显示出来”这么简单,而是选对组件嵌入方式。简单点位展示用 st.map(),大量点数据可视化用 st.pydeck_chart(),常规 GIS 图层展示和 Folium 生态用 streamlit-folium。如果你需要复杂地图编辑、矢量切片、三维场景或完整 WebGIS 交互,就要考虑专门的前端地图框架。

对于 GIS 学习和项目原型来说,Streamlit 是非常实用的工具。只要处理好坐标系、数据量、底图访问和交互返回这几个问题,就能快速搭建一个可用、可演示、可扩展的地图应用。