Streamlit开发WebGIS?地图组件怎么嵌?

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

很多做 GIS 原型系统的同学都会遇到一个问题:Streamlit开发WebGIS?地图组件怎么嵌? 如果只是展示一个点图,几行代码就能跑起来;但一旦涉及 GeoJSON、底图、点击交互、图层控制和部署,选择哪个地图组件、数据怎么传、性能怎么控制,就会直接影响项目是否可用。

本文以 Streamlit 开发轻量级 WebGIS 为场景,重点讲清楚 Streamlit 嵌入地图组件的几种常用方式:st.mappydeckstreamlit-folium 和自定义 HTML 组件。你可以根据数据量、交互需求和部署环境,快速选出合适方案。

引言:Streamlit开发WebGIS时为什么地图组件是关键

Streamlit 的优势是开发快,特别适合做 GIS 数据看板、空间分析演示、内部工具和算法原型。但 Streamlit 本身不是传统意义上的 WebGIS 框架,它更像是一个 Python 数据应用框架。

所以在 Streamlit 开发 WebGIS 时,地图能力通常来自外部组件。例如:

  • 只看经纬度点位:使用 st.map
  • 需要大规模点、线、面可视化:使用 pydeck
  • 想用 Leaflet 生态和常见 GIS 图层:使用 streamlit-folium
  • 需要接入已有 OpenLayers、Cesium 或复杂前端地图:使用 Streamlit 自定义 HTML 组件。

如果选错组件,常见结果是:地图能显示但不能交互,GeoJSON 加载慢,坐标系不对,部署后底图空白,或者 Streamlit 每次刷新导致地图状态丢失。

Streamlit开发WebGIS地图组件嵌入与streamlit-folium方案对比
Streamlit 开发 WebGIS 时常见地图组件嵌入路径与适用场景。

背景:Streamlit嵌入地图组件的典型需求

在 GIS 项目中,Streamlit 嵌入地图组件通常不是为了做一个完整的互联网地图平台,而是解决以下几类实际问题:

  • 展示采样点、门店、设备、事件等经纬度点位。
  • 叠加行政区、网格、缓冲区、分析结果等 GeoJSON 图层。
  • 在地图上点击要素,并把属性返回给 Streamlit 表格或图表。
  • 快速验证空间分析结果,例如选址、覆盖范围、轨迹可视化。
  • 把 Python 处理后的 GeoPandas 数据直接展示成 WebGIS 地图。

这些需求看起来都叫“嵌地图”,但技术复杂度差异很大。一个只读点图和一个支持点击交互的多图层 WebGIS,选型完全不同。

原理:Streamlit地图组件到底是怎么嵌进去的

理解 Streamlit 地图组件嵌入方式,先要分清两层:

  • Python 后端层:负责读取 Shapefile、GeoJSON、PostGIS 查询结果,进行 GeoPandas 或 Pandas 处理。
  • 前端地图层:负责在浏览器中渲染地图,例如 Leaflet、deck.gl、Mapbox GL 或 OpenLayers。

Streamlit 的地图组件本质上是在这两层之间做桥接。Python 端把数据转换成组件可识别的格式,前端组件在浏览器里完成地图渲染。

常见组件的原理可以简单理解为:

  • st.map:Streamlit 内置地图接口,适合快速显示经纬度点。
  • pydeck:基于 deck.gl,适合点云、轨迹、热力图、面图层等可视化。
  • streamlit-folium:把 Folium 生成的 Leaflet 地图嵌入 Streamlit,适合传统 WebGIS 图层展示。
  • components.html:直接把 HTML、JavaScript 地图页面嵌入 Streamlit,灵活度最高,但维护成本也最高。

GIS 数据还要注意一个核心前提:大多数 Web 地图组件默认使用经纬度坐标,也就是 WGS84 坐标系,EPSG:4326。很多国内项目数据可能是 CGCS2000、地方投影坐标或 Web Mercator,如果不转换,地图会出现偏移、变形或完全看不到。

