205 lines
5.6 KiB
Markdown
205 lines
5.6 KiB
Markdown
# PROJ 数据库版本冲突问题修复(2026-03-05)
|
||
|
||
## 问题描述
|
||
|
||
在扫描 D-InSAR 结果时出现 PROJ 错误:
|
||
|
||
```
|
||
PROJ: proj_identify: C:\Program Files\PostgreSQL\17\share\contrib\postgis-3.6\proj\proj.db
|
||
contains DATABASE.LAYOUT.VERSION.MINOR = 2 whereas a number >= 3 is expected.
|
||
It comes from another PROJ installation.
|
||
```
|
||
|
||
## 根本原因
|
||
|
||
1. **PROJ 版本冲突**:
|
||
- PostgreSQL 17 自带的 PostGIS 3.6 包含旧版 PROJ 数据库(版本 2)
|
||
- GDAL/rasterio 期望 PROJ 数据库版本 >= 3
|
||
- 系统在坐标转换时(`Transformer.from_crs`)触发此警告
|
||
|
||
2. **环境变量优先级**:
|
||
- Windows 系统可能将 PostgreSQL 的 PROJ 路径添加到 PATH
|
||
- GDAL 默认搜索 PATH 中的 PROJ 数据库
|
||
- 找到了 PostgreSQL 的旧版本而非 GDAL 自带的新版本
|
||
|
||
## 影响
|
||
|
||
- 🟡 **中等**:不会导致程序崩溃,但会产生大量警告日志
|
||
- 坐标转换仍然可以工作(PROJ 会回退到兼容模式)
|
||
- 日志污染,影响问题排查
|
||
|
||
## 解决方案
|
||
|
||
### 方案 1:设置 PROJ_LIB 环境变量(已实施)
|
||
|
||
**文件**:`run_backend.py`
|
||
|
||
**修改内容**:
|
||
```python
|
||
def main() -> None:
|
||
# Load environment variables
|
||
load_dotenv()
|
||
|
||
# Fix PROJ database version conflict
|
||
# PostgreSQL's PROJ database is outdated, use GDAL's bundled PROJ data
|
||
# This prevents "DATABASE.LAYOUT.VERSION.MINOR = 2 whereas >= 3 is expected" error
|
||
if "PROJ_LIB" not in os.environ:
|
||
# Try to find GDAL's PROJ data directory
|
||
try:
|
||
from osgeo import gdal
|
||
gdal_data = gdal.GetConfigOption("GDAL_DATA")
|
||
if gdal_data:
|
||
proj_lib = os.path.join(os.path.dirname(gdal_data), "proj")
|
||
if os.path.exists(proj_lib):
|
||
os.environ["PROJ_LIB"] = proj_lib
|
||
print(f"[*] Set PROJ_LIB to: {proj_lib}")
|
||
except Exception as e:
|
||
print(f"[WARN] Could not auto-configure PROJ_LIB: {e}")
|
||
|
||
port = int(os.getenv("PORT", 8000))
|
||
# ... rest of the code
|
||
```
|
||
|
||
**工作原理**:
|
||
1. 在启动时检查 `PROJ_LIB` 环境变量是否已设置
|
||
2. 如果未设置,尝试从 GDAL 配置中获取 PROJ 数据目录
|
||
3. 设置 `PROJ_LIB` 指向 GDAL 自带的 PROJ 数据库
|
||
|
||
### 方案 2:手动设置环境变量(备选)
|
||
|
||
如果方案 1 不生效,可以在 `.env` 文件中手动设置:
|
||
|
||
```bash
|
||
# 找到 GDAL 的 PROJ 数据目录
|
||
python -c "from osgeo import gdal; import os; print(os.path.join(os.path.dirname(gdal.GetConfigOption('GDAL_DATA')), 'proj'))"
|
||
|
||
# 将输出路径添加到 .env
|
||
PROJ_LIB=C:\path\to\gdal\proj
|
||
```
|
||
|
||
或者在 PowerShell 启动脚本中设置:
|
||
|
||
```powershell
|
||
# scripts/start_app.ps1
|
||
$env:PROJ_LIB = "C:\path\to\gdal\proj"
|
||
```
|
||
|
||
### 方案 3:抑制 GDAL 警告(不推荐)
|
||
|
||
如果只是想隐藏警告而不解决根本问题:
|
||
|
||
```python
|
||
# 在 run_backend.py 中添加
|
||
from osgeo import gdal
|
||
gdal.SetConfigOption('CPL_LOG', 'OFF') # 关闭所有 GDAL 日志
|
||
```
|
||
|
||
**不推荐原因**:
|
||
- 会隐藏所有 GDAL 错误和警告
|
||
- 不解决根本问题
|
||
- 可能掩盖其他重要错误
|
||
|
||
## 验证
|
||
|
||
### 1. 检查 PROJ_LIB 是否生效
|
||
|
||
启动后端后,查看日志:
|
||
```
|
||
[*] Set PROJ_LIB to: C:\Users\...\site-packages\osgeo\data\proj
|
||
```
|
||
|
||
### 2. 运行扫描任务
|
||
|
||
在前端触发 D-InSAR 结果扫描,观察日志:
|
||
- ✅ 应该不再出现 PROJ 警告
|
||
- ✅ Footprint 提取正常完成
|
||
|
||
### 3. 验证坐标转换
|
||
|
||
```python
|
||
from pyproj import Transformer
|
||
transformer = Transformer.from_crs("EPSG:32650", "EPSG:4326", always_xy=True)
|
||
x, y = transformer.transform(500000, 3000000)
|
||
print(f"Transformed: {x}, {y}")
|
||
# 应该不产生警告
|
||
```
|
||
|
||
## 前端锁定问题
|
||
|
||
**问题**:扫描任务运行时前端没有锁定
|
||
|
||
**可能原因**:
|
||
1. 扫描任务执行太快(< 3 秒),前端轮询间隔(3 秒)来不及捕获
|
||
2. 任务状态更新有延迟
|
||
|
||
**解决方案**:
|
||
|
||
### 方案 A:增加最小锁定时间(推荐)
|
||
|
||
在扫描任务开始时立即锁定,结束时延迟解锁:
|
||
|
||
```python
|
||
# job_handlers.py
|
||
async def _handle_scan_dinsar(job: SystemJobORM) -> None:
|
||
if not job.task_id:
|
||
raise ValueError("SCAN_DINSAR requires task_id for progress tracking.")
|
||
|
||
await task_service.start_task(job.task_id, message="正在扫描 D-InSAR 结果...")
|
||
|
||
# 确保任务至少运行 2 秒,让前端有时间捕获
|
||
start_time = time.time()
|
||
|
||
# ... 执行扫描 ...
|
||
|
||
# 确保最小执行时间
|
||
elapsed = time.time() - start_time
|
||
if elapsed < 2.0:
|
||
await asyncio.sleep(2.0 - elapsed)
|
||
|
||
await task_service.complete_task(job.task_id, message="扫描完成")
|
||
```
|
||
|
||
### 方案 B:前端立即锁定(更好)
|
||
|
||
在提交扫描任务后立即锁定前端,不等待轮询:
|
||
|
||
```javascript
|
||
// DataManagementPanel.jsx
|
||
const handleScanDinsarResults = async () => {
|
||
try {
|
||
// 立即锁定前端
|
||
onJobQueued?.(null, 'SCAN_DINSAR');
|
||
|
||
const response = await scanDinsarResults(selectedDirs);
|
||
const taskId = response.data.task_id;
|
||
|
||
// 更新任务 ID
|
||
onJobQueued?.(taskId, 'SCAN_DINSAR');
|
||
|
||
addLog('info', `D-InSAR 结果扫描任务已提交: ${taskId}`);
|
||
} catch (error) {
|
||
// 解锁前端
|
||
setIsGlobalLocked(false);
|
||
addLog('error', `扫描失败: ${error.message}`);
|
||
}
|
||
};
|
||
```
|
||
|
||
## 状态
|
||
|
||
- ✅ PROJ 环境变量修复已实施
|
||
- ⏳ 等待测试验证
|
||
- ⏳ 前端锁定问题待确认是否需要修复
|
||
|
||
## 下一步
|
||
|
||
1. 重启后端服务
|
||
2. 运行 D-InSAR 结果扫描
|
||
3. 观察日志,确认 PROJ 警告消失
|
||
4. 如果前端仍未锁定,实施方案 B
|
||
|
||
## 相关文档
|
||
|
||
- `SECURITY_FIX_PROGRESS.md` - 安全修复进度
|
||
- `HOTFIX_NGINX_BOM_2026-03-04.md` - Nginx BOM 修复
|