Files
xian_algorithm_new/app/services/qgis/map_service.py
T

146 lines
5.4 KiB
Python
Raw Normal View History

2026-06-19 17:04:03 +08:00
"""
地图生成主流程控制器。
2026-06-21 15:24:09 +08:00
模板加载 → 图层过滤 → 缩放 → 文本更新 → 导出。
2026-06-19 17:04:03 +08:00
"""
import os
import time
from qgis.core import QgsProject, QgsDataSourceUri
2026-06-20 15:50:24 +08:00
from app.config.paths import get_logger
from app.config.qgis_mappings import TABLE_RENAMES, SCHEMA_REPLACEMENTS
2026-06-19 17:04:03 +08:00
from .template_modifier import TemplateModifier
from .layer_filter import LayerFilter
from .map_exporter import MapExporter
from app.utils.map_zoom import MapZoom
logger = get_logger("qgis.service")
class MapService:
def __init__(self, config: dict):
self.config = config
def generate(self, model: dict) -> str:
2026-06-21 15:24:09 +08:00
_timing = {}
2026-06-21 13:29:19 +08:00
t_total = time.time()
2026-06-19 17:04:03 +08:00
template_path = model["path"]
2026-06-21 13:29:19 +08:00
template_name = os.path.basename(template_path)
2026-06-19 17:04:03 +08:00
project = QgsProject.instance()
2026-06-21 15:24:09 +08:00
# ── 步骤 1:加载模板 ──
2026-06-21 13:29:19 +08:00
t0 = time.time()
2026-06-21 15:24:09 +08:00
project.clear()
modifier = TemplateModifier(self.config)
actual_path = modifier.modify(template_path)
project.read(actual_path)
if actual_path != template_path:
try:
os.remove(actual_path)
except OSError:
pass
2026-06-21 13:29:19 +08:00
_timing["1.load"] = time.time() - t0
# ── 步骤 2:图层连接修正 ──
t0 = time.time()
2026-06-21 15:24:09 +08:00
self._fix_invalid_layers(project)
2026-06-21 13:29:19 +08:00
_timing["2.connections"] = time.time() - t0
2026-06-20 17:50:17 +08:00
2026-06-21 13:29:19 +08:00
# ── 步骤 3:图层过滤 ──
t0 = time.time()
2026-06-19 17:04:03 +08:00
LayerFilter().apply(project, model)
2026-06-21 13:29:19 +08:00
_timing["3.filter"] = time.time() - t0
2026-06-19 17:04:03 +08:00
2026-06-21 13:29:19 +08:00
# ── 步骤 4:地图缩放 ──
t0 = time.time()
2026-06-19 17:04:03 +08:00
layout = project.layoutManager().layoutByName(model["mapLayout"])
if layout is None:
available = [l.name() for l in project.layoutManager().layouts()]
2026-06-21 15:24:09 +08:00
raise RuntimeError(f"模板中未找到布局 '{model['mapLayout']}',可用布局:{available}")
2026-06-19 17:04:03 +08:00
map_item = layout.itemById("Map")
zoom = MapZoom(project, layout, map_item)
zoom.execute(model["zoomRule"], {
2026-06-21 15:24:09 +08:00
"X": model["centerX"], "Y": model["centerY"], "value": model["zoomValue"],
2026-06-19 17:04:03 +08:00
})
2026-06-21 13:29:19 +08:00
_timing["4.zoom"] = time.time() - t0
2026-06-19 17:04:03 +08:00
2026-06-21 13:29:19 +08:00
# ── 步骤 5:文本 + 比例尺 + 导出 ──
t0 = time.time()
2026-06-19 17:04:03 +08:00
exporter = MapExporter(self.config, layout)
exporter.update_texts(model)
exporter.update_scale_bar()
exporter.export(model["outFile"])
2026-06-21 13:29:19 +08:00
_timing["5.export"] = time.time() - t0
2026-06-19 17:04:03 +08:00
2026-06-21 13:29:19 +08:00
total = time.time() - t_total
steps = ", ".join(f"{k}={v:.1f}s" for k, v in _timing.items())
2026-06-21 15:24:09 +08:00
logger.info(f"{template_name}{total:.1f}s ({steps})")
2026-06-19 17:04:03 +08:00
return model["name"]
2026-06-21 13:29:19 +08:00
def _fix_invalid_layers(self, project: QgsProject) -> None:
2026-06-21 15:24:09 +08:00
"""只修复 TemplateModifier 处理后仍无效的图层"""
2026-06-21 13:29:19 +08:00
t0 = time.time()
2026-06-19 17:04:03 +08:00
db_config = self.config["db"]
2026-06-21 15:24:09 +08:00
actual_schema = "qgis"
2026-06-19 17:04:03 +08:00
2026-06-21 13:29:19 +08:00
fixed = 0
failed = 0
total = 0
2026-06-19 17:04:03 +08:00
for layer in project.mapLayers().values():
2026-06-21 13:29:19 +08:00
if layer.providerType() != "postgres":
2026-06-19 17:04:03 +08:00
continue
2026-06-21 13:29:19 +08:00
total += 1
if layer.isValid():
2026-06-21 15:24:09 +08:00
continue
2026-06-21 13:29:19 +08:00
try:
2026-06-20 17:50:17 +08:00
self._fix_postgres_layer(layer, db_config, actual_schema)
2026-06-21 13:29:19 +08:00
if layer.isValid():
fixed += 1
else:
failed += 1
except Exception as e:
logger.error(f" 修复图层 {layer.name()} 失败: {e}")
failed += 1
elapsed = time.time() - t0
if total:
logger.info(
f" 图层修正: 总计{total}个PG层, 跳过{total - fixed - failed}(已有效), "
f"修复{fixed}, 仍失败{failed}, 耗时{elapsed:.1f}s"
)
2026-06-19 17:04:03 +08:00
2026-06-20 17:50:17 +08:00
@staticmethod
def _fix_postgres_layer(layer, db_config, actual_schema):
"""修正单个 PostgreSQL 图层的连接参数"""
try:
uri = layer.dataProvider().uri()
uri.setConnection(
2026-06-21 15:24:09 +08:00
db_config["host"], str(db_config["port"]),
db_config["database"], db_config["username"], db_config["password"],
2026-06-20 17:50:17 +08:00
)
uri_str = uri.uri()
for old_schema in SCHEMA_REPLACEMENTS:
if f'table="{old_schema}".' in uri_str:
2026-06-21 15:24:09 +08:00
uri_str = uri_str.replace(f'table="{old_schema}".', f'table="{actual_schema}".')
2026-06-20 17:50:17 +08:00
uri = QgsDataSourceUri(uri_str)
break
uri_str = uri.uri()
for old_name, new_name in TABLE_RENAMES.items():
full_old = f'table="{actual_schema}"."{old_name}"'
full_new = f'table="{actual_schema}"."{new_name}"'
if full_old in uri_str:
uri_str = uri_str.replace(full_old, full_new)
uri = QgsDataSourceUri(uri_str)
uri_str = uri.uri()
if " srid=0 " in uri_str:
uri_str = uri_str.replace(" srid=0 ", " srid=4326 ")
uri = QgsDataSourceUri(uri_str)
layer.setDataSource(uri.uri(), layer.name(), "postgres")
if layer.isValid():
2026-06-21 13:29:19 +08:00
logger.debug(f" 图层 {layer.name()} 连接更新成功")
2026-06-20 17:50:17 +08:00
else:
2026-06-21 13:29:19 +08:00
logger.error(f" 图层 {layer.name()} 更新后仍无效")
2026-06-20 17:50:17 +08:00
except Exception as e:
2026-06-21 15:24:09 +08:00
logger.error(f" 更新图层 {layer.name()} 连接失败: {e}")