步骤:用st.map快速嵌入最简单的点位地图

如果你的需求只是把点位展示在地图上,st.map 是最省事的方案。它适合教学演示、数据预览和非常轻量的点位可视化。

1. 准备经纬度字段

st.map 默认识别 latlon 字段,也可以识别 latitudelongitude。示例代码如下:

import streamlit as st
import pandas as pd

st.set_page_config(page_title="Streamlit WebGIS 点位示例", layout="wide")

data = pd.DataFrame({
    "name": ["点位A", "点位B", "点位C"],
    "lat": [31.2304, 31.2201, 31.2406],
    "lon": [121.4737, 121.5002, 121.4558]
})

st.title("Streamlit 点位地图")
st.dataframe(data)
st.map(data, latitude="lat", longitude="lon", zoom=11)

2. 适合使用st.map的场景

  • 只需要展示点位,不需要复杂图层控制。
  • 数据量不大,主要用于快速预览。
  • 不需要点击要素返回属性。
  • 不需要叠加 GeoJSON 面图层或矢量瓦片。

如果你想做 Streamlit 开发 WebGIS 的正式原型,st.map 只能作为入门方案。它的优势是简单,限制也很明显。

步骤:用streamlit-folium嵌入Leaflet地图

如果你希望在 Streamlit 中使用更接近传统 WebGIS 的地图能力,streamlit-folium 是非常实用的选择。它可以使用 Folium 创建 Leaflet 地图,并在 Streamlit 页面中显示。

1. 安装依赖

pip install streamlit folium streamlit-folium geopandas

如果你需要读取 Shapefile,还要确保本机 GeoPandas 环境可以正常读取矢量数据。建议使用 Conda 环境安装 GIS 依赖,避免 GDAL、Fiona、pyproj 版本冲突。

2. 嵌入一个基础 Leaflet 地图

import streamlit as st
import folium
from streamlit_folium import st_folium

st.set_page_config(page_title="Streamlit Folium WebGIS", layout="wide")

st.title("Streamlit 嵌入 Folium 地图")

m = folium.Map(
    location=[31.2304, 121.4737],
    zoom_start=11,
    tiles="OpenStreetMap"
)

folium.Marker(
    location=[31.2304, 121.4737],
    popup="上海中心点",
    tooltip="点击查看属性"
).add_to(m)

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

这段代码会在 Streamlit 页面中嵌入一个 Leaflet 地图,并显示一个可点击 Marker。对于 GIS 初学者来说,这是最容易理解的 WebGIS 嵌图方式。

3. 加载GeoJSON面图层

实际 GIS 项目中,常见需求是把行政区、网格、缓冲区或分析结果作为 GeoJSON 图层叠加到地图上。

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

st.set_page_config(page_title="Streamlit GeoJSON 图层", layout="wide")

geojson_path = "data/district.geojson"
gdf = gpd.read_file(geojson_path)

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

center = gdf.geometry.centroid.iloc[0]
m = folium.Map(location=[center.y, center.x], zoom_start=10, tiles="OpenStreetMap")

folium.GeoJson(
    gdf,
    name="行政区图层",
    tooltip=folium.GeoJsonTooltip(fields=["name"], aliases=["名称"])
).add_to(m)

folium.LayerControl().add_to(m)

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

这里有一个关键点:在加载 GeoJSON 之前,先检查坐标系。如果原始数据不是 EPSG:4326,就用 to_crs(epsg=4326) 转换。很多 Streamlit 地图不显示的问题,本质上都是坐标系错误。

4. 获取地图点击结果

streamlit-folium 可以把部分地图交互结果返回给 Streamlit。比如用户点击地图后,可以获取点击点坐标:

import streamlit as st
import folium
from streamlit_folium import st_folium

m = folium.Map(location=[31.2304, 121.4737], zoom_start=11)

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

st.write("地图交互返回结果:")
st.write(map_data)

