GIS项目Web可视化太丑?手把手教你用Streamlit打造高颜值交互界面(含:组件源码)

编程与开发
Dr.GIS
wowwwai GIS研习社 · 工具流程与项目排障

GIS项目Web可视化太丑?手把手教你用Streamlit打造高颜值交互界面(含:组件源码),这个问题很多 GIS 学生和入门工程师都会遇到:空间分析结果已经做出来了,地图也能显示,但交互界面像临时脚本,给老师、甲方或团队演示时缺少“产品感”。本文用一个可复现的 Streamlit GIS 可视化项目,带你把 GeoJSON 数据、指标卡片、筛选控件、交互地图和图表面板整合成一个更清爽的 Web 界面。

引言:为什么 GIS项目Web可视化 经常显得“不够好看”

很多 GIS 项目不是输在空间分析,而是输在展示层。常见情况是:地图占满页面、图例随意摆放、筛选条件不明显、统计图和地图没有联动,最后看起来像“工具窗口截图”,而不是一个完整的 WebGIS 演示界面。

Streamlit 的优势是上手快,不需要完整掌握前端框架,也能用 Python 快速搭建交互式数据应用。对于 GIS 场景,它适合做这些工作:

  • 空间分析成果的快速 Web 展示。
  • GeoJSON、Shapefile 转换后的轻量级地图预览。
  • 项目汇报用的交互式仪表盘。
  • 带筛选、统计、地图联动的 GIS 数据探索工具。
  • 给非技术人员使用的内部数据查看页面。

本文重点不是讲复杂前端,而是解决一个具体问题:如何用 Streamlit 把 GIS 数据做成更美观、更易用、更适合演示的交互界面。

GIS项目Web可视化 Streamlit交互地图界面示意图
Streamlit GIS 可视化界面的基本组成:数据、筛选、地图、指标卡片和统计图表。

背景:适合用 Streamlit 做 GIS Web 可视化的场景

Streamlit 不是传统意义上的 WebGIS 框架,它更像是一个 Python 数据应用框架。它的核心价值是:用少量 Python 代码,把数据处理、地图展示和交互控件组织成一个可访问的网页。

对于 GIS 项目,以下场景特别适合使用 Streamlit:

  • 课程设计展示:例如城市 POI 分析、人口密度分析、道路可达性分析。
  • 毕业设计原型:需要一个能点击、能筛选、能看图表的 Web 展示页面。
  • 空间分析报告:把缓冲区分析、叠加分析、栅格统计结果做成可交互仪表盘。
  • 内部数据查看:给业务同事查看地块、网点、管线、巡检点等空间数据。
  • 算法结果演示:比如路径规划、选址分析、风险分区、变化检测结果展示。

但也要注意,Streamlit 并不适合替代完整 WebGIS 系统。如果项目需要复杂权限、多人编辑、海量矢量瓦片、前端定制组件和长期运维,通常还是要考虑 OpenLayers、Leaflet、Mapbox GL JS 或 Cesium 等更专业的前端方案。

原理:Streamlit GIS 可视化界面由哪些部分组成

一个好看的 Streamlit GIS 可视化界面,本质上由五个部分组成:

  1. 数据读取层:使用 GeoPandas 读取 GeoJSON、Shapefile、GeoPackage 等空间数据。
  2. 空间处理层:完成坐标系转换、字段清洗、分类统计、范围筛选。
  3. 地图展示层:使用 Folium、PyDeck 或 Plotly 展示交互地图。
  4. 交互控制层:使用 Streamlit 的 sidebar、selectbox、multiselect、slider 等控件。
  5. 信息表达层:用指标卡片、图表、表格和说明文字让用户理解地图结果。

如果只把地图塞进页面,界面通常会显得粗糙。更推荐的布局是:左侧控制条件,中间地图展示,顶部给关键指标,底部补充图表和明细表。

在本文示例中,我们使用以下技术组合:

  • Streamlit:搭建 Web 应用界面。
  • GeoPandas:读取和处理 GIS 矢量数据。
  • Folium:生成 Leaflet 风格的交互地图。
  • streamlit-folium:把 Folium 地图嵌入 Streamlit 页面。
  • Pandas:做属性表统计和图表数据整理。

步骤:从零搭建一个高颜值 Streamlit GIS 交互界面

步骤 1:准备 Python 环境

建议使用独立虚拟环境,避免和 ArcGIS Pro、QGIS 自带 Python 环境混在一起。下面示例以普通 Python 环境为准。

pip install streamlit geopandas folium streamlit-folium pandas shapely pyproj

如果你在 Windows 上安装 GeoPandas 报错,可以优先使用 Conda 环境:

conda create -n streamlit-gis python=3.10
conda activate streamlit-gis
conda install -c conda-forge geopandas folium streamlit-folium streamlit

步骤 2:准备示例数据结构

假设我们有一个城市公共服务点数据,文件名为 data/poi.geojson,至少包含以下字段:

字段名 含义 示例
name 点位名称 第一人民医院
type 设施类型 医院、学校、公园
district 所属区县 海淀区
score 综合评分 86
geometry 空间几何 Point

如果你手头是 Shapefile,也可以先用 QGIS 另存为 GeoJSON,或者在 Python 中直接读取 .shp。需要注意的是,Shapefile 字段名可能被截断,中文编码也更容易出问题。

步骤 3:创建 Streamlit 主程序

新建文件 app.py,先完成页面配置、数据读取和基础样式。

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

st.set_page_config(
    page_title="GIS 项目 Web 可视化仪表盘",
    page_icon="🗺️",
    layout="wide"
)

st.markdown(
    """
    <style>
    .main {
        background-color: #f7f9fc;
    }
    .block-container {
        padding-top: 1.5rem;
        padding-bottom: 2rem;
    }
    .metric-card {
        background: white;
        padding: 18px;
        border-radius: 14px;
        box-shadow: 0 4px 16px rgba(0,0,0,0.06);
        border: 1px solid #edf0f5;
    }
    </style>
    """,
    unsafe_allow_html=True
)

@st.cache_data
def load_data(path):
    gdf = gpd.read_file(path)
    if gdf.crs is None:
        gdf = gdf.set_crs(epsg=4326)
    else:
        gdf = gdf.to_crs(epsg=4326)
    return gdf

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

这里有两个关键点:

  • layout="wide" 可以让 GIS 地图获得更宽的显示空间。
  • 地图展示通常需要 WGS84 坐标系,也就是 EPSG:4326,因此读取后要统一转换。

步骤 4:设计侧边栏筛选控件

GIS项目Web可视化要好用,筛选控件必须清晰。不要把所有字段都堆在页面上,优先放用户最关心的字段,例如行政区、类型、评分范围。

st.sidebar.title("筛选条件")

district_options = sorted(gdf["district"].dropna().unique().tolist())
type_options = sorted(gdf["type"].dropna().unique().tolist())

selected_districts = st.sidebar.multiselect(
    "选择区县",
    district_options,
    default=district_options
)

selected_types = st.sidebar.multiselect(
    "选择设施类型",
    type_options,
    default=type_options
)

min_score = int(gdf["score"].min())
max_score = int(gdf["score"].max())

score_range = st.sidebar.slider(
    "评分范围",
    min_value=min_score,
    max_value=max_score,
    value=(min_score, max_score)
)

filtered = gdf[
    (gdf["district"].isin(selected_districts)) &
    (gdf["type"].isin(selected_types)) &
    (gdf["score"].between(score_range[0], score_range[1]))
].copy()

这样用户可以通过左侧控件动态控制地图内容,页面会自动刷新。对于 GIS 展示项目来说,这比静态地图截图更有说服力。

步骤 5:添加标题区和指标卡片

高颜值 Streamlit 界面的关键不只是颜色,而是信息层次。顶部最好先告诉用户当前数据规模、筛选结果和核心指标。

st.title("城市公共服务设施 GIS 可视化仪表盘")
st.caption("基于 Streamlit + GeoPandas + Folium 的轻量级 WebGIS 展示示例")

col1, col2, col3, col4 = st.columns(4)

total_count = len(filtered)
district_count = filtered["district"].nunique()
type_count = filtered["type"].nunique()
avg_score = round(filtered["score"].mean(), 1) if total_count > 0 else 0

with col1:
    st.metric("点位数量", total_count)

with col2:
    st.metric("覆盖区县", district_count)

with col3:
    st.metric("设施类型", type_count)

with col4:
    st.metric("平均评分", avg_score)

指标卡片适合放“第一眼要看的信息”。如果你的项目是土地利用,可以放建设用地面积、耕地面积、变化面积;如果是管线巡检,可以放隐患点数量、已处理数量、超期数量。

步骤 6:用 Folium 绘制交互地图

下面是核心地图组件源码。它会根据筛选结果动态显示点位,并按设施类型设置颜色。

def get_color(category):
    color_map = {
        "医院": "red",
        "学校": "blue",
        "公园": "green",
        "商场": "purple",
        "交通": "orange"
    }
    return color_map.get(category, "gray")

def create_map(data):
    if data.empty:
        return folium.Map(location=[35.0, 105.0], zoom_start=4, tiles="CartoDB positron")

    center_lat = data.geometry.y.mean()
    center_lon = data.geometry.x.mean()

    m = folium.Map(
        location=[center_lat, center_lon],
        zoom_start=11,
        tiles="CartoDB positron"
    )

    for _, row in data.iterrows():
        popup_html = f"""
        <b>名称:</b>{row["name"]}<br>
        <b>类型:</b>{row["type"]}<br>
        <b>区县:</b>{row["district"]}<br>
        <b>评分:</b>{row["score"]}
        """

        folium.CircleMarker(
            location=[row.geometry.y, row.geometry.x],
            radius=7,
            color=get_color(row["type"]),
            fill=True,
            fill_color=get_color(row["type"]),
            fill_opacity=0.75,
            popup=folium.Popup(popup_html, max_width=260),
            tooltip=row["name"]
        ).add_to(m)

    folium.LayerControl().add_to(m)
    return m

把地图嵌入页面:

left_col, right_col = st.columns([2.2, 1])

with left_col:
    st.subheader("交互地图")
    map_obj = create_map(filtered)
    st_folium(map_obj, width=None, height=620)

with right_col:
    st.subheader("类型统计")
    type_count_df = filtered["type"].value_counts().reset_index()
    type_count_df.columns = ["设施类型", "数量"]
    st.bar_chart(type_count_df.set_index("设施类型"))

    st.subheader("区县统计")
    district_count_df = filtered["district"].value_counts().reset_index()
    district_count_df.columns = ["区县", "数量"]
    st.dataframe(district_count_df, use_container_width=True)

这里使用左右分栏:左侧放地图,右侧放统计图和表格。这个布局很适合 GIS项目Web可视化,因为地图负责空间位置,图表负责数量对比,表格负责明细核查。

步骤 7:添加数据明细和下载按钮

实际项目中,用户通常希望筛选后能看到数据表,甚至导出结果。可以继续添加明细表和 CSV 下载。

st.subheader("筛选结果明细")

show_columns = ["name", "type", "district", "score"]
st.dataframe(
    filtered[show_columns],
    use_container_width=True,
    hide_index=True
)

csv_data = filtered[show_columns].to_csv(index=False).encode("utf-8-sig")

st.download_button(
    label="下载筛选结果 CSV",
    data=csv_data,
    file_name="filtered_gis_result.csv",
    mime="text/csv"
)

如果数据包含中文,导出 CSV 时建议使用 utf-8-sig,这样用 Excel 打开时更不容易乱码。

步骤 8:运行项目

在终端进入项目目录,执行:

streamlit run app.py

浏览器打开本地地址后,你就可以看到一个具备筛选、指标卡片、交互地图、图表和数据下载功能的 Streamlit GIS 可视化界面。

常见坑:Streamlit GIS 可视化最容易出错的地方

1. 地图点位跑到海上或完全不显示

最常见原因是坐标系错误。Folium 和 Leaflet 默认使用经纬度坐标,也就是 WGS84。如果你的数据是 CGCS2000 高斯投影、Web Mercator 或地方坐标,必须先转换到 EPSG:4326

gdf = gdf.to_crs(epsg=4326)

判断方法很简单:经度通常在 -180 到 180 之间,纬度通常在 -90 到 90 之间。如果坐标值是几十万或几百万,多半不是经纬度。

2. Shapefile 中文字段乱码

Shapefile 由多个文件组成,编码问题很常见。建议优先把数据转换为 GeoPackage 或 GeoJSON。如果必须读取 Shapefile,可以尝试指定编码:

gdf = gpd.read_file("data/poi.shp", encoding="utf-8")
gdf = gpd.read_file("data/poi.shp", encoding="gbk")

如果一个不行,再换另一个。国内历史数据中 gbk 很常见。

3. 数据量太大导致页面卡顿

Streamlit GIS 可视化适合中小规模数据快速展示。如果一次性渲染几万甚至几十万个点,Folium 地图会明显变慢。

可以考虑这些优化:

  • 先按行政区、类型或时间筛选,再渲染地图。
  • 只显示当前视图需要的数据,不要全量加载。
  • 点数据过多时使用聚合显示,例如 MarkerCluster。
  • 大规模矢量数据考虑切片服务或矢量瓦片。
  • 面数据先进行简化,减少几何节点数量。

4. 页面看起来还是很“脚本感”

原因通常不是 Streamlit 不行,而是布局和信息层次没有设计。建议遵循这个顺序:

  1. 顶部放项目标题和一句话说明。
  2. 左侧放筛选条件。
  3. 第一屏放核心指标和地图。
  4. 地图旁边放图表,而不是把所有内容垂直堆叠。
  5. 底部放明细表、下载按钮和说明。

5. Streamlit 部署后找不到数据文件

部署时要检查相对路径。推荐项目结构如下:

streamlit-gis-demo/
├── app.py
├── data/
│   └── poi.geojson
├── requirements.txt

代码中使用:

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

不要写成本机绝对路径,例如 D:projectdatapoi.geojson,否则部署到服务器后会直接失效。

方法比较:Streamlit、Leaflet、OpenLayers、ArcGIS Experience Builder 怎么选

方案 适合场景 优点 限制
Streamlit Python GIS 分析结果展示、课程设计、内部原型 开发快,Python 友好,适合数据分析和仪表盘 复杂前端交互和大型 WebGIS 能力有限
Leaflet 轻量 Web 地图、点线面展示、移动端地图 轻巧,插件丰富,学习成本相对低 需要写 JavaScript,复杂工程需自行组织
OpenLayers 专业 WebGIS、复杂图层控制、投影支持 GIS 能力强,适合严肃 WebGIS 项目 学习曲线比 Leaflet 高
ArcGIS Experience Builder ArcGIS 生态内的快速应用搭建 低代码,和 ArcGIS Online、Portal 集成好 依赖 Esri 生态,定制和部署受平台影响

简单判断:如果你的核心工作在 Python 里,目标是快速展示 GIS 分析成果,Streamlit 很合适;如果你要做正式 WebGIS 产品,尤其涉及复杂图层、权限、编辑和长期维护,就应该考虑 Leaflet、OpenLayers 或成熟 GIS 平台。

检查清单:做一个更好看的 Streamlit GIS 项目前先核对这些项

  • 坐标系:地图展示前是否统一到 EPSG:4326
  • 字段:筛选字段、统计字段、弹窗字段是否清晰可读?
  • 布局:是否使用宽屏布局和合理分栏?
  • 第一屏:用户打开后是否能立刻看到标题、指标和地图?
  • 交互:是否提供区县、类型、时间、评分等关键筛选?
  • 地图:底图是否简洁?点线面符号是否区分明显?
  • 图表:是否用图表解释地图背后的数量关系?
  • 性能:是否避免一次性渲染过大的矢量数据?
  • 导出:是否提供筛选结果下载?
  • 部署:是否使用相对路径,并准备好 requirements.txt

FAQ:Streamlit GIS 可视化常见问题

Q1:Streamlit 能不能直接显示 Shapefile?

可以。使用 GeoPandas 的 read_file 可以读取 Shapefile,但要保证 .shp.dbf.shx.prj 等文件完整。为了部署和分享更方便,建议转换成 GeoJSON 或 GeoPackage。

Q2:Streamlit 做 GIS项目Web可视化 是否需要会 JavaScript?

不一定。使用 Folium、PyDeck、Plotly 等 Python 库,可以完成很多地图展示和交互。如果要做非常复杂的前端地图交互,例如自定义绘制、复杂图层管理、前端空间编辑,就需要 JavaScript 和 WebGIS 前端框架。

Q3:Folium 和 PyDeck 选哪个更适合 GIS 地图?

Folium 更接近传统 Leaflet 地图,适合点线面、弹窗、图层控制等常规 GIS 展示。PyDeck 更适合大规模点、轨迹、热力和 3D 风格的数据可视化。入门项目建议先用 Folium。

Q4:Streamlit 页面如何变得更美观?

优先调整信息结构,而不是一开始就改 CSS。先做好标题、指标卡片、分栏、地图、图表和表格的位置关系。必要时再用少量 CSS 调整背景、边距、卡片圆角和阴影。

Q5:数据量很大时还适合用 Streamlit 吗?

适合做分析后台和结果预览,但不适合直接渲染海量原始矢量数据。大数据量场景建议先做抽稀、聚合、分区筛选,或者使用 PostGIS、GeoServer、矢量瓦片等服务端方案。

Q6:Streamlit GIS 应用怎么部署?

常见方式包括内网服务器部署、Docker 部署、Streamlit Community Cloud 部署等。部署前要准备 requirements.txt,并确保数据文件路径使用相对路径。如果涉及敏感空间数据,建议优先部署在单位内网环境。

结论:用 Streamlit 快速提升 GIS 项目的展示效果

GIS项目Web可视化太丑,通常不是因为分析结果不专业,而是界面没有组织好。用 Streamlit 可以在不深入前端开发的情况下,把 GIS 数据读取、筛选、交互地图、指标卡片、统计图和结果下载整合到一个清爽的 Web 页面中。

本文给出的组件源码可以作为一个基础模板:左侧筛选、顶部指标、中间地图、右侧统计、底部明细。你可以把示例中的 POI 数据替换成地块、管线、道路、遥感解译结果或空间分析成果,再根据项目主题调整字段和符号样式。

如果你的目标是课程设计、毕业设计、项目汇报或内部 GIS 数据看板,Streamlit 是一个非常实用的选择;如果后续要走向正式 WebGIS 产品,再逐步迁移到 Leaflet、OpenLayers、Cesium 或企业 GIS 平台会更稳妥。