扫描QGIS模板
This commit is contained in:
+56
-20
@@ -154,23 +154,51 @@ def _extract_center_from_condition(event_type: str, condition: dict) -> tuple:
|
|||||||
# 构建 QGIS 服务配置字典
|
# 构建 QGIS 服务配置字典
|
||||||
# ============================================================
|
# ============================================================
|
||||||
|
|
||||||
def _build_qgis_config(batch_folder: str) -> dict:
|
def _check_docker_running() -> bool:
|
||||||
"""构建 QGIS 服务配置(含批次输出目录)"""
|
"""检测 Docker 容器是否在运行"""
|
||||||
from app.services.qgis.qgis_env import (
|
from app.services.qgis.qgis_env import get_docker_container
|
||||||
get_docker_container, get_host_file_store, get_container_file_store,
|
|
||||||
get_docker_project_dir,
|
|
||||||
)
|
|
||||||
gpkg_dir = get_gpkg_dir()
|
|
||||||
|
|
||||||
# Docker 模式:config 中的路径必须是容器内路径(模板修改器在容器内运行)
|
|
||||||
try:
|
try:
|
||||||
result = subprocess.run(
|
result = subprocess.run(
|
||||||
["docker", "inspect", "--format={{.State.Running}}", get_docker_container()],
|
["docker", "inspect", "--format={{.State.Running}}", get_docker_container()],
|
||||||
capture_output=True, text=True, timeout=5,
|
capture_output=True, text=True, timeout=5,
|
||||||
)
|
)
|
||||||
is_docker = result.stdout.strip() == "true"
|
return result.stdout.strip() == "true"
|
||||||
except Exception:
|
except Exception:
|
||||||
is_docker = False
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def _list_container_templates(container: str, event_type: str) -> list:
|
||||||
|
"""Docker 模式下:扫描容器内模板目录,返回模板文件名列表"""
|
||||||
|
from app.services.qgis.qgis_env import get_docker_container
|
||||||
|
container_tpl_dir = getattr(settings, "QGIS_DOCKER_TEMPLATE_DIR", "") or "/data/template"
|
||||||
|
container_path = f"{container_tpl_dir.rstrip('/')}/{event_type}"
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["docker", "exec", container, "ls", container_path],
|
||||||
|
capture_output=True, text=True, timeout=10,
|
||||||
|
)
|
||||||
|
if result.returncode != 0:
|
||||||
|
logger.error(f"[Docker] 列出容器模板失败: {result.stderr.strip()}")
|
||||||
|
return []
|
||||||
|
files = [
|
||||||
|
f.strip() for f in result.stdout.splitlines()
|
||||||
|
if f.strip().endswith(".qgz") and not f.strip().startswith("tmp")
|
||||||
|
]
|
||||||
|
logger.info(f"[Docker] 容器内模板扫描: {container_path} → {len(files)} 个模板")
|
||||||
|
return sorted(files)
|
||||||
|
except Exception as e:
|
||||||
|
logger.error(f"[Docker] 列出容器模板异常: {e}")
|
||||||
|
return []
|
||||||
|
|
||||||
|
|
||||||
|
def _build_qgis_config(batch_folder: str) -> dict:
|
||||||
|
"""构建 QGIS 服务配置(含批次输出目录和 Docker 模式标志)"""
|
||||||
|
from app.services.qgis.qgis_env import (
|
||||||
|
get_docker_container, get_host_file_store, get_container_file_store,
|
||||||
|
get_docker_project_dir,
|
||||||
|
)
|
||||||
|
gpkg_dir = get_gpkg_dir()
|
||||||
|
is_docker = _check_docker_running()
|
||||||
|
|
||||||
if is_docker:
|
if is_docker:
|
||||||
# GPKG 目录:优先使用容器内本地路径(预拷贝后绕过 WSL2 9P)
|
# GPKG 目录:优先使用容器内本地路径(预拷贝后绕过 WSL2 9P)
|
||||||
@@ -206,6 +234,7 @@ def _build_qgis_config(batch_folder: str) -> dict:
|
|||||||
},
|
},
|
||||||
"static_layers": build_static_layers_config(gpkg_dir),
|
"static_layers": build_static_layers_config(gpkg_dir),
|
||||||
"batch_folder": batch_folder,
|
"batch_folder": batch_folder,
|
||||||
|
"is_docker": is_docker,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@@ -242,15 +271,22 @@ def _background_export(inference_id: int) -> None:
|
|||||||
config = _build_qgis_config(batch_folder)
|
config = _build_qgis_config(batch_folder)
|
||||||
|
|
||||||
# 2. 扫描模板
|
# 2. 扫描模板
|
||||||
template_base = os.path.join(
|
is_docker = config.get("is_docker", False)
|
||||||
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
|
if is_docker:
|
||||||
"app", "data", "template"
|
container_tpl = getattr(settings, "QGIS_DOCKER_TEMPLATE_DIR", "") or "/data/template"
|
||||||
)
|
template_dir = f"{container_tpl.rstrip('/')}/{event_type}"
|
||||||
template_dir = os.path.join(template_base, event_type)
|
template_files = _list_container_templates(get_docker_container(), event_type)
|
||||||
template_files = sorted([
|
else:
|
||||||
f for f in os.listdir(template_dir)
|
tpl_subdir = getattr(settings, "QGIS_HOST_TEMPLATE_SUBDIR", "app/data/template")
|
||||||
if f.endswith(".qgz") and not f.startswith("tmp")
|
template_base = os.path.join(
|
||||||
])
|
os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))),
|
||||||
|
*tpl_subdir.replace("\\", "/").split("/")
|
||||||
|
)
|
||||||
|
template_dir = os.path.join(template_base, event_type)
|
||||||
|
template_files = sorted([
|
||||||
|
f for f in os.listdir(template_dir)
|
||||||
|
if f.endswith(".qgz") and not f.startswith("tmp")
|
||||||
|
])
|
||||||
priority_keywords = getattr(settings, "QGIS_PRIORITY_TEMPLATES", [])
|
priority_keywords = getattr(settings, "QGIS_PRIORITY_TEMPLATES", [])
|
||||||
if priority_keywords:
|
if priority_keywords:
|
||||||
def _priority_order(name: str) -> tuple:
|
def _priority_order(name: str) -> tuple:
|
||||||
|
|||||||
@@ -172,6 +172,8 @@ def map_template_to_container(host_path: str) -> str:
|
|||||||
|
|
||||||
主机: F:/project/xian/xian_algorithm_new/app/data/template/rainfall/xxx.qgz
|
主机: F:/project/xian/xian_algorithm_new/app/data/template/rainfall/xxx.qgz
|
||||||
容器: /data/template/rainfall/xxx.qgz
|
容器: /data/template/rainfall/xxx.qgz
|
||||||
|
|
||||||
|
已为容器路径时直接返回(幂等)。
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
from config import settings
|
from config import settings
|
||||||
@@ -179,16 +181,21 @@ def map_template_to_container(host_path: str) -> str:
|
|||||||
except Exception:
|
except Exception:
|
||||||
container_tpl = ""
|
container_tpl = ""
|
||||||
|
|
||||||
|
# 已经是容器路径,直接返回
|
||||||
|
normalized = host_path.replace("\\", "/")
|
||||||
|
if container_tpl and normalized.startswith(container_tpl.rstrip("/") + "/"):
|
||||||
|
return normalized
|
||||||
|
|
||||||
if not container_tpl:
|
if not container_tpl:
|
||||||
# fallback: 用通用映射(走 9P,慢)
|
# fallback: 用通用映射(走 9P,慢)
|
||||||
return map_host_to_container(host_path)
|
return map_host_to_container(host_path)
|
||||||
|
|
||||||
normalized = host_path.replace("\\", "/").lower()
|
normalized_lower = normalized.lower()
|
||||||
# 找 "app/data/template/" 后面的部分
|
# 找模板子目录后面的相对路径(主机端 marker 从配置读取)
|
||||||
marker = "app/data/template/"
|
marker = getattr(settings, "QGIS_HOST_TEMPLATE_SUBDIR", "app/data/template").rstrip("/") + "/"
|
||||||
idx = normalized.find(marker)
|
idx = normalized_lower.find(marker.lower())
|
||||||
if idx >= 0:
|
if idx >= 0:
|
||||||
relative = host_path.replace("\\", "/")[idx + len(marker):]
|
relative = normalized[idx + len(marker):]
|
||||||
return f"{container_tpl.rstrip('/')}/{relative}"
|
return f"{container_tpl.rstrip('/')}/{relative}"
|
||||||
|
|
||||||
return map_host_to_container(host_path)
|
return map_host_to_container(host_path)
|
||||||
|
|||||||
@@ -12,6 +12,8 @@ PREDICT_PROBABILITY_THRESHOLD = 50
|
|||||||
QGIS_GPKG_DIR = "app/data/gpkg"
|
QGIS_GPKG_DIR = "app/data/gpkg"
|
||||||
# 容器内 GPKG 本地路径
|
# 容器内 GPKG 本地路径
|
||||||
QGIS_DOCKER_GPKG_DIR = "/data/gpkg"
|
QGIS_DOCKER_GPKG_DIR = "/data/gpkg"
|
||||||
|
# 模板目录(相对于项目根,用于路径映射时的 marker 匹配)
|
||||||
|
QGIS_HOST_TEMPLATE_SUBDIR = "app/data/template"
|
||||||
# 容器内模板本地路径
|
# 容器内模板本地路径
|
||||||
QGIS_DOCKER_TEMPLATE_DIR = "/data/template"
|
QGIS_DOCKER_TEMPLATE_DIR = "/data/template"
|
||||||
# 专题图输出子目录
|
# 专题图输出子目录
|
||||||
|
|||||||
Reference in New Issue
Block a user