if map_data and map_data.get("last_clicked"):
    lat = map_data["last_clicked"]["lat"]
    lng = map_data["last_clicked"]["lng"]
    st.success(f"你点击的位置:纬度 {lat}, 经度 {lng}")

这类交互适合做点选查询、人工标注、坐标拾取等轻量 WebGIS 功能。但如果你要做复杂编辑、拓扑检查或大量要素选择,建议使用专业前端 WebGIS 框架。

步骤:用pydeck嵌入高性能空间可视化地图

当数据量较大,或者你需要热力图、轨迹、柱状图、点云效果时,可以考虑 pydeck。它基于 deck.gl,更适合数据可视化型 WebGIS。

1. 安装依赖

pip install streamlit pydeck pandas

2. 显示点图层

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

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

df = pd.DataFrame({
    "name": ["点位A", "点位B", "点位C"],
    "lat": [31.2304, 31.2201, 31.2406],
    "lon": [121.4737, 121.5002, 121.4558],
    "value": [10, 30, 20]
})

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

view_state = pdk.ViewState(
    latitude=31.2304,
    longitude=121.4737,
    zoom=10,
    pitch=0
)

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

st.pydeck_chart(deck)

pydeck 的优势是图层表达能力强,适合 GIS 数据看板。缺点是它更偏空间可视化,不像 Leaflet 那样直接适配大量传统 GIS 插件。

步骤:用components.html嵌入自定义WebGIS页面

如果你已经有 OpenLayers、Leaflet、Mapbox GL JS 或 Cesium 写好的前端地图页面,也可以用 Streamlit 的 HTML 组件直接嵌入。

import streamlit as st
import streamlit.components.v1 as components

html = """
<div id="map" style="width:100%;height:600px;background:#eef;">
  这里可以放置自定义 WebGIS 地图容器
</div>
<script>
  console.log("在这里初始化 Leaflet、OpenLayers 或 Cesium 地图");
</script>
"""

components.html(html, height=650)

这种方式灵活度最高,适合接入已有 WebGIS 前端代码。但要注意,Streamlit 与 JavaScript 之间的数据交互并不像完整前后端项目那样自由。如果需要复杂状态同步,需要额外封装 Streamlit Component。

常见坑:Streamlit地图组件怎么嵌都不对时先查这些

1. 坐标系不是EPSG:4326

这是最常见问题。Web 地图组件通常使用经纬度坐标,很多 GIS 数据是投影坐标。比如米制坐标直接传给地图,地图当然找不到正确位置。

import geopandas as gpd

gdf = gpd.read_file("data/input.shp")
print(gdf.crs)

gdf_4326 = gdf.to_crs(epsg=4326)
gdf_4326.to_file("data/output.geojson", driver="GeoJSON")

2. 经纬度字段写反

很多组件位置字段要求是 [lon, lat],也就是先经度后纬度。但表格里经常写成 lat, lon。如果地图点位跑到海里或国外,优先检查这个问题。

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

Streamlit 每次脚本重新运行时,地图组件也可能重新渲染。如果 GeoJSON 文件很大,页面会明显变慢。建议:

  • 先用 QGIS 或 GeoPandas 简化几何。
  • 只加载当前分析需要的字段。
  • 大数据量场景考虑矢量瓦片或 PostGIS 后端查询。
  • 在 Streamlit 中使用缓存减少重复读取。
import streamlit as st
import geopandas as gpd

@st.cache_data
def load_data(path):
    gdf = gpd.read_file(path)
    return gdf.to_crs(epsg=4326)

gdf = load_data("data/district.geojson")

4. 部署后底图不显示

本地能显示,部署后底图不显示,通常和网络访问、瓦片服务、HTTPS、跨域或 Token 配置有关。尤其是使用第三方底图服务时,要确认部署环境能访问该服务。

5. Streamlit刷新导致地图状态丢失

Streamlit 的运行机制是交互触发脚本从上到下重新执行。如果没有使用 st.session_state 或组件返回值保存状态,地图选择、点击结果和筛选条件可能会丢失。

方法比较:st.map、pydeck、streamlit-folium和自定义组件怎么选

方案 适合场景 优点 限制
st.map 快速点位预览 内置、代码少、上手快 图层和交互能力有限
pydeck 空间数据可视化、热力图、轨迹、较大点数据 视觉表达强,适合数据看板 传统 GIS 图层管理能力较弱
streamlit-folium Leaflet 地图、GeoJSON 图层、Marker、图层控制 GIS 友好,生态成熟,易于教学和原型 大数据量 GeoJSON 性能有限
components.html 嵌入已有 OpenLayers、Cesium 或复杂前端地图 灵活度最高 需要前端能力,交互同步成本高

如果你是 GIS 学生或刚入门的 GIS 工程师,建议从 streamlit-folium 开始。它既能体现 WebGIS 的图层思想,又不会像完整前端开发那样复杂。

检查清单:Streamlit开发WebGIS地图组件嵌入前后要确认什么

  • 数据是否有明确坐标系,是否已转换为 EPSG:4326。
  • 点数据字段是否明确区分经度 lon 和纬度 lat。
  • GeoJSON 是否过大,是否需要简化几何或裁剪范围。
  • 是否需要点击、框选、弹窗、图层控制等交互。
  • 是否需要接入 PostGIS、Shapefile、GeoPackage 或在线接口。
  • 底图服务在部署环境中是否可以访问。
  • 是否使用 st.cache_data 缓存空间数据读取结果。
  • 是否需要用 st.session_state 保存地图交互状态。
  • 项目是内部原型,还是面向公众访问的正式 WebGIS。

FAQ:Streamlit开发WebGIS常见问题

Q1:Streamlit适合开发正式WebGIS系统吗?

Streamlit 更适合 GIS 原型、内部工具、分析看板和教学演示。如果是高并发、复杂权限、海量图层、地图编辑和长期运维的正式 WebGIS,建议使用前后端分离架构,例如 FastAPI、PostGIS、OpenLayers 或 Cesium。

Q2:Streamlit地图组件怎么嵌GeoJSON最方便?

最方便的是使用 streamlit-folium。先用 GeoPandas 读取 GeoJSON,再确认坐标系为 EPSG:4326,最后通过 Folium 的 GeoJson 图层添加到地图中。

Q3:streamlit-folium和pydeck哪个更适合GIS?

如果你的重点是传统 GIS 图层、Marker、弹窗和图层控制,streamlit-folium 更合适。如果重点是数据可视化、大量点位、热力图和三维柱状表达,pydeck 更合适。

Q4:为什么Streamlit地图在本地能显示,部署后不显示?

常见原因包括底图服务无法访问、网络被限制、Token 没配置、HTTPS 混合内容问题、GeoJSON 路径错误。建议先检查浏览器控制台,再确认部署服务器是否能访问瓦片服务。

Q5:Streamlit能不能嵌入OpenLayers或Cesium?

可以通过 components.html 嵌入简单页面,也可以开发自定义 Streamlit Component。但如果项目核心是 OpenLayers 或 Cesium 复杂交互,建议把 Streamlit 作为分析后台或管理界面,而不是承担全部前端 WebGIS 功能。

结论:先按需求选组件,再处理坐标系和性能

回到标题中的问题:Streamlit开发WebGIS?地图组件怎么嵌? 最实用的答案不是只选一个组件,而是按需求分层选择。

只做点位预览,用 st.map;做 GIS 教学、GeoJSON 图层和常规交互,用 streamlit-folium;做空间数据可视化和较大规模点图层,用 pydeck;要嵌入已有 OpenLayers、Cesium 或复杂前端地图,用 components.html 或自定义组件。

无论选择哪种方案,都要优先检查三个问题:坐标系是否正确、数据量是否适合前端直接加载、部署环境是否能访问底图服务。把这三点处理好,Streamlit 开发轻量级 WebGIS 就会稳定很多。