fix: 修复 pip wheel GDAL 的 proj.db 未被找到的 bug

根因: pip 安装的 GDAL ≥3.5 把 proj.db 放在
  {site-packages}/osgeo/data/proj/proj.db
而之前的代码将其加入 prefixes 列表后,搜索循环只查找
  {prefix}/Library/share/proj/ 子目录,
导致 site-packages 中的 v6+ proj.db 被漏掉。

修复:
  1. 新增独立搜索步骤:直接扫描 site-packages 下
     osgeo/data/proj/ 和 osgeo/proj_data/ 目录
  2. 找到 v6+ 版本后直接设为 proj_lib,
     不再经过前缀子目录搜索(优先级最高)
  3. 同时扫描 site-packages/fiona/proj_data/,
     找到旧版时打印明确路径警告,
     确认 PROJ_DATA 已覆盖该旧版
  4. 仅 site-packages 未找到时回退到前缀搜索
This commit is contained in:
duxin
2026-07-07 16:47:02 +08:00
parent 721370f3e5
commit 6b6e593fa5

View File

@ -172,39 +172,62 @@ def _find_and_set_gdal_env():
pass
prefixes.extend(_extra_prefixes)
# 6) pip wheel GDAL: {site-packages}/osgeo/data/proj/proj.db
# (conda-forge GDAL wheel on Windows 有时把 proj.db 放这里)
# 6) pip wheel GDAL / Fiona: {site-packages}/*/proj.db 直接搜索
# pip 安装的 GDAL ≥3.5 把 proj.db 放在 site-packages/osgeo/data/proj/
# Fiona 打包的旧版 proj.db 在 site-packages/fiona/proj_data/
# 优先使用 GDAL 的 v6+,忽略 Fiona 的 v2
_site_proj = None # site-packages 中找到的 v6+ proj.db
_site_gdal = None
_fiona_v2_path = None # 记录 Fiona 的旧版供警告
try:
import site as _site
for _sp in _site.getsitepackages():
pip_proj = os.path.join(_sp, "osgeo", "data", "proj")
if os.path.isfile(os.path.join(pip_proj, "proj.db")):
pip_gdal = os.path.join(_sp, "osgeo", "data", "gdal")
if _sp not in prefixes:
prefixes.append(_sp)
# GDAL wheel: osgeo/data/proj/proj.db
for _sub in ("osgeo/data/proj", "osgeo\\data\\proj",
"osgeo/proj_data", "osgeo\\proj_data"):
_db = os.path.join(_sp, _sub, "proj.db")
if os.path.isfile(_db):
_ver = _check_proj_db_version(_db)
if _ver >= 6 and not _site_proj:
_site_proj = os.path.join(_sp, _sub)
_gd = os.path.join(_sp, "osgeo", "data", "gdal")
if os.path.isdir(_gd):
_site_gdal = _gd
# Fiona 打包的旧版 → 记录供警告
for _sub in ("fiona/proj_data", "fiona\\proj_data"):
_fdb = os.path.join(_sp, _sub, "proj.db")
if os.path.isfile(_fdb) and not _fiona_v2_path:
_fiona_v2_path = _fdb
except Exception:
pass
proj_lib = None
gdal_data = None
for prefix in prefixes:
if not proj_lib:
# 先尝试标准子目录(快速路径)
for sub in ("Library/share/proj", "share/proj",
"Library\\share\\proj", "share\\proj"):
db_path = os.path.join(prefix, sub, "proj.db")
if os.path.isfile(db_path) and _check_proj_db_version(db_path) >= 6:
proj_lib = os.path.join(prefix, sub)
break
# 标准路径未找到 → 递归搜索(仅对 conda 环境前缀)
if not proj_lib and (
os.path.basename(prefix) not in ("", "share", "bin", "Scripts")
):
proj_lib = _find_proj_in_prefix(prefix)
if not gdal_data:
gdal_data = _find_gdal_in_prefix(prefix)
if proj_lib and gdal_data:
break
# 直接使用 site-packages 中找到的 v6+ proj.db优先级最高
# 它和当前 Python 进程中的 GDAL/PROJ 库版本匹配)
if _site_proj:
proj_lib = _site_proj
gdal_data = _site_gdal if _site_gdal else None
else:
proj_lib = None
gdal_data = None
# site-packages 没找到 → 回退到前缀搜索
if not proj_lib:
for prefix in prefixes:
if not proj_lib:
for sub in ("Library/share/proj", "share/proj",
"Library\\share\\proj", "share\\proj"):
db_path = os.path.join(prefix, sub, "proj.db")
if os.path.isfile(db_path) and _check_proj_db_version(db_path) >= 6:
proj_lib = os.path.join(prefix, sub)
break
if not proj_lib and (
os.path.basename(prefix) not in ("", "share", "bin", "Scripts")
):
proj_lib = _find_proj_in_prefix(prefix)
if not gdal_data:
gdal_data = _find_gdal_in_prefix(prefix)
if proj_lib and gdal_data:
break
# ── 应用环境变量 ──
if proj_lib:
@ -232,13 +255,16 @@ def _find_and_set_gdal_env():
# ── 屏蔽 Fiona 内部旧版 PROJ 路径 ──
# Fiona wheels 捆绑了古老的 proj.db (VERSION.MINOR=2)
# 即使设置了 PROJ_DATA 仍然可能被某些旧版 Fiona 忽略。
# 以下环境变量作为最后防线强制覆盖。
if proj_lib:
# GDAL ≥3.5 同时检查 PROJ_DATA 和 PROJ_LIB
os.environ["PROJ_NETWORK"] = "OFF" # 禁用 PROJ 网络下载(避免意外行为)
# 记录此信息供诊断
if "PROJ_DATA" in os.environ:
print("[ENV] 已屏蔽 Fiona 内部旧版 PROJ 数据库,全进程统一使用上述 proj.db")
if _fiona_v2_path:
print(f"[ENV] ⚠ 检测到 Fiona 自带的旧版 proj.db (v2) 位于:")
print(f" {_fiona_v2_path}")
print(f" PROJ_DATA 已指向 v6+ 版本,此旧版将被忽略")
if proj_lib:
print(f" 确认全进程 PROJ 来源: {proj_lib}")
_find_and_set_gdal_env()