GIS开发还在用Flask?Streamlit极速原型开发手册,附:三维地图加载源码!

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

引言:如果你正在做 WebGIS 工具、空间数据分析面板或三维地图演示,看到“GIS开发还在用Flask?Streamlit极速原型开发手册,附:三维地图加载源码!”这个题目,大概率是因为你遇到了一个很现实的问题:GIS功能还没验证清楚,却先被前后端工程、路由、模板、接口、部署配置拖慢了。

Flask 当然不是不能做 GIS 开发。它适合构建正式的 Web 服务、空间数据接口和后台系统。但在“快速验证一个 GIS 想法”这件事上,Streamlit 往往更直接:写 Python 脚本,加载空间数据,显示地图,加入参数控件,几分钟就能做出一个可交互原型。

本文聚焦一个具体场景:用 Streamlit 快速搭建 GIS 原型页面,并加载一个三维地图示例。你会看到 Streamlit 适合什么、不适合什么,以及如何把它用于 GIS 学生、空间分析师和 GIS 工程师的日常验证工作。

Streamlit GIS开发 三维地图加载源码工作流
Streamlit 用于 GIS 极速原型开发时,可以把数据处理、参数控件和三维地图预览放在同一个 Python 脚本中完成。

背景:为什么 GIS开发原型阶段不一定要先用 Flask

背景:很多 GIS 开发者习惯从 Flask 开始做 WebGIS 原型:先写后端接口,再写 HTML 模板,再接 Leaflet、OpenLayers 或 Cesium。这个方式很标准,但在早期验证阶段经常显得过重。

例如你只是想回答下面几个问题:

  • 这批点数据能不能按属性做高度表达?
  • 缓冲区半径参数调整后,结果变化是否合理?
  • 某个 GeoJSON 文件是否适合在浏览器端直接加载?
  • 业务人员能否通过下拉框筛选区域并查看地图结果?
  • 三维柱状地图是否适合表达人口、客流、POI密度或灾害强度?

这些问题的核心并不是“搭建一个完整 Web 系统”,而是“快速把 GIS 结果展示出来,并让别人可以操作”。这正是 Streamlit 的优势。

Streamlit 是一个面向数据应用的 Python 框架。它不要求你手写大量前端代码,而是通过 Python API 创建输入框、按钮、侧边栏、图表和地图视图。对于熟悉 Pandas、GeoPandas、PyDeck、Folium 的 GIS 用户来说,上手成本很低。

原理:Streamlit 做 GIS 原型的核心逻辑

原理:Streamlit 的工作方式可以理解为“脚本驱动的交互式页面”。你写一个 Python 文件,Streamlit 运行它,并把其中的控件、图表、地图渲染到网页中。

当用户调整控件时,Streamlit 会重新执行脚本,并根据新的参数刷新结果。这种机制非常适合 GIS 分析原型,因为很多 GIS 操作本身就是“参数输入 -> 数据处理 -> 地图输出”。

典型的 Streamlit GIS 开发流程如下:

  1. 读取 CSV、GeoJSON、Shapefile、GeoPackage 或数据库数据。
  2. 使用 Pandas、GeoPandas、Shapely、PyProj 等库进行空间处理。
  3. 通过侧边栏控件设置筛选条件、分类字段、半径、高度比例等参数。
  4. 使用 PyDeck、Folium、Plotly 或内置地图组件显示结果。
  5. 把脚本运行成一个可访问的本地 Web 页面。

需要注意的是,Streamlit 不是传统意义上的 WebGIS 框架。它更像是一个 GIS 数据应用快速搭建工具。正式生产环境中,如果需要复杂权限、多人并发、高性能瓦片服务、稳定 API 和前后端分离架构,Flask、FastAPI、Django、GeoServer、PostGIS、Cesium 等仍然很重要。

步骤:用 Streamlit 快速搭建 GIS 三维地图原型

步骤:下面演示一个最小可运行的 Streamlit 三维地图加载示例。示例使用 Streamlit 加 PyDeck,把点数据渲染为三维柱状图层。它适合表达 POI 数量、人口密度、订单量、客流量、事故点强度等空间分布。

1. 创建 Python 环境

建议使用独立虚拟环境,避免和已有 GIS 工程依赖冲突。

python -m venv streamlit-gis-env

# Windows
streamlit-gis-envScriptsactivate

# macOS / Linux
source streamlit-gis-env/bin/activate

2. 安装依赖库

这个示例主要使用 Streamlit、Pandas 和 PyDeck。如果你后续要读取 Shapefile 或 GeoPackage,可以再安装 GeoPandas。

pip install streamlit pandas pydeck

如果需要做更多空间数据处理,可以安装:

pip install geopandas shapely pyproj

3. 准备点数据

为了让示例可以直接运行,这里在代码中构造一份模拟数据。实际项目中,你可以把它替换成 CSV、GeoJSON、PostGIS 查询结果或 GeoPandas 读取结果。

字段说明如下:

  • lon:经度。
  • lat:纬度。
  • name:点位名称。
  • value:用于三维高度表达的数值。
  • type:用于分类筛选的类型字段。

4. 编写 Streamlit 三维地图源码

在项目目录中新建 app.py,写入以下代码:

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

st.set_page_config(
    page_title="Streamlit GIS 三维地图原型",
    layout="wide"
)

st.title("Streamlit GIS 三维地图原型")

st.write("这是一个用于 GIS 快速原型开发的三维地图示例,使用 PyDeck 加载点数据并按数值生成柱状高度。")

data = pd.DataFrame({
    "name": ["点位A", "点位B", "点位C", "点位D", "点位E"],
    "lon": [116.397, 116.410, 116.382, 116.430, 116.365],
    "lat": [39.908, 39.920, 39.895, 39.900, 39.915],
    "value": [120, 260, 180, 340, 90],
    "type": ["商业", "交通", "商业", "居住", "交通"]
})

st.sidebar.header("地图参数")

selected_type = st.sidebar.multiselect(
    "选择点位类型",
    options=sorted(data["type"].unique()),
    default=sorted(data["type"].unique())
)

height_scale = st.sidebar.slider(
    "三维高度比例",
    min_value=10,
    max_value=200,
    value=80,
    step=10
)

radius = st.sidebar.slider(
    "柱体半径",
    min_value=50,
    max_value=1000,
    value=300,
    step=50
)

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

st.subheader("筛选后的点数据")
st.dataframe(filtered, use_container_width=True)

if filtered.empty:
    st.warning("当前筛选条件下没有可显示的数据。")
else:
    layer = pdk.Layer(
        "ColumnLayer",
        data=filtered,
        get_position=["lon", "lat"],
        get_elevation="value",
        elevation_scale=height_scale,
        radius=radius,
        get_fill_color=[255, 120, 60, 180],
        pickable=True,
        auto_highlight=True
    )

    view_state = pdk.ViewState(
        longitude=float(filtered["lon"].mean()),
        latitude=float(filtered["lat"].mean()),
        zoom=11,
        pitch=50,
        bearing=0
    )

    tooltip = {
        "html": "<b>名称:</b>{name}<br/><b>类型:</b>{type}<br/><b>数值:</b>{value}",
        "style": {
            "backgroundColor": "steelblue",
            "color": "white"
        }
    }

    deck = pdk.Deck(
        layers=[layer],
        initial_view_state=view_state,
        tooltip=tooltip,
        map_style=None
    )

    st.pydeck_chart(deck, use_container_width=True)

5. 运行 Streamlit 应用

在终端中执行:

streamlit run app.py

浏览器会自动打开本地页面。如果没有自动打开,可以根据终端提示访问类似下面的地址:

http://localhost:8501

运行后,你应该能看到一个三维柱状地图。通过左侧侧边栏可以筛选点位类型、调整柱体半径和三维高度比例。这就是一个最小可用的 Streamlit GIS 原型。

步骤:把 CSV 或 GeoJSON 接入 Streamlit GIS 应用

上面的示例使用的是内置模拟数据。实际项目中,更常见的需求是加载外部空间数据。

加载 CSV 点数据

如果你的 CSV 已经包含经纬度字段,可以这样读取:

uploaded_file = st.sidebar.file_uploader("上传CSV点数据", type=["csv"])

if uploaded_file is not None:
    data = pd.read_csv(uploaded_file)
    st.write(data.head())

请确保 CSV 至少包含经度和纬度字段,例如 lonlat,或者 longitudelatitude。字段名称不统一时,建议在页面中增加下拉框,让用户选择经纬度字段。

加载 GeoJSON 数据

如果要加载 GeoJSON,可以使用 GeoPandas:

import geopandas as gpd

uploaded_geojson = st.sidebar.file_uploader("上传GeoJSON", type=["geojson", "json"])

if uploaded_geojson is not None:
    gdf = gpd.read_file(uploaded_geojson)
    gdf = gdf.to_crs(epsg=4326)

    if gdf.geometry.geom_type.isin(["Point"]).all():
        gdf["lon"] = gdf.geometry.x
        gdf["lat"] = gdf.geometry.y
        st.dataframe(gdf.drop(columns="geometry"))

这里的关键是 to_crs(epsg=4326)。大多数前端地图和 WebGIS 可视化库默认使用 WGS84 经纬度坐标。如果数据是投影坐标系,例如 CGCS2000 高斯投影、Web Mercator 或地方坐标系,直接显示会出现偏移、飞到海上或完全看不见。

常见坑:Streamlit GIS 开发中最容易踩的错误

常见坑:Streamlit GIS 开发很快,但并不代表可以忽略 GIS 基础。下面这些问题在原型阶段非常常见。

1. 坐标系不统一导致地图偏移

三维地图加载失败或显示在错误位置,最常见原因是坐标系问题。PyDeck、Leaflet、OpenLayers 等浏览器地图通常需要经纬度坐标,也就是 EPSG:4326。

  • 如果数据来自 Shapefile,先检查 .prj 文件是否存在。
  • 如果数据来自 PostGIS,检查 ST_SRID(geom) 是否正确。
  • 如果数据来自 GeoPandas,显示前建议统一执行 to_crs(epsg=4326)

2. 字段类型不是数字,导致三维高度异常

用于高度表达的字段必须是数值。如果 CSV 中的 value 字段被读成字符串,三维柱体可能无法正确渲染。

data["value"] = pd.to_numeric(data["value"], errors="coerce")
data = data.dropna(subset=["value"])

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

Streamlit 很适合原型验证,但并不是海量 WebGIS 渲染引擎。如果一次性加载几十万点到浏览器,页面卡顿很正常。

建议处理方式:

  • 先按范围、类型或时间筛选数据。
  • 对点数据做网格聚合或六边形聚合。
  • 对线面数据做简化处理。
  • 大型底图、瓦片和复杂三维场景交给专业 WebGIS 方案处理。

4. 把 Streamlit 当成完整后端框架

Streamlit 更适合数据应用和内部工具,不适合替代所有 Web 后端。如果你的项目需要复杂登录、多角色权限、稳定 REST API、异步任务队列、微服务部署,那么 Flask 或 FastAPI 仍然更合适。

5. 不了解缓存机制导致重复计算很慢

GIS 数据读取和空间分析往往耗时较长。Streamlit 每次交互可能重新执行脚本,因此应合理使用缓存。

@st.cache_data
def load_csv(path):
    return pd.read_csv(path)

对于数据库连接、模型对象等资源,可以考虑使用 st.cache_resource。但缓存不是万能的,输入参数变化、文件更新和内存占用都需要注意。

方法比较:Streamlit、Flask 与传统 WebGIS 方案怎么选

方法比较:在 GIS 开发中,不同工具适合不同阶段。不要简单判断“谁取代谁”,而要看当前任务是原型验证、内部工具,还是正式系统。

方案 适合场景 优势 限制
Streamlit GIS 原型、空间分析面板、内部数据工具、快速演示 开发快,Python 友好,控件和图表集成简单 复杂前端定制、权限系统和高并发能力有限
Flask GIS 后端接口、轻量 Web 服务、空间数据 API 灵活、生态成熟、适合构建服务端逻辑 前端页面和交互需要额外开发
FastAPI 高性能 API、异步接口、前后端分离 WebGIS 接口开发效率高,文档自动生成,类型提示友好 地图界面仍需前端框架配合
Leaflet / OpenLayers 二维 WebGIS 地图应用 前端地图能力强,适合正式地图系统 需要 JavaScript 开发能力
Cesium 三维地球、倾斜摄影、三维场景、时空可视化 三维能力强,适合专业 3D GIS 学习和工程复杂度较高
PostGIS 空间数据库、空间查询、数据管理 空间索引和查询能力强,适合生产数据底座 本身不是前端展示工具

简单来说,如果你要快速验证 GIS 分析结果,用 Streamlit。要做正式接口服务,用 Flask 或 FastAPI。要做复杂 WebGIS 前端,用 Leaflet、OpenLayers 或 Cesium。要管理空间数据和查询性能,用 PostGIS。

检查清单:开始一个 Streamlit GIS 原型前先确认这些

检查清单:为了避免原型看起来能跑、结果却不可靠,建议在开发前按下面清单检查。

  • 是否明确原型目标:展示、筛选、分析、三维表达,还是数据质检?
  • 输入数据格式是否确定:CSV、GeoJSON、Shapefile、GeoPackage、PostGIS?
  • 数据坐标系是否已确认,并能转换到 EPSG:4326?
  • 地图中用于高度、颜色、分类的字段是否存在?
  • 数值字段是否已经转换为数值类型?
  • 数据量是否适合直接在浏览器端渲染?
  • 是否需要缓存读取和空间计算结果?
  • 是否需要导出筛选后的数据或截图结果?
  • 是否只是内部使用,还是需要正式部署给多人访问?
  • 后续是否需要迁移到 Flask、FastAPI、OpenLayers 或 Cesium?

FAQ:Streamlit GIS开发常见问题

FAQ:下面整理一些读者在使用 Streamlit 做 GIS 原型时经常遇到的问题。

1. Streamlit 能不能替代 Flask 做 GIS开发?

不能简单替代。Streamlit 适合 GIS 快速原型、内部工具和数据分析页面;Flask 更适合正式 Web 后端、接口服务和业务系统。如果你的重点是快速展示空间分析结果,Streamlit 更省事。如果你的重点是稳定服务和系统架构,Flask 或 FastAPI 更合适。

2. Streamlit 可以加载三维地图吗?

可以。常见方式是使用 PyDeck,它基于 deck.gl,支持点、线、面、热力、六边形、柱状等多种可视化图层。本文示例中的 ColumnLayer 就可以用于三维柱状地图加载。

3. Streamlit 加载 GeoJSON 很慢怎么办?

先检查 GeoJSON 文件大小和几何复杂度。GeoJSON 是文本格式,面数据复杂时会非常大。可以考虑简化几何、按范围裁剪、转换为矢量瓦片,或者只在 Streamlit 中展示抽样结果。对于正式 WebGIS,不建议把超大 GeoJSON 直接丢给浏览器渲染。

4. 为什么我的点数据显示在海上或完全看不到?

优先检查坐标系。浏览器地图通常需要 WGS84 经纬度。如果你把投影坐标当作经纬度传入,地图位置一定会错。使用 GeoPandas 时,可以通过 gdf.crs 检查坐标系,并用 gdf.to_crs(epsg=4326) 转换。

5. Streamlit 适合部署给客户长期使用吗?

如果是内部分析工具、小团队查看结果或临时演示,可以考虑部署。若是面向大量用户的正式系统,仍需评估认证、权限、并发、日志、安全、数据更新和运维要求。很多团队会先用 Streamlit 验证需求,再迁移到更完整的 WebGIS 架构。

6. 三维地图中的高度比例应该怎么设置?

高度比例取决于数据值范围和地图缩放级别。建议先查看数值字段的最小值、最大值和分布,再通过滑块动态调整。不要只追求视觉夸张,三维高度应服务于空间比较,而不是制造误导。

结论:Streamlit 是 GIS 原型开发的加速器,不是所有 WebGIS 的替代品

结论:对于 GIS 开发来说,Streamlit 的最大价值是减少原型阶段的工程负担。你可以用熟悉的 Python 读取空间数据、处理字段、设置参数控件,并快速把二维或三维地图结果展示给同事、老师、客户或业务方。

如果你的目标是快速验证空间分析思路、制作 GIS 数据看板、演示三维地图加载效果,Streamlit 非常值得掌握。本文给出的三维地图加载源码可以作为起点,后续你可以继续接入 CSV、GeoJSON、GeoPandas、PostGIS 查询结果,扩展成更完整的 GIS 原型应用。

但也要记住:原型开发讲究快,生产系统讲究稳。Streamlit 适合帮你更快走到“需求是否成立”这一步;当需求明确后,再根据项目规模选择 Flask、FastAPI、OpenLayers、Cesium、PostGIS 等更完整的 GIS 技术栈,才是更稳妥的路线。