Files
xian_algorithm_new/app/services/qgis/map_service.py
T
2026-06-19 17:04:03 +08:00

135 lines
4.7 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""
地图生成主流程控制器。
协调模板加载、图层过滤、缩放、文本更新、导出。
"""
import os
import time
from qgis.core import QgsProject, QgsDataSourceUri
from .template_cache import TemplateCache
from .template_modifier import TemplateModifier
from .layer_filter import LayerFilter
from .map_exporter import MapExporter
from app.utils.map_zoom import MapZoom
from app.config.paths import get_logger
logger = get_logger("qgis.service")
# 全局模板缓存(跨请求复用)
template_cache = TemplateCache()
class MapService:
def __init__(self, config: dict):
self.config = config
def generate(self, model: dict) -> str:
"""
执行完整的地图生成流程。
Args:
model: 包含地图参数的字典
Returns:
地图名称
"""
t_start = time.time()
template_path = model["path"]
project = QgsProject.instance()
is_cache_hit = template_cache.is_loaded(template_path)
# 加载/恢复模板
if is_cache_hit:
logger.info(f"[缓存命中] {os.path.basename(template_path)}")
project, texts, extent = template_cache.restore_template(template_path)
template_cache.reset_project_state(project, texts, extent)
else:
logger.info(f"[首次加载] {os.path.basename(template_path)}")
modifier = TemplateModifier(self.config)
actual_path = modifier.modify(template_path)
template_cache.load_template(actual_path, layout_name=model.get("mapLayout", "A4"))
if actual_path != template_path:
try:
os.remove(actual_path)
except OSError:
pass
# 更新 PostgreSQL 图层连接(仅首次加载)
if not is_cache_hit:
self._update_db_connections(project)
# 图层过滤
LayerFilter().apply(project, model)
# 地图缩放
layout = project.layoutManager().layoutByName(model["mapLayout"])
if layout is None:
available = [l.name() for l in project.layoutManager().layouts()]
raise RuntimeError(
f"模板中未找到布局 '{model['mapLayout']}',可用布局:{available}"
)
map_item = layout.itemById("Map")
zoom = MapZoom(project, layout, map_item)
zoom.execute(model["zoomRule"], {
"X": model["centerX"],
"Y": model["centerY"],
"value": model["zoomValue"],
})
# 文本更新 + 比例尺 + 导出
exporter = MapExporter(self.config, layout)
exporter.update_texts(model)
exporter.update_scale_bar()
exporter.export(model["outFile"])
elapsed = time.time() - t_start
logger.info(
f"{'[缓存命中]' if is_cache_hit else '[首次加载]'} "
f"导出完成: {model['name']},耗时 {elapsed:.1f}s"
)
return model["name"]
def _update_db_connections(self, project: QgsProject) -> None:
"""更新所有 PostgreSQL 图层的数据库连接参数"""
db_config = self.config["db"]
override = self.config.get("template_override", {})
actual_schema = override.get("actual", {}).get("schema", "qgis")
static_count = 0
for layer in project.mapLayers().values():
if layer.providerType() == "ogr":
static_count += 1
continue
if layer.providerType() != "postgres":
continue
try:
uri = layer.dataProvider().uri()
uri.setConnection(
db_config["host"],
str(db_config["port"]),
db_config["database"],
db_config["username"],
db_config["password"],
)
# 更新 schematable="base"."xxx" → table="qgis"."xxx"
old_uri = uri.uri()
if f'table="{actual_schema}".' not in old_uri:
new_uri = old_uri.replace('table="base".', f'table="{actual_schema}".')
if new_uri != old_uri:
uri = QgsDataSourceUri(new_uri)
layer.setDataSource(uri.uri(), layer.name(), "postgres")
if layer.isValid():
logger.info(f"图层 {layer.name()} 连接更新成功")
else:
logger.error(f"图层 {layer.name()} 更新后仍无效")
except Exception as e:
logger.error(f"更新图层 {layer.name()} 连接失败: {e}")
if static_count:
logger.info(f"静态底图已本地化: {static_count} 个 GPKG 图层跳过远程连接")