init proj
This commit is contained in:
38
.dockerignore
Normal file
38
.dockerignore
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
# Python
|
||||||
|
__pycache__/
|
||||||
|
*.py[cod]
|
||||||
|
*$py.class
|
||||||
|
*.so
|
||||||
|
.Python
|
||||||
|
*.egg-info/
|
||||||
|
dist/
|
||||||
|
build/
|
||||||
|
.venv/
|
||||||
|
venv/
|
||||||
|
env/
|
||||||
|
|
||||||
|
# IDE
|
||||||
|
.vscode/
|
||||||
|
.idea/
|
||||||
|
*.swp
|
||||||
|
*.swo
|
||||||
|
*~
|
||||||
|
|
||||||
|
# OS
|
||||||
|
.DS_Store
|
||||||
|
Thumbs.db
|
||||||
|
|
||||||
|
# Git
|
||||||
|
.git/
|
||||||
|
.gitignore
|
||||||
|
|
||||||
|
# Docker
|
||||||
|
Dockerfile
|
||||||
|
.dockerignore
|
||||||
|
|
||||||
|
# Other
|
||||||
|
*.log
|
||||||
|
.pytest_cache/
|
||||||
|
.coverage
|
||||||
|
htmlcov/
|
||||||
|
|
||||||
1
.python-version
Normal file
1
.python-version
Normal file
@@ -0,0 +1 @@
|
|||||||
|
3.11
|
||||||
30
Dockerfile
Normal file
30
Dockerfile
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
# 使用 Python 3.11 作为基础镜像
|
||||||
|
FROM 172.16.102.3:30648/astral-sh/uv:python3.14-bookworm
|
||||||
|
|
||||||
|
# 设置工作目录
|
||||||
|
WORKDIR /app
|
||||||
|
|
||||||
|
# 安装系统依赖
|
||||||
|
RUN apt-get update && apt-get install -y \
|
||||||
|
gcc \
|
||||||
|
g++ \
|
||||||
|
&& rm -rf /var/lib/apt/lists/*
|
||||||
|
|
||||||
|
# 复制项目文件
|
||||||
|
COPY pyproject.toml uv.lock ./
|
||||||
|
COPY main.py ./
|
||||||
|
COPY README.md ./
|
||||||
|
|
||||||
|
# 使用 uv 安装依赖
|
||||||
|
RUN uv sync --frozen
|
||||||
|
|
||||||
|
# 暴露 Streamlit 端口
|
||||||
|
EXPOSE 8501
|
||||||
|
|
||||||
|
# 设置健康检查
|
||||||
|
HEALTHCHECK --interval=30s --timeout=10s --start-period=5s --retries=3 \
|
||||||
|
CMD python -c "import requests; requests.get('http://localhost:8501/_stcore/health')"
|
||||||
|
|
||||||
|
# 运行 Streamlit 应用
|
||||||
|
CMD ["uv", "run", "streamlit", "run", "main.py", "--server.port=8501", "--server.address=0.0.0.0"]
|
||||||
|
|
||||||
223
README.md
223
README.md
@@ -1,3 +1,222 @@
|
|||||||
# aiearthapp
|
# AI Earth App
|
||||||
|
|
||||||
earth app
|
一个基于 Streamlit 和 Leafmap 构建的交互式 COG (Cloud Optimized GeoTIFF) 地图查看器应用。支持在线加载、可视化和分析云优化的地理空间栅格数据。
|
||||||
|
|
||||||
|
## ✨ 功能特性
|
||||||
|
|
||||||
|
### 核心功能
|
||||||
|
- 🗺️ **COG 数据加载**: 支持通过 URL 加载云优化的 GeoTIFF 文件
|
||||||
|
- 📊 **波段配置**: 灵活配置多波段索引和波段计算表达式(如 NDVI、NDWI 等)
|
||||||
|
- 🎨 **颜色映射**: 内置 60+ 种颜色映射方案,支持自定义颜色映射
|
||||||
|
- 🔍 **自动定位**: 自动获取 COG 边界信息并定位地图视图
|
||||||
|
- 📏 **数值范围调整**: 可自定义数据值的重缩放范围
|
||||||
|
- 🎛️ **图层控制**: 支持多图层叠加显示,可调整透明度
|
||||||
|
- 📍 **边界框显示**: 可选显示 COG 数据的边界框
|
||||||
|
|
||||||
|
### 技术特性
|
||||||
|
- ⚡ **高性能**: 基于 Titiler 服务进行瓦片化渲染,支持大数据量
|
||||||
|
- 🌐 **Web 标准**: 使用 Web Mercator 投影,兼容标准地图服务
|
||||||
|
- 🖥️ **响应式界面**: 宽屏布局,适配不同屏幕尺寸
|
||||||
|
- 🔄 **实时更新**: 修改配置后实时更新地图显示
|
||||||
|
|
||||||
|
## 🛠️ 技术栈
|
||||||
|
|
||||||
|
- **Streamlit** (>=1.28.0): Web 应用框架,提供交互式界面
|
||||||
|
- **Leafmap** (>=0.25.0): 地理空间可视化库,基于 Folium
|
||||||
|
- **Folium** (>=0.14.0): 交互式地图库
|
||||||
|
- **Requests** (>=2.31.0): HTTP 请求库,用于与 Titiler API 通信
|
||||||
|
- **Titiler**: 外部服务,用于 COG 数据的瓦片化处理
|
||||||
|
|
||||||
|
## 📋 系统要求
|
||||||
|
|
||||||
|
- Python >= 3.11
|
||||||
|
- 网络连接(用于访问 COG 数据和 Titiler 服务)
|
||||||
|
|
||||||
|
## 🚀 快速开始
|
||||||
|
|
||||||
|
### 方式一:使用 pip 安装
|
||||||
|
|
||||||
|
1. **克隆或下载项目**
|
||||||
|
```bash
|
||||||
|
cd aiearthapp
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **安装依赖**
|
||||||
|
```bash
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
或者直接安装依赖:
|
||||||
|
```bash
|
||||||
|
pip install streamlit leafmap folium requests
|
||||||
|
```
|
||||||
|
|
||||||
|
3. **运行应用**
|
||||||
|
```bash
|
||||||
|
streamlit run main.py
|
||||||
|
```
|
||||||
|
|
||||||
|
应用将在浏览器中自动打开,默认地址为 `http://localhost:8501`
|
||||||
|
|
||||||
|
### 方式二:使用 Docker
|
||||||
|
|
||||||
|
1. **使用 Docker Compose(推荐)**
|
||||||
|
```bash
|
||||||
|
docker-compose up -d
|
||||||
|
```
|
||||||
|
|
||||||
|
2. **或使用 Docker 命令**
|
||||||
|
```bash
|
||||||
|
docker build -t aiearthapp .
|
||||||
|
docker run -p 8501:8501 aiearthapp
|
||||||
|
```
|
||||||
|
|
||||||
|
访问 `http://localhost:8501` 查看应用
|
||||||
|
|
||||||
|
## 📖 使用说明
|
||||||
|
|
||||||
|
### 基本操作流程
|
||||||
|
|
||||||
|
1. **输入 COG URL**
|
||||||
|
- 在侧边栏的"数据源"部分输入 COG 文件的 URL 地址
|
||||||
|
- 支持 HTTP/HTTPS 协议的可访问 URL
|
||||||
|
- 示例:`http://minio.minio-dev:9000/staticfiles/s2_mosaic_cog.tif`
|
||||||
|
|
||||||
|
2. **配置波段和表达式**
|
||||||
|
- **波段索引 (indexes)**: 指定要使用的波段,用逗号分隔(如:`4,8`)
|
||||||
|
- **表达式 (expression)**: 输入波段计算表达式(如:`(b2-b1)/(b2+b1)` 表示 NDVI)
|
||||||
|
- 表达式中的 `b1`, `b2` 等对应索引中的第 1、2 个波段
|
||||||
|
|
||||||
|
3. **调整数值范围**
|
||||||
|
- 设置 **最小值 (rescale min)** 和 **最大值 (rescale max)**
|
||||||
|
- 用于将数据值映射到 0-255 范围进行显示
|
||||||
|
|
||||||
|
4. **选择颜色映射**
|
||||||
|
- 从下拉列表选择预设的颜色映射方案
|
||||||
|
- 或勾选"使用自定义颜色映射"输入自定义方案名称
|
||||||
|
|
||||||
|
5. **调整显示设置**
|
||||||
|
- 调整图层透明度(0.0 - 1.0)
|
||||||
|
- 设置图层名称(用于图层控制面板)
|
||||||
|
|
||||||
|
6. **获取 COG 信息**
|
||||||
|
- 点击"🔄 获取 COG 信息"按钮手动获取数据信息
|
||||||
|
- 或勾选"自动定位到 COG 范围"实现自动获取和定位
|
||||||
|
|
||||||
|
### 常用表达式示例
|
||||||
|
|
||||||
|
- **NDVI (归一化植被指数)**: `(b2-b1)/(b2+b1)`
|
||||||
|
- **NDWI (归一化水体指数)**: `(b1-b2)/(b1+b2)`
|
||||||
|
- **简单比值**: `b2/b1`
|
||||||
|
- **差值**: `b2-b1`
|
||||||
|
- **多波段组合**: `(b1+b2+b3)/3` (平均值)
|
||||||
|
|
||||||
|
### 界面交互
|
||||||
|
|
||||||
|
- **地图操作**:
|
||||||
|
- 拖拽移动地图
|
||||||
|
- 鼠标滚轮缩放
|
||||||
|
- 使用图层控制面板切换底图和 COG 图层
|
||||||
|
|
||||||
|
- **侧边栏功能**:
|
||||||
|
- 展开/折叠各个配置区域
|
||||||
|
- 查看 COG 详细信息(边界、尺寸、波段数等)
|
||||||
|
- 查看使用说明
|
||||||
|
|
||||||
|
## ⚙️ 配置说明
|
||||||
|
|
||||||
|
### COG URL 格式
|
||||||
|
|
||||||
|
COG URL 必须是可公开访问的 HTTP/HTTPS 地址,指向有效的 Cloud Optimized GeoTIFF 文件。
|
||||||
|
|
||||||
|
### Titiler 服务
|
||||||
|
|
||||||
|
应用默认使用 `https://titiler.dev.maimaiag.com` 作为 Titiler 服务地址。如需修改,请编辑 `main.py` 中的相关 URL。
|
||||||
|
|
||||||
|
### 颜色映射选项
|
||||||
|
|
||||||
|
应用支持 Titiler 兼容的所有颜色映射方案,包括:
|
||||||
|
- 科学可视化:`viridis`, `plasma`, `inferno`, `magma`, `cividis`
|
||||||
|
- 光谱映射:`spectral`, `rdylgn`, `rdylbu`, `turbo`, `jet`
|
||||||
|
- 地形映射:`terrain`, `gist_earth`
|
||||||
|
- 以及其他 50+ 种方案
|
||||||
|
|
||||||
|
## 🐳 Docker 部署
|
||||||
|
|
||||||
|
### 使用 Docker Compose
|
||||||
|
|
||||||
|
项目包含 `docker-compose.yml` 文件,可直接使用:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
# 启动服务
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
# 查看日志
|
||||||
|
docker-compose logs -f
|
||||||
|
|
||||||
|
# 停止服务
|
||||||
|
docker-compose down
|
||||||
|
```
|
||||||
|
|
||||||
|
### 自定义配置
|
||||||
|
|
||||||
|
可以通过环境变量配置 Streamlit:
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
environment:
|
||||||
|
- STREAMLIT_SERVER_PORT=8501
|
||||||
|
- STREAMLIT_SERVER_ADDRESS=0.0.0.0
|
||||||
|
```
|
||||||
|
|
||||||
|
## 🔧 故障排查
|
||||||
|
|
||||||
|
### 常见问题
|
||||||
|
|
||||||
|
1. **无法获取 COG 信息**
|
||||||
|
- 检查 COG URL 是否正确且可访问
|
||||||
|
- 确认网络连接正常
|
||||||
|
- 检查 Titiler 服务是否可用
|
||||||
|
|
||||||
|
2. **地图不显示 COG 图层**
|
||||||
|
- 确认已获取 COG 边界信息
|
||||||
|
- 检查波段索引和表达式是否正确
|
||||||
|
- 查看浏览器控制台是否有错误信息
|
||||||
|
|
||||||
|
3. **颜色显示异常**
|
||||||
|
- 调整数值范围(rescale min/max)
|
||||||
|
- 尝试不同的颜色映射方案
|
||||||
|
- 检查表达式计算结果是否在合理范围内
|
||||||
|
|
||||||
|
4. **Docker 容器无法启动**
|
||||||
|
- 检查端口 8501 是否被占用
|
||||||
|
- 查看容器日志:`docker-compose logs`
|
||||||
|
- 确认 Docker 和 Docker Compose 版本兼容
|
||||||
|
|
||||||
|
## 📝 开发说明
|
||||||
|
|
||||||
|
### 项目结构
|
||||||
|
|
||||||
|
```
|
||||||
|
aiearthapp/
|
||||||
|
├── main.py # 主应用文件
|
||||||
|
├── pyproject.toml # 项目配置和依赖
|
||||||
|
├── Dockerfile # Docker 镜像构建文件
|
||||||
|
├── docker-compose.yml # Docker Compose 配置
|
||||||
|
└── README.md # 项目文档
|
||||||
|
```
|
||||||
|
|
||||||
|
### 依赖管理
|
||||||
|
|
||||||
|
项目使用 `pyproject.toml` 管理依赖,支持使用 `uv` 或 `pip` 安装。
|
||||||
|
|
||||||
|
## 📄 许可证
|
||||||
|
|
||||||
|
本项目采用 MIT 许可证。
|
||||||
|
|
||||||
|
## 🤝 贡献
|
||||||
|
|
||||||
|
欢迎提交 Issue 和 Pull Request!
|
||||||
|
|
||||||
|
## 📧 联系方式
|
||||||
|
|
||||||
|
如有问题或建议,请通过 Issue 反馈。
|
||||||
|
|||||||
335
main.py
Normal file
335
main.py
Normal file
@@ -0,0 +1,335 @@
|
|||||||
|
import streamlit as st
|
||||||
|
import leafmap.foliumap as leafmap
|
||||||
|
import folium
|
||||||
|
import requests
|
||||||
|
from urllib.parse import quote, quote_plus
|
||||||
|
|
||||||
|
st.set_page_config(
|
||||||
|
layout="wide",
|
||||||
|
initial_sidebar_state="expanded",
|
||||||
|
page_title="COG Viewer",
|
||||||
|
page_icon="🗺️"
|
||||||
|
|
||||||
|
)
|
||||||
|
|
||||||
|
# 隐藏右上角的菜单按钮
|
||||||
|
# st.markdown("""
|
||||||
|
# <style>
|
||||||
|
# #MainMenu {visibility: hidden;}
|
||||||
|
# header {visibility: hidden;}
|
||||||
|
# footer {visibility: hidden;}
|
||||||
|
# [data-testid="stHeader"] {visibility: hidden;}
|
||||||
|
# [data-testid="stToolbar"] {visibility: hidden;}
|
||||||
|
# </style>
|
||||||
|
# """, unsafe_allow_html=True)
|
||||||
|
|
||||||
|
# 侧边栏配置
|
||||||
|
st.sidebar.title("🗺️ COG 图层配置")
|
||||||
|
|
||||||
|
# COG 数据源配置
|
||||||
|
st.sidebar.subheader("数据源")
|
||||||
|
cog_url = st.sidebar.text_input(
|
||||||
|
"COG URL",
|
||||||
|
value="http://minio.minio-dev:9000/staticfiles/s2_mosaic_cog.tif",
|
||||||
|
help="COG 文件的 URL 地址"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 波段和表达式配置
|
||||||
|
st.sidebar.subheader("波段配置")
|
||||||
|
indexes = st.sidebar.text_input(
|
||||||
|
"波段索引 (indexes)",
|
||||||
|
value="4,8",
|
||||||
|
help="要使用的波段索引,用逗号分隔,例如: 4,8"
|
||||||
|
)
|
||||||
|
|
||||||
|
expression = st.sidebar.text_input(
|
||||||
|
"表达式 (expression)",
|
||||||
|
value="(b2-b1)/(b2+b1)",
|
||||||
|
help="波段计算表达式,例如: (b2-b1)/(b2+b1) 表示 NDVI"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 数值范围配置
|
||||||
|
st.sidebar.subheader("数值范围")
|
||||||
|
rescale_min = st.sidebar.number_input(
|
||||||
|
"最小值 (rescale min)",
|
||||||
|
value=-1.0,
|
||||||
|
step=0.1,
|
||||||
|
format="%.2f"
|
||||||
|
)
|
||||||
|
rescale_max = st.sidebar.number_input(
|
||||||
|
"最大值 (rescale max)",
|
||||||
|
value=1.0,
|
||||||
|
step=0.1,
|
||||||
|
format="%.2f"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 颜色映射配置
|
||||||
|
st.sidebar.subheader("颜色映射")
|
||||||
|
# titiler 支持的颜色映射列表(小写,使用下划线)
|
||||||
|
colormap_options = [
|
||||||
|
"rdylgn", "viridis", "plasma", "inferno", "magma", "cividis",
|
||||||
|
"spectral", "rdylbu", "rdgy", "rdbu", "piyg", "prgn", "brbg",
|
||||||
|
"puor", "oranges", "greens", "blues", "reds", "greys",
|
||||||
|
"turbo", "jet", "rainbow", "coolwarm", "seismic", "terrain",
|
||||||
|
"hot", "cool", "spring", "summer", "autumn", "winter",
|
||||||
|
"bone", "copper", "pink", "gray", "binary", "gist_earth",
|
||||||
|
"gist_rainbow", "gist_stern", "gist_heat", "gist_ncar",
|
||||||
|
"nipy_spectral", "tab10", "tab20", "set1", "set2", "set3",
|
||||||
|
"pastel1", "pastel2", "paired", "accent", "dark2", "flag",
|
||||||
|
"prism", "ocean", "gist_gray", "gist_yarg", "afmhot",
|
||||||
|
"gist_rainbow_r", "hsv", "cubehelix", "brg", "hsv_r"
|
||||||
|
]
|
||||||
|
|
||||||
|
use_custom_colormap = st.sidebar.checkbox(
|
||||||
|
"使用自定义颜色映射",
|
||||||
|
value=False,
|
||||||
|
help="勾选后可以输入自定义的颜色映射名称"
|
||||||
|
)
|
||||||
|
|
||||||
|
if use_custom_colormap:
|
||||||
|
colormap_name = st.sidebar.text_input(
|
||||||
|
"自定义颜色映射名称",
|
||||||
|
value="rdylgn",
|
||||||
|
help="输入 titiler 支持的颜色映射名称(小写,使用下划线)"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
colormap_name = st.sidebar.selectbox(
|
||||||
|
"颜色映射 (colormap)",
|
||||||
|
options=colormap_options,
|
||||||
|
index=0, # 默认使用 rdylgn
|
||||||
|
help="选择颜色映射方案(titiler 支持的格式)"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 图层显示配置
|
||||||
|
st.sidebar.subheader("显示设置")
|
||||||
|
layer_opacity = st.sidebar.slider(
|
||||||
|
"透明度 (opacity)",
|
||||||
|
min_value=0.0,
|
||||||
|
max_value=1.0,
|
||||||
|
value=0.75,
|
||||||
|
step=0.1
|
||||||
|
)
|
||||||
|
|
||||||
|
layer_name = st.sidebar.text_input(
|
||||||
|
"图层名称",
|
||||||
|
value="S2 Mosaic COG",
|
||||||
|
help="在地图图层控制中显示的图层名称"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 在侧边栏底部添加使用说明
|
||||||
|
with st.sidebar.expander("ℹ️ 使用说明", expanded=False):
|
||||||
|
st.markdown("""
|
||||||
|
**快速开始:**
|
||||||
|
1. 输入 COG URL
|
||||||
|
2. 配置波段和表达式
|
||||||
|
3. 调整颜色映射和透明度
|
||||||
|
4. 地图会自动定位
|
||||||
|
|
||||||
|
**提示:** 修改 COG URL 后会自动获取信息并重新定位
|
||||||
|
""")
|
||||||
|
|
||||||
|
# 获取 COG 信息
|
||||||
|
def get_cog_info(cog_url):
|
||||||
|
"""从 titiler 获取 COG 的完整信息"""
|
||||||
|
try:
|
||||||
|
encoded_url = quote_plus(cog_url)
|
||||||
|
info_url = f"https://titiler.dev.maimaiag.com/cog/info?url={encoded_url}"
|
||||||
|
response = requests.get(info_url, timeout=15)
|
||||||
|
if response.status_code == 200:
|
||||||
|
info = response.json()
|
||||||
|
return info
|
||||||
|
else:
|
||||||
|
st.error(f"获取 COG 信息失败: HTTP {response.status_code}")
|
||||||
|
return None
|
||||||
|
except requests.exceptions.Timeout:
|
||||||
|
st.error("请求超时,请检查网络连接或 COG URL")
|
||||||
|
return None
|
||||||
|
except Exception as e:
|
||||||
|
st.error(f"无法获取 COG 信息: {e}")
|
||||||
|
return None
|
||||||
|
|
||||||
|
# 在侧边栏添加获取信息按钮
|
||||||
|
st.sidebar.subheader("COG 信息")
|
||||||
|
auto_fit = st.sidebar.checkbox(
|
||||||
|
"自动定位到 COG 范围",
|
||||||
|
value=True,
|
||||||
|
help="勾选后会自动根据 COG 边界定位地图"
|
||||||
|
)
|
||||||
|
|
||||||
|
if st.sidebar.button("🔄 获取 COG 信息", help="点击获取 COG 的详细信息"):
|
||||||
|
with st.spinner("正在获取 COG 信息..."):
|
||||||
|
cog_info = get_cog_info(cog_url)
|
||||||
|
if cog_info:
|
||||||
|
st.session_state['cog_info'] = cog_info
|
||||||
|
# titiler 返回的是 bounds 字段,格式为 [minx, miny, maxx, maxy]
|
||||||
|
st.session_state['cog_bbox'] = cog_info.get('bounds')
|
||||||
|
st.sidebar.success("✓ COG 信息获取成功!")
|
||||||
|
else:
|
||||||
|
st.session_state['cog_info'] = None
|
||||||
|
st.session_state['cog_bbox'] = None
|
||||||
|
|
||||||
|
# 检查 COG URL 是否改变,如果改变则清除旧信息并重新获取
|
||||||
|
if 'last_cog_url' not in st.session_state:
|
||||||
|
st.session_state['last_cog_url'] = cog_url
|
||||||
|
st.session_state['cog_info'] = None
|
||||||
|
st.session_state['cog_bbox'] = None
|
||||||
|
elif st.session_state['last_cog_url'] != cog_url:
|
||||||
|
# URL 改变了,清除旧信息
|
||||||
|
st.session_state['last_cog_url'] = cog_url
|
||||||
|
st.session_state['cog_info'] = None
|
||||||
|
st.session_state['cog_bbox'] = None
|
||||||
|
# URL 改变时,如果启用了自动定位,立即获取新信息
|
||||||
|
if auto_fit and cog_url.strip():
|
||||||
|
with st.spinner("检测到 COG URL 变化,正在获取新信息..."):
|
||||||
|
cog_info = get_cog_info(cog_url)
|
||||||
|
if cog_info:
|
||||||
|
st.session_state['cog_info'] = cog_info
|
||||||
|
# titiler 返回的是 bounds 字段,格式为 [minx, miny, maxx, maxy]
|
||||||
|
st.session_state['cog_bbox'] = cog_info.get('bounds')
|
||||||
|
# st.success("✓ 已自动定位到新的 COG 范围")
|
||||||
|
else:
|
||||||
|
st.session_state['cog_bbox'] = None
|
||||||
|
|
||||||
|
# 获取 COG 边界(从 session_state 或自动获取)
|
||||||
|
if st.session_state.get('cog_bbox') is None:
|
||||||
|
if auto_fit and cog_url.strip():
|
||||||
|
with st.spinner("正在获取 COG 边界信息..."):
|
||||||
|
cog_info = get_cog_info(cog_url)
|
||||||
|
if cog_info:
|
||||||
|
st.session_state['cog_info'] = cog_info
|
||||||
|
# titiler 返回的是 bounds 字段,格式为 [minx, miny, maxx, maxy]
|
||||||
|
st.session_state['cog_bbox'] = cog_info.get('bounds')
|
||||||
|
else:
|
||||||
|
st.session_state['cog_bbox'] = None
|
||||||
|
|
||||||
|
cog_bbox = st.session_state.get('cog_bbox')
|
||||||
|
cog_info = st.session_state.get('cog_info')
|
||||||
|
|
||||||
|
# 显示 COG 信息
|
||||||
|
if cog_info:
|
||||||
|
with st.sidebar.expander("📊 COG 详细信息", expanded=False):
|
||||||
|
if 'bounds' in cog_info:
|
||||||
|
bounds = cog_info['bounds']
|
||||||
|
# bounds 格式: [minx, miny, maxx, maxy] = [最小经度, 最小纬度, 最大经度, 最大纬度]
|
||||||
|
st.write(f"**边界框 (Bounds):**")
|
||||||
|
st.write(f"- 最小经度: {bounds[0]:.6f}")
|
||||||
|
st.write(f"- 最小纬度: {bounds[1]:.6f}")
|
||||||
|
st.write(f"- 最大经度: {bounds[2]:.6f}")
|
||||||
|
st.write(f"- 最大纬度: {bounds[3]:.6f}")
|
||||||
|
# 计算中心点
|
||||||
|
center_lon = (bounds[0] + bounds[2]) / 2
|
||||||
|
center_lat = (bounds[1] + bounds[3]) / 2
|
||||||
|
st.write(f"**中心点:** ({center_lat:.6f}, {center_lon:.6f})")
|
||||||
|
|
||||||
|
if 'width' in cog_info and 'height' in cog_info:
|
||||||
|
st.write(f"**尺寸:** {cog_info['width']} × {cog_info['height']} 像素")
|
||||||
|
|
||||||
|
if 'count' in cog_info:
|
||||||
|
st.write(f"**波段数:** {cog_info['count']}")
|
||||||
|
|
||||||
|
if 'crs' in cog_info:
|
||||||
|
st.write(f"**坐标系:** {cog_info['crs']}")
|
||||||
|
|
||||||
|
if 'dtype' in cog_info:
|
||||||
|
st.write(f"**数据类型:** {cog_info['dtype']}")
|
||||||
|
|
||||||
|
# 创建地图
|
||||||
|
if cog_bbox and auto_fit:
|
||||||
|
# bounds 格式: [minx, miny, maxx, maxy] = [最小经度, 最小纬度, 最大经度, 最大纬度]
|
||||||
|
# 计算中心点
|
||||||
|
center_lat = (cog_bbox[1] + cog_bbox[3]) / 2
|
||||||
|
center_lon = (cog_bbox[0] + cog_bbox[2]) / 2
|
||||||
|
|
||||||
|
# 计算合适的缩放级别(基于边界框大小)
|
||||||
|
lat_diff = cog_bbox[3] - cog_bbox[1]
|
||||||
|
lon_diff = cog_bbox[2] - cog_bbox[0]
|
||||||
|
max_diff = max(lat_diff, lon_diff)
|
||||||
|
|
||||||
|
# 根据范围大小估算合适的缩放级别
|
||||||
|
if max_diff > 10:
|
||||||
|
initial_zoom = 6
|
||||||
|
elif max_diff > 5:
|
||||||
|
initial_zoom = 7
|
||||||
|
elif max_diff > 1:
|
||||||
|
initial_zoom = 8
|
||||||
|
elif max_diff > 0.5:
|
||||||
|
initial_zoom = 9
|
||||||
|
elif max_diff > 0.1:
|
||||||
|
initial_zoom = 10
|
||||||
|
else:
|
||||||
|
initial_zoom = 11
|
||||||
|
|
||||||
|
# 创建地图并设置初始视图
|
||||||
|
m = leafmap.Map(
|
||||||
|
center=[center_lat, center_lon],
|
||||||
|
zoom=initial_zoom,
|
||||||
|
minimap_control=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# 使用边界框来适应视图(确保 COG 完全可见)
|
||||||
|
# fit_bounds 需要 [[min_lat, min_lon], [max_lat, max_lon]] 格式
|
||||||
|
m.fit_bounds([[cog_bbox[1], cog_bbox[0]], [cog_bbox[3], cog_bbox[2]]])
|
||||||
|
|
||||||
|
# 添加边界框标记(可选)
|
||||||
|
if st.sidebar.checkbox("显示 COG 边界框", value=False):
|
||||||
|
folium.Rectangle(
|
||||||
|
bounds=[[cog_bbox[1], cog_bbox[0]], [cog_bbox[3], cog_bbox[2]]],
|
||||||
|
color="red",
|
||||||
|
weight=2,
|
||||||
|
fill=False,
|
||||||
|
popup=f"COG 边界框\n经度范围: {cog_bbox[0]:.6f} ~ {cog_bbox[2]:.6f}\n纬度范围: {cog_bbox[1]:.6f} ~ {cog_bbox[3]:.6f}"
|
||||||
|
).add_to(m)
|
||||||
|
else:
|
||||||
|
# 如果无法获取边界或未启用自动定位,使用默认视图
|
||||||
|
m = leafmap.Map(
|
||||||
|
center=[39.9042, 116.4074], # 默认北京
|
||||||
|
zoom=8,
|
||||||
|
minimap_control=True
|
||||||
|
)
|
||||||
|
|
||||||
|
# 添加底图作为参考
|
||||||
|
m.add_basemap("OpenStreetMap")
|
||||||
|
|
||||||
|
# 只有在有边界信息时才添加 COG 图层,避免无效请求
|
||||||
|
if cog_bbox and cog_url.strip():
|
||||||
|
# 构建 COG 瓦片 URL
|
||||||
|
# 对 URL 参数进行编码
|
||||||
|
# 使用 quote_plus 确保 + 号被编码为 %2B
|
||||||
|
encoded_cog_url = quote_plus(cog_url)
|
||||||
|
encoded_expression = quote_plus(expression)
|
||||||
|
|
||||||
|
# 拼接完整的瓦片 URL
|
||||||
|
cog_tiles_url = (
|
||||||
|
f"https://titiler.dev.maimaiag.com/cog/tiles/WebMercatorQuad/{{z}}/{{x}}/{{y}}.png"
|
||||||
|
f"?url={encoded_cog_url}"
|
||||||
|
f"&indexes={indexes}"
|
||||||
|
f"&expression={encoded_expression}"
|
||||||
|
f"&rescale={rescale_min},{rescale_max}"
|
||||||
|
f"&colormap_name={colormap_name}"
|
||||||
|
)
|
||||||
|
|
||||||
|
# 在侧边栏显示生成的 URL(用于调试)
|
||||||
|
if st.sidebar.checkbox("显示生成的 URL", value=False):
|
||||||
|
st.sidebar.code(cog_tiles_url, language=None)
|
||||||
|
|
||||||
|
# 添加自定义 COG 瓦片图层
|
||||||
|
folium.TileLayer(
|
||||||
|
tiles=cog_tiles_url,
|
||||||
|
attr="COG Viewer - Titiler",
|
||||||
|
name=layer_name,
|
||||||
|
overlay=True,
|
||||||
|
control=True,
|
||||||
|
opacity=layer_opacity,
|
||||||
|
show=True, # 默认显示
|
||||||
|
# error_tile_url="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
|
||||||
|
).add_to(m)
|
||||||
|
else:
|
||||||
|
# 如果没有边界信息,显示提示
|
||||||
|
if cog_url.strip() and not cog_bbox:
|
||||||
|
st.info("⚠️ 无法获取 COG 边界信息,请检查 COG URL 是否正确或点击'获取 COG 信息'按钮手动获取。")
|
||||||
|
# 添加图层控制
|
||||||
|
m.add_layer_control()
|
||||||
|
|
||||||
|
# 使用更大的地图高度,占据更多屏幕空间
|
||||||
|
# 根据常见屏幕高度,使用 900px 可以让地图占据大部分可视区域
|
||||||
|
m.to_streamlit(height=800)
|
||||||
13
pyproject.toml
Normal file
13
pyproject.toml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
[project]
|
||||||
|
name = "aiearthapp"
|
||||||
|
version = "0.1.0"
|
||||||
|
description = "基于 Streamlit 和 Leafmap 的地图应用"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.11"
|
||||||
|
dependencies = [
|
||||||
|
"streamlit>=1.28.0",
|
||||||
|
"leafmap>=0.25.0",
|
||||||
|
"folium>=0.14.0",
|
||||||
|
"setuptools>=65.0.0",
|
||||||
|
"requests>=2.31.0",
|
||||||
|
]
|
||||||
Reference in New Issue
Block a user