diff --git a/app/api/qgis_map_export.py b/app/api/qgis_map_export.py index 9dd79be..62ab76b 100644 --- a/app/api/qgis_map_export.py +++ b/app/api/qgis_map_export.py @@ -218,23 +218,6 @@ def _build_qgis_config(batch_folder: str) -> dict: "qgis": { "exportDpi": getattr(settings, "QGIS_EXPORT_DPI", 300), }, - "template_override": { - "enabled": getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ENABLED", False), - "original": { - "host": getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ORIGINAL_HOST", "localhost"), - "port": getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ORIGINAL_PORT", 5432), - "dbname": getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ORIGINAL_DB_NAME", "yjzyk_xian"), - "schema": getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ORIGINAL_SCHEMA", "base"), - }, - "actual": { - "host": getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ACTUAL_HOST", ""), - "port": getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ACTUAL_PORT", ""), - "dbname": getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ACTUAL_DB_NAME", "xian_new"), - "schema": getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ACTUAL_SCHEMA", "qgis"), - "username": getattr(settings, "DB_USER", "postgres"), - "password": getattr(settings, "DB_PASSWORD", ""), - }, - }, "static_layers": build_static_layers_config(gpkg_dir), "batch_folder": batch_folder, } diff --git a/app/data/cache/qgis_templates/19470628504c856d.qgz b/app/data/cache/qgis_templates/19470628504c856d.qgz deleted file mode 100644 index 4237f75..0000000 Binary files a/app/data/cache/qgis_templates/19470628504c856d.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/32dc5df99342a7da.qgz b/app/data/cache/qgis_templates/32dc5df99342a7da.qgz deleted file mode 100644 index 8aa0455..0000000 Binary files a/app/data/cache/qgis_templates/32dc5df99342a7da.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/3bac2f2636b197e9.qgz b/app/data/cache/qgis_templates/3bac2f2636b197e9.qgz deleted file mode 100644 index b3d5895..0000000 Binary files a/app/data/cache/qgis_templates/3bac2f2636b197e9.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/41f47cbdd9c9c8d0.qgz b/app/data/cache/qgis_templates/41f47cbdd9c9c8d0.qgz deleted file mode 100644 index 7febeb0..0000000 Binary files a/app/data/cache/qgis_templates/41f47cbdd9c9c8d0.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/4c40d3feeddf4567.qgz b/app/data/cache/qgis_templates/4c40d3feeddf4567.qgz deleted file mode 100644 index 0a6a778..0000000 Binary files a/app/data/cache/qgis_templates/4c40d3feeddf4567.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/5a480fbeb8b2c66c.qgz b/app/data/cache/qgis_templates/5a480fbeb8b2c66c.qgz deleted file mode 100644 index bbba32e..0000000 Binary files a/app/data/cache/qgis_templates/5a480fbeb8b2c66c.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/66a88abaa976d72a.qgz b/app/data/cache/qgis_templates/66a88abaa976d72a.qgz deleted file mode 100644 index 36d821e..0000000 Binary files a/app/data/cache/qgis_templates/66a88abaa976d72a.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/82ba30aacf8a53dd.qgz b/app/data/cache/qgis_templates/82ba30aacf8a53dd.qgz deleted file mode 100644 index eedc10a..0000000 Binary files a/app/data/cache/qgis_templates/82ba30aacf8a53dd.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/98c5467c5904eee5.qgz b/app/data/cache/qgis_templates/98c5467c5904eee5.qgz deleted file mode 100644 index f2b2da0..0000000 Binary files a/app/data/cache/qgis_templates/98c5467c5904eee5.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/bc46a5234f68e54e.qgz b/app/data/cache/qgis_templates/bc46a5234f68e54e.qgz deleted file mode 100644 index 1f2747f..0000000 Binary files a/app/data/cache/qgis_templates/bc46a5234f68e54e.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/d8c1e99a6640a0fa.qgz b/app/data/cache/qgis_templates/d8c1e99a6640a0fa.qgz deleted file mode 100644 index cc0a0ef..0000000 Binary files a/app/data/cache/qgis_templates/d8c1e99a6640a0fa.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/d94f0d54dd607cbf.qgz b/app/data/cache/qgis_templates/d94f0d54dd607cbf.qgz deleted file mode 100644 index e4b78cc..0000000 Binary files a/app/data/cache/qgis_templates/d94f0d54dd607cbf.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/dc2534d5788a805f.qgz b/app/data/cache/qgis_templates/dc2534d5788a805f.qgz deleted file mode 100644 index e18c4e7..0000000 Binary files a/app/data/cache/qgis_templates/dc2534d5788a805f.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/ef423854adc709b4.qgz b/app/data/cache/qgis_templates/ef423854adc709b4.qgz deleted file mode 100644 index 0fdf90c..0000000 Binary files a/app/data/cache/qgis_templates/ef423854adc709b4.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/f0cf04a82b3bfb58.qgz b/app/data/cache/qgis_templates/f0cf04a82b3bfb58.qgz deleted file mode 100644 index 8fecf3d..0000000 Binary files a/app/data/cache/qgis_templates/f0cf04a82b3bfb58.qgz and /dev/null differ diff --git a/app/data/cache/qgis_templates/index.json b/app/data/cache/qgis_templates/index.json deleted file mode 100644 index a574ebf..0000000 --- a/app/data/cache/qgis_templates/index.json +++ /dev/null @@ -1,244 +0,0 @@ -{ - "templates": { - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨内涝潜在隐患点及人口分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\4c40d3feeddf4567.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨内涝潜在隐患点及农作物分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\19470628504c856d.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨泥石流潜在隐患点及农作物分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\32dc5df99342a7da.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨防汛物资分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\f0cf04a82b3bfb58.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨山洪潜在隐患点及人口分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\66a88abaa976d72a.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨山洪潜在隐患点及农作物分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\3bac2f2636b197e9.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨附近医院分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\ef423854adc709b4.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨滑坡潜在隐患点及人口分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\98c5467c5904eee5.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨地灾风险区分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\41f47cbdd9c9c8d0.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.87768948431949, - "ymin": 32.972841920667406, - "xmax": 108.96006631597123, - "ymax": 33.68287129318787 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨附近水库分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\dc2534d5788a805f.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨滑坡潜在隐患点及农作物分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\bc46a5234f68e54e.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨救援队伍分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\d8c1e99a6640a0fa.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨城市生命线工程分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\82ba30aacf8a53dd.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.46472706539424, - "ymin": 32.921385746843555, - "xmax": 108.51635924537791, - "ymax": 33.61124690706137 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨泥石流潜在隐患点及人口分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\5a480fbeb8b2c66c.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.95707951802657, - "ymin": 32.9127480757085, - "xmax": 109.03945634967837, - "ymax": 33.622777448228966 - } - }, - "F:/project/xian/xian_algorithm_new/app/data/template/rainfall/暴雨避难场所分布图.qgz": { - "file": "F:\\project\\xian\\xian_algorithm_new\\app\\data\\cache\\qgis_templates\\d94f0d54dd607cbf.qgz", - "layout": "A3", - "texts": { - "mapTitle": "四川长宁6.0级地震震中附近乡镇距离分布图", - "mapTime": "制图时间:2020年11月", - "mapUint": "制图单位:四川省地震应急分服务中心", - "info": "时间:2019年06月17日22点55分\n震级:6.0级\n位置:四川省宜宾市长宁县双河镇" - }, - "extent": { - "xmin": 107.46472706539424, - "ymin": 32.921385746843555, - "xmax": 108.51635924537791, - "ymax": 33.61124690706137 - } - } - } -} \ No newline at end of file diff --git a/app/script/export_static_layers.py b/app/script/export_static_layers.py index 7e08dbb..6df97cd 100644 --- a/app/script/export_static_layers.py +++ b/app/script/export_static_layers.py @@ -124,12 +124,9 @@ def main(): # 从 config.settings 读取数据库配置 try: from config import settings - host = getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ACTUAL_HOST", - getattr(settings, "DB_HOST", "47.92.216.173")) - port = str(getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ACTUAL_PORT", - getattr(settings, "DB_PORT", 7654))) - dbname = getattr(settings, "QGIS_TEMPLATE_OVERRIDE_ACTUAL_DB_NAME", - getattr(settings, "DB_NAME", "xian_new")) + host = getattr(settings, "DB_HOST", "47.92.216.173") + port = str(getattr(settings, "DB_PORT", 7654)) + dbname = getattr(settings, "DB_NAME", "xian_new") user = getattr(settings, "DB_USER", "postgres") password = getattr(settings, "DB_PASSWORD", "zhangsan") except Exception: diff --git a/app/services/qgis/map_service.py b/app/services/qgis/map_service.py index dc860c8..48a0cb1 100644 --- a/app/services/qgis/map_service.py +++ b/app/services/qgis/map_service.py @@ -1,6 +1,6 @@ """ 地图生成主流程控制器。 -协调模板加载、图层过滤、缩放、文本更新、导出。 +模板加载 → 图层过滤 → 缩放 → 文本更新 → 导出。 """ import os import time @@ -9,7 +9,6 @@ from qgis.core import QgsProject, QgsDataSourceUri from app.config.paths import get_logger from app.config.qgis_mappings import TABLE_RENAMES, SCHEMA_REPLACEMENTS -from .template_cache import TemplateCache from .template_modifier import TemplateModifier from .layer_filter import LayerFilter from .map_exporter import MapExporter @@ -17,56 +16,37 @@ from app.utils.map_zoom import MapZoom logger = get_logger("qgis.service") -# 全局模板缓存(跨请求复用) -template_cache = TemplateCache() - class MapService: def __init__(self, config: dict): self.config = config def generate(self, model: dict) -> str: - """ - 执行完整的地图生成流程。 - """ - _timing = {} # {step: seconds} + _timing = {} 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:加载/恢复模板 ── + # ── 步骤 1:加载模板 ── t0 = time.time() - if is_cache_hit: - logger.info(f"[缓存命中] {template_name}") - project.clear() - project, texts, extent = template_cache.restore_template(template_path) - template_cache.reset_project_state(project, texts, extent) - else: - logger.info(f"[首次加载] {template_name}") - 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 + 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 _timing["1.load"] = time.time() - t0 # ── 步骤 2:图层连接修正 ── t0 = time.time() - if not is_cache_hit: - self._fix_invalid_layers(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) @@ -77,15 +57,11 @@ class MapService: 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}" - ) + 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"], + "X": model["centerX"], "Y": model["centerY"], "value": model["zoomValue"], }) _timing["4.zoom"] = time.time() - t0 @@ -99,18 +75,14 @@ class MapService: 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"{template_name} → {total:.1f}s ({steps})" - ) + logger.info(f"{template_name} → {total:.1f}s ({steps})") return model["name"] def _fix_invalid_layers(self, project: QgsProject) -> None: - """只修复 TemplateModifier 处理后仍无效的图层(TemplateModifier 已修正大部分连接)""" + """只修复 TemplateModifier 处理后仍无效的图层""" t0 = time.time() db_config = self.config["db"] - override = self.config.get("template_override", {}) - actual_schema = override.get("actual", {}).get("schema", "qgis") + actual_schema = "qgis" fixed = 0 failed = 0 @@ -120,7 +92,7 @@ class MapService: continue total += 1 if layer.isValid(): - continue # TemplateModifier 已修正,跳过 + continue try: self._fix_postgres_layer(layer, db_config, actual_schema) if layer.isValid(): @@ -138,42 +110,21 @@ class MapService: 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): """修正单个 PostgreSQL 图层的连接参数""" try: uri = layer.dataProvider().uri() uri.setConnection( - db_config["host"], - str(db_config["port"]), - db_config["database"], - db_config["username"], - db_config["password"], + db_config["host"], str(db_config["port"]), + db_config["database"], db_config["username"], db_config["password"], ) - # Schema 替换 uri_str = uri.uri() for old_schema in SCHEMA_REPLACEMENTS: if f'table="{old_schema}".' in uri_str: - uri_str = uri_str.replace( - f'table="{old_schema}".', - f'table="{actual_schema}".', - ) + uri_str = uri_str.replace(f'table="{old_schema}".', f'table="{actual_schema}".') 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}"' @@ -181,17 +132,14 @@ class MapService: if full_old in uri_str: uri_str = uri_str.replace(full_old, full_new) uri = QgsDataSourceUri(uri_str) - # SRID 修正 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(): logger.debug(f" 图层 {layer.name()} 连接更新成功") else: logger.error(f" 图层 {layer.name()} 更新后仍无效") except Exception as e: - logger.error(f" 更新图层 {layer.name()} 连接失败: {e}") \ No newline at end of file + logger.error(f" 更新图层 {layer.name()} 连接失败: {e}") diff --git a/app/services/qgis/qgis_daemon.py b/app/services/qgis/qgis_daemon.py index 06e6b95..8b7f9c8 100644 --- a/app/services/qgis/qgis_daemon.py +++ b/app/services/qgis/qgis_daemon.py @@ -115,18 +115,10 @@ def main(): qgs_app = QgsApplication([], False) qgs_app.initQgis() - from app.services.qgis.map_service import MapService, template_cache - from app.services.qgis.template_modifier import TemplateModifier + from app.services.qgis.map_service import MapService from app.config.qgis_mappings import build_static_layers_config, get_gpkg_dir - # 从磁盘恢复已缓存的模板 - cached_count = template_cache.load_persistent_cache() - print(f"[qgis_daemon] QGIS 已启动, 磁盘缓存: {cached_count} 个模板", - file=sys.stderr, flush=True) - - total_cached = len(template_cache._cache) - print(f"[qgis_daemon] 就绪: 缓存共{total_cached}个模板", - file=sys.stderr, flush=True) + print("[qgis_daemon] QGIS 已启动", file=sys.stderr, flush=True) # ── 请求处理循环 ── print("[qgis_daemon] 等待请求...", file=sys.stderr, flush=True) @@ -197,7 +189,6 @@ def main(): # 清理 project = QgsProject.instance() project.clear() - template_cache.cleanup() print("[qgis_daemon] 已退出", file=sys.stderr, flush=True) diff --git a/app/services/qgis/qgis_runner.py b/app/services/qgis/qgis_runner.py index ae8b017..dcd9137 100644 --- a/app/services/qgis/qgis_runner.py +++ b/app/services/qgis/qgis_runner.py @@ -138,12 +138,6 @@ def main(): # 初始化 QgsApplication(只做一次) qgs_app = _init_qgis() - # 从磁盘恢复已缓存的模板(跨进程加速) - from app.services.qgis.map_service import template_cache - cached_count = template_cache.load_persistent_cache() - if cached_count > 0: - print(f"[qgis_runner] 磁盘缓存命中: {cached_count} 个模板", file=sys.stderr) - try: from app.services.qgis.map_service import MapService diff --git a/app/services/qgis/template_cache.py b/app/services/qgis/template_cache.py deleted file mode 100644 index 9b747b4..0000000 --- a/app/services/qgis/template_cache.py +++ /dev/null @@ -1,192 +0,0 @@ -""" -模板缓存引擎(支持持久化)。 - -流程: -1. 首次加载:TemplateModifier 修改模板 → project.read() → 保存到磁盘缓存 -2. 同进程内缓存命中:从内存恢复(~1s) -3. 跨进程/重启:从磁盘缓存恢复(首次 ~5s,后续 ~1s) -""" -import hashlib -import json -import os -import shutil -import time -import tempfile - -from qgis.core import QgsProject, QgsRectangle - -from app.config.paths import get_logger - -logger = get_logger("qgis.cache") - -# 持久化缓存目录(项目根目录下) -_CACHE_DIR = os.path.join( - os.path.dirname(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__))))), - "app", "data", "cache", "qgis_templates", -) -_INDEX_FILE = os.path.join(_CACHE_DIR, "index.json") - - -class TemplateCache: - def __init__(self): - self._cache: dict[str, dict] = {} - - # ── 公开接口 ── - - def is_loaded(self, template_path: str) -> bool: - return template_path in self._cache - - def save_current(self, template_path: str, layout_name: str = "A4") -> None: - """将当前已加载的项目保存到内存缓存 + 磁盘缓存""" - project = QgsProject.instance() - - # ── 保存项目到持久化缓存目录 ── - os.makedirs(_CACHE_DIR, exist_ok=True) - key = self._path_key(template_path) - cache_file = os.path.join(_CACHE_DIR, f"{key}.qgz") - - t_save = time.time() - project.write(cache_file) - logger.info(f" 缓存写入磁盘: {os.path.basename(cache_file)} ({time.time() - t_save:.1f}s)") - - # ── 记录状态 ── - texts = {} - extent = None - layout = project.layoutManager().layoutByName(layout_name) - if layout: - for item_id in ["mapTitle", "mapTime", "mapUint", "info"]: - item = layout.itemById(item_id) - if item: - texts[item_id] = item.text() - map_item = layout.itemById("Map") - if map_item: - extent = QgsRectangle(map_item.extent()) - - entry = { - "file": cache_file, - "texts": texts, - "extent": extent, - "layout": layout_name, - } - self._cache[template_path] = entry - - # ── 更新索引文件 ── - self._update_index(template_path, entry) - logger.info(f" 模板已缓存: {os.path.basename(template_path)} ({len(self._cache)} 个)") - - def restore_template(self, template_path: str) -> tuple: - """从缓存恢复模板""" - cached = self._cache.get(template_path) - if not cached: - raise RuntimeError(f"模板未缓存: {template_path}") - - project = QgsProject.instance() - t0 = time.time() - - # 大部分图层已 GPKG 本地化,不需要 FlagDontResolveLayers - project.read(cached["file"]) - - logger.debug(f" 恢复耗时: {time.time() - t0:.1f}s") - return project, cached["texts"], cached["extent"] - - def reset_project_state(self, project: QgsProject, texts: dict, extent) -> None: - """重置项目到干净状态""" - for layer in project.mapLayers().values(): - if layer.subsetString(): - layer.setSubsetString("") - - layout_name = "A4" - for cached in self._cache.values(): - layout_name = cached.get("layout", "A4") - break - - layout = project.layoutManager().layoutByName(layout_name) - if layout: - for item_id, text in texts.items(): - item = layout.itemById(item_id) - if item: - item.setText(text) - if extent: - map_item = layout.itemById("Map") - if map_item: - map_item.zoomToExtent(extent) - - def load_persistent_cache(self) -> int: - """从磁盘恢复所有缓存到内存(qgis_runner 启动时调用)""" - if not os.path.isfile(_INDEX_FILE): - logger.info(" 磁盘缓存为空,首次启动需要加载模板") - return 0 - - try: - with open(_INDEX_FILE, "r", encoding="utf-8") as f: - index = json.load(f) - except (json.JSONDecodeError, OSError) as e: - logger.warning(f" 缓存索引损坏: {e}") - return 0 - - count = 0 - for template_path, entry in index.get("templates", {}).items(): - cache_file = entry.get("file", "") - if not cache_file or not os.path.isfile(cache_file): - logger.debug(f" 跳过无效缓存: {os.path.basename(template_path)}") - continue - texts = entry.get("texts", {}) - extent = entry.get("extent") - # 反序列化 extent - if extent and isinstance(extent, dict): - extent = QgsRectangle( - extent.get("xmin", 0), extent.get("ymin", 0), - extent.get("xmax", 0), extent.get("ymax", 0), - ) - else: - extent = None - self._cache[template_path] = { - "file": cache_file, - "texts": texts, - "extent": extent, - "layout": entry.get("layout", "A4"), - } - count += 1 - - logger.info(f" 磁盘缓存加载: {count} 个模板") - return count - - def cleanup(self) -> None: - """清理内存缓存(磁盘缓存保留)""" - self._cache.clear() - logger.info("已清理内存缓存") - - # ── 内部方法 ── - - @staticmethod - def _path_key(template_path: str) -> str: - """模板路径 → 缓存文件名 key""" - return hashlib.md5(template_path.encode()).hexdigest()[:16] - - def _update_index(self, template_path: str, entry: dict) -> None: - """更新磁盘索引文件""" - index = {} - if os.path.isfile(_INDEX_FILE): - try: - with open(_INDEX_FILE, "r", encoding="utf-8") as f: - index = json.load(f) - except (json.JSONDecodeError, OSError): - pass - - index.setdefault("templates", {})[template_path] = { - "file": entry["file"], - "layout": entry.get("layout", "A4"), - "texts": entry.get("texts", {}), - "extent": ( - { - "xmin": entry["extent"].xMinimum(), - "ymin": entry["extent"].yMinimum(), - "xmax": entry["extent"].xMaximum(), - "ymax": entry["extent"].yMaximum(), - } - if entry.get("extent") else None - ), - } - - with open(_INDEX_FILE, "w", encoding="utf-8") as f: - json.dump(index, f, ensure_ascii=False, indent=2) diff --git a/app/services/qgis/template_modifier.py b/app/services/qgis/template_modifier.py index 159e490..f87413a 100644 --- a/app/services/qgis/template_modifier.py +++ b/app/services/qgis/template_modifier.py @@ -57,14 +57,13 @@ class TemplateModifier: def modify(self, template_path: str) -> str: """修改模板文件,返回修改后的临时 .qgz 路径""" override = self.config.get("template_override") - has_override = override and override.get("enabled", False) has_static = bool(self._static_map) - if not has_override and not has_static: + if not override and not has_static: return template_path - orig = override["original"] if has_override else None - actual = override["actual"] if has_override else None + orig = override.get("original") if override else None + actual = override.get("actual") if override else None try: tmp = tempfile.NamedTemporaryFile( diff --git a/settings.toml b/settings.toml index 9d41810..3329b82 100644 --- a/settings.toml +++ b/settings.toml @@ -85,16 +85,6 @@ QGIS_ROOT = "D:/QGIS" # 专题图输出子目录 QGIS_OUTPUT_DIR = "xian/qgis/map/:eventType/:disasterTime" QGIS_DEFAULTS_MAP_UNIT = "制图单位:西安市应急管理局" -# 模板数据库覆盖:将模板中硬编码的连接替换为实际环境连接 -QGIS_TEMPLATE_OVERRIDE_ENABLED = true -QGIS_TEMPLATE_OVERRIDE_ORIGINAL_HOST = "localhost" -QGIS_TEMPLATE_OVERRIDE_ORIGINAL_PORT = 5432 -QGIS_TEMPLATE_OVERRIDE_ORIGINAL_DB_NAME = "yjzyk_xian" -QGIS_TEMPLATE_OVERRIDE_ORIGINAL_SCHEMA = "base" -QGIS_TEMPLATE_OVERRIDE_ACTUAL_HOST = "47.92.216.173" -QGIS_TEMPLATE_OVERRIDE_ACTUAL_PORT = 7654 -QGIS_TEMPLATE_OVERRIDE_ACTUAL_DB_NAME = "xian_new" -QGIS_TEMPLATE_OVERRIDE_ACTUAL_SCHEMA = "qgis" # ============================================================ # 生产环境 @@ -133,13 +123,3 @@ FILE_STORE_DIR = "/data" # QGIS 配置 # ============================================================ QGIS_ROOT = "/home/QGIS" -# 模板数据库覆盖:将模板中硬编码的连接替换为实际环境连接 -QGIS_TEMPLATE_OVERRIDE_ENABLED = true -QGIS_TEMPLATE_OVERRIDE_ORIGINAL_HOST = "localhost" -QGIS_TEMPLATE_OVERRIDE_ORIGINAL_PORT = 5432 -QGIS_TEMPLATE_OVERRIDE_ORIGINAL_DB_NAME = "yjzyk_xian" -QGIS_TEMPLATE_OVERRIDE_ORIGINAL_SCHEMA = "base" -QGIS_TEMPLATE_OVERRIDE_ACTUAL_HOST = "10.22.245.138" -QGIS_TEMPLATE_OVERRIDE_ACTUAL_PORT = 54321 -QGIS_TEMPLATE_OVERRIDE_ACTUAL_DB_NAME = "xian_new" -QGIS_TEMPLATE_OVERRIDE_ACTUAL_SCHEMA = "qgis"