QGIS提升速度
This commit is contained in:
@@ -14,7 +14,6 @@ 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")
|
||||
|
||||
@@ -29,43 +28,58 @@ class MapService:
|
||||
def generate(self, model: dict) -> str:
|
||||
"""
|
||||
执行完整的地图生成流程。
|
||||
|
||||
Args:
|
||||
model: 包含地图参数的字典
|
||||
|
||||
Returns:
|
||||
地图名称
|
||||
"""
|
||||
t_start = time.time()
|
||||
_timing = {} # {step: seconds}
|
||||
t_total = time.time()
|
||||
|
||||
template_path = model["path"]
|
||||
template_name = os.path.basename(template_path)
|
||||
project = QgsProject.instance()
|
||||
is_cache_hit = template_cache.is_loaded(template_path)
|
||||
|
||||
# 加载/恢复模板
|
||||
# ── 步骤 1:加载/恢复模板 ──
|
||||
t0 = time.time()
|
||||
if is_cache_hit:
|
||||
logger.info(f"[缓存命中] {os.path.basename(template_path)}")
|
||||
logger.info(f"[缓存命中] {template_name}")
|
||||
project, texts, extent = template_cache.restore_template(template_path)
|
||||
template_cache.reset_project_state(project, texts, extent)
|
||||
# FlagDontResolveLayers 跳过了数据库连接,手动触发 PostgreSQL 图层解析
|
||||
self._resolve_postgres_layers(project)
|
||||
else:
|
||||
logger.info(f"[首次加载] {os.path.basename(template_path)}")
|
||||
# 直接读原始模板,不做文件级修改(避免 ZIP 兼容性问题)
|
||||
project.read(template_path)
|
||||
logger.info(f"[首次加载] {template_name}")
|
||||
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
|
||||
_timing["1.load"] = time.time() - t0
|
||||
|
||||
# 更新图层连接 + GPKG/SRID/表名修正(仅首次加载)
|
||||
# ── 步骤 2:图层连接修正 ──
|
||||
t0 = time.time()
|
||||
if not is_cache_hit:
|
||||
self._update_db_connections(project)
|
||||
self._fix_invalid_layers(project)
|
||||
_timing["2.connections"] = time.time() - t0
|
||||
|
||||
# 图层过滤
|
||||
# ★ 首次加载完成后再保存缓存(确保图层已修正)
|
||||
if not is_cache_hit:
|
||||
template_cache.save_current(template_path, model.get("mapLayout", "A3"))
|
||||
|
||||
# ── 步骤 3:图层过滤 ──
|
||||
t0 = time.time()
|
||||
LayerFilter().apply(project, model)
|
||||
_timing["3.filter"] = time.time() - t0
|
||||
|
||||
# 地图缩放
|
||||
# ── 步骤 4:地图缩放 ──
|
||||
t0 = time.time()
|
||||
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"], {
|
||||
@@ -73,61 +87,69 @@ class MapService:
|
||||
"Y": model["centerY"],
|
||||
"value": model["zoomValue"],
|
||||
})
|
||||
_timing["4.zoom"] = time.time() - t0
|
||||
|
||||
# 文本更新 + 比例尺 + 导出
|
||||
# ── 步骤 5:文本 + 比例尺 + 导出 ──
|
||||
t0 = time.time()
|
||||
exporter = MapExporter(self.config, layout)
|
||||
exporter.update_texts(model)
|
||||
exporter.update_scale_bar()
|
||||
exporter.export(model["outFile"])
|
||||
_timing["5.export"] = time.time() - t0
|
||||
|
||||
elapsed = time.time() - t_start
|
||||
total = time.time() - t_total
|
||||
steps = ", ".join(f"{k}={v:.1f}s" for k, v in _timing.items())
|
||||
logger.info(
|
||||
f"{'[缓存命中]' if is_cache_hit else '[首次加载]'} "
|
||||
f"导出完成: {model['name']},耗时 {elapsed:.1f}s"
|
||||
f"{template_name} → {total:.1f}s ({steps})"
|
||||
)
|
||||
return model["name"]
|
||||
|
||||
def _update_db_connections(self, project: QgsProject) -> None:
|
||||
"""更新图层连接 + SRID修正 + GPKG静态层替换"""
|
||||
def _fix_invalid_layers(self, project: QgsProject) -> None:
|
||||
"""只修复 TemplateModifier 处理后仍无效的图层(TemplateModifier 已修正大部分连接)"""
|
||||
t0 = time.time()
|
||||
db_config = self.config["db"]
|
||||
override = self.config.get("template_override", {})
|
||||
actual_schema = override.get("actual", {}).get("schema", "qgis")
|
||||
static_config = self.config.get("static_layers", {})
|
||||
static_enabled = static_config.get("enabled", False)
|
||||
gpkg_dir = static_config.get("gpkg_dir", "")
|
||||
static_layers_map = static_config.get("layers", {})
|
||||
|
||||
static_count = 0
|
||||
|
||||
fixed = 0
|
||||
failed = 0
|
||||
total = 0
|
||||
for layer in project.mapLayers().values():
|
||||
provider = layer.providerType()
|
||||
|
||||
# GPKG 静态层替换:postgres 表 → 本地 GPKG 文件
|
||||
if provider == "postgres" and static_enabled:
|
||||
uri_str = layer.dataProvider().uri().uri()
|
||||
for name, info in static_layers_map.items():
|
||||
table_key = info["table"]
|
||||
schema, table = table_key.split(".", 1)
|
||||
if f'table="{schema}"."{table}"' in uri_str:
|
||||
gpkg_path = os.path.join(gpkg_dir, info["file"]).replace("\\", "/")
|
||||
layer.setDataSource(gpkg_path, name, "ogr")
|
||||
static_count += 1
|
||||
logger.debug(f"静态图层 {name} → GPKG")
|
||||
break
|
||||
else:
|
||||
# 没匹配到静态层,继续处理为 postgres
|
||||
self._fix_postgres_layer(layer, db_config, actual_schema)
|
||||
if layer.providerType() != "postgres":
|
||||
continue
|
||||
|
||||
if provider == "ogr":
|
||||
static_count += 1
|
||||
continue
|
||||
|
||||
if provider == "postgres":
|
||||
total += 1
|
||||
if layer.isValid():
|
||||
continue # TemplateModifier 已修正,跳过
|
||||
try:
|
||||
self._fix_postgres_layer(layer, db_config, actual_schema)
|
||||
if layer.isValid():
|
||||
fixed += 1
|
||||
else:
|
||||
failed += 1
|
||||
except Exception as e:
|
||||
logger.error(f" 修复图层 {layer.name()} 失败: {e}")
|
||||
failed += 1
|
||||
|
||||
if static_count:
|
||||
logger.info(f"静态底图已本地化: {static_count} 个图层")
|
||||
elapsed = time.time() - t0
|
||||
if total:
|
||||
logger.info(
|
||||
f" 图层修正: 总计{total}个PG层, 跳过{total - fixed - failed}(已有效), "
|
||||
f"修复{fixed}, 仍失败{failed}, 耗时{elapsed:.1f}s"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def _resolve_postgres_layers(project: QgsProject) -> None:
|
||||
"""FlagDontResolveLayers 跳过了数据库连接,重新 setDataSource 触发"""
|
||||
for layer in project.mapLayers().values():
|
||||
if layer.providerType() != "postgres":
|
||||
continue
|
||||
try:
|
||||
source = layer.source()
|
||||
if source:
|
||||
layer.setDataSource(source, layer.name(), "postgres")
|
||||
except Exception:
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def _fix_postgres_layer(layer, db_config, actual_schema):
|
||||
@@ -168,9 +190,8 @@ class MapService:
|
||||
layer.setDataSource(uri.uri(), layer.name(), "postgres")
|
||||
|
||||
if layer.isValid():
|
||||
fc = layer.featureCount()
|
||||
logger.info(f"图层 {layer.name()} 连接更新成功 ({fc} features)")
|
||||
logger.debug(f" 图层 {layer.name()} 连接更新成功")
|
||||
else:
|
||||
logger.error(f"图层 {layer.name()} 更新后仍无效")
|
||||
logger.error(f" 图层 {layer.name()} 更新后仍无效")
|
||||
except Exception as e:
|
||||
logger.error(f"更新图层 {layer.name()} 连接失败: {e}")
|
||||
logger.error(f" 更新图层 {layer.name()} 连接失败: {e}")
|
||||
Reference in New Issue
Block a user