From 298c47f1d79f306d29ec72da7c2456f504e8a727 Mon Sep 17 00:00:00 2001 From: index-git Date: Wed, 17 Feb 2021 17:41:34 +0100 Subject: [PATCH] Ensure Output SRS list for all QGIS layers --- CHANGELOG.md | 1 + src/layman/__init__.py | 6 +- .../{geoserver => }/output_srs_list_test.py | 68 ++++++++++++++----- src/layman/layer/qgis/output_srs.py | 11 +++ src/layman/layer/qgis/wms.py | 12 ++++ 5 files changed, 80 insertions(+), 18 deletions(-) rename src/layman/common/{geoserver => }/output_srs_list_test.py (56%) create mode 100644 src/layman/layer/qgis/output_srs.py diff --git a/CHANGELOG.md b/CHANGELOG.md index 39dedbe4f..de0cda1fb 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -24,6 +24,7 @@ - [#154](https://github.com/jirik/layman/issues/154) For endpoints [POST Layers](doc/rest.md#post-layers) and [PATCH Layer](doc/rest.md#patch-layer), parameter *sld* is replaced by the new parameter *style* and marked as deprecated. In response to endpoints [GET Layer](doc/rest.md#get-layer) and [PATCH Layer](doc/rest.md#patch-layer), *sld* is replaced by the new *style* item and marked as deprecated. - [#154](https://github.com/jirik/layman/issues/154) Also QGIS style file is accepted, previously only SLD file was. - [#154](https://github.com/jirik/layman/issues/154) Layers with [QGIS style](https://docs.qgis.org/3.16/en/docs/user_manual/style_library/style_manager.html#exporting-items) are published on [GeoServer dedicated WMS workspace](doc/data-storage.md#geoserver) through WMS cascade from QGIS server, where they are stored as QGS file. All layers are published directly from PostgreSQL database to GeoServer for [WFS workspace](doc/data-storage.md#geoserver). +- [#154](https://github.com/jirik/layman/issues/154) During startup, [LAYMAN_OUTPUT_SRS_LIST](doc/env-settings.md#LAYMAN_OUTPUT_SRS_LIST) is ensure for all QGIS layers. ## v1.9.1 2021-01-18 diff --git a/src/layman/__init__.py b/src/layman/__init__.py index c6c2d5d07..80ac87e59 100644 --- a/src/layman/__init__.py +++ b/src/layman/__init__.py @@ -81,7 +81,6 @@ with app.app_context(): from . import upgrade - upgrade.upgrade() app.logger.info(f'Loading Redis database') @@ -93,6 +92,11 @@ import_authn_to_redis() + app.logger.info(f'Update SRS output list for QGIS projects') + with app.app_context(): + from .layer.qgis import output_srs + output_srs.ensure_output_srs_for_all() + pipe.multi() pipe.set(LAYMAN_DEPS_ADJUSTED_KEY, 'done') pipe.execute() diff --git a/src/layman/common/geoserver/output_srs_list_test.py b/src/layman/common/output_srs_list_test.py similarity index 56% rename from src/layman/common/geoserver/output_srs_list_test.py rename to src/layman/common/output_srs_list_test.py index 74a2571cc..27713d8e9 100644 --- a/src/layman/common/geoserver/output_srs_list_test.py +++ b/src/layman/common/output_srs_list_test.py @@ -1,5 +1,6 @@ import pytest -from layman import settings +from layman import settings, app +from layman.layer.qgis import util as qgis_util, wms as qgis_wms from test import process, process_client, geoserver_client @@ -24,34 +25,54 @@ def register_layer_to_delete(workspace, layername): @pytest.fixture(scope="module") def ensure_layer(delete_layer_after_test): - def ensure_layer_internal(workspace, layername, file_paths=None): + def ensure_layer_internal(workspace, layername, file_paths=None, style_file=None): if (workspace, layername) not in LAYERS_TO_DELETE_AFTER_TEST: - process_client.publish_layer(workspace, layername, file_paths=file_paths) + process_client.publish_layer(workspace, layername, file_paths=file_paths, style_file=style_file) delete_layer_after_test(workspace, layername) yield ensure_layer_internal def test_custom_srs_list(ensure_layer): workspace = 'test_custom_srs_list_workspace' - layername1 = 'test_custom_srs_list_layer1' - layername2 = 'test_custom_srs_list_layer2' + layer_sld1 = 'test_custom_srs_list_sld_layer1' + layer_sld2 = 'test_custom_srs_list_sld_layer2' + layer_qgis1 = 'test_custom_srs_list_qgis_layer1' + layer_qgis2 = 'test_custom_srs_list_qgis_layer2' + source_style_file_path = 'sample/style/funny_qml.xml' assert settings.LAYMAN_OUTPUT_SRS_LIST != OUTPUT_SRS_LIST process.ensure_layman_function(process.LAYMAN_DEFAULT_SETTINGS) - ensure_layer(workspace, layername1) - assert_wms_output_srs_list(workspace, layername1, settings.LAYMAN_OUTPUT_SRS_LIST) - assert_wfs_output_srs_list(workspace, layername1, settings.LAYMAN_OUTPUT_SRS_LIST) + ensure_layer(workspace, layer_sld1) + ensure_layer(workspace, layer_qgis1, style_file=source_style_file_path) + + with app.app_context(): + assert_gs_wms_output_srs_list(workspace, layer_sld1, settings.LAYMAN_OUTPUT_SRS_LIST) + assert_wfs_output_srs_list(workspace, layer_sld1, settings.LAYMAN_OUTPUT_SRS_LIST) + assert not qgis_wms.get_layer_info(workspace, layer_sld1) + + assert_gs_wms_output_srs_list(workspace, layer_qgis1, settings.LAYMAN_OUTPUT_SRS_LIST) + assert_wfs_output_srs_list(workspace, layer_qgis1, settings.LAYMAN_OUTPUT_SRS_LIST) + assert_qgis_output_srs_list(workspace, layer_qgis1, settings.LAYMAN_OUTPUT_SRS_LIST) + assert_qgis_wms_output_srs_list(workspace, layer_qgis1, settings.LAYMAN_OUTPUT_SRS_LIST) process.ensure_layman_function({ 'LAYMAN_OUTPUT_SRS_LIST': ','.join([str(code) for code in OUTPUT_SRS_LIST]) }) - ensure_layer(workspace, layername2) - for layername in [layername1, layername2]: - assert_wms_output_srs_list(workspace, layername, OUTPUT_SRS_LIST) - assert_wfs_output_srs_list(workspace, layername, OUTPUT_SRS_LIST) - - -def assert_wms_output_srs_list(workspace, layername, expected_output_srs_list): + ensure_layer(workspace, layer_sld2) + ensure_layer(workspace, layer_qgis2, style_file=source_style_file_path) + with app.app_context(): + for layer in [layer_sld1, layer_sld2, ]: + assert_gs_wms_output_srs_list(workspace, layer, OUTPUT_SRS_LIST) + assert_wfs_output_srs_list(workspace, layer, OUTPUT_SRS_LIST) + assert not qgis_wms.get_layer_info(workspace, layer) + for layer in [layer_qgis1, layer_qgis2, ]: + assert_gs_wms_output_srs_list(workspace, layer, OUTPUT_SRS_LIST) + assert_wfs_output_srs_list(workspace, layer, OUTPUT_SRS_LIST) + assert_qgis_output_srs_list(workspace, layer, OUTPUT_SRS_LIST) + assert_qgis_wms_output_srs_list(workspace, layer, OUTPUT_SRS_LIST) + + +def assert_gs_wms_output_srs_list(workspace, layername, expected_output_srs_list): wms = geoserver_client.get_wms_capabilities(workspace) assert layername in wms.contents wms_layer = wms.contents[layername] @@ -59,6 +80,14 @@ def assert_wms_output_srs_list(workspace, layername, expected_output_srs_list): assert f"EPSG:{expected_output_srs}" in wms_layer.crsOptions +def assert_qgis_wms_output_srs_list(workspace, layer, expected_output_srs_list): + wms = qgis_wms.get_wms_capabilities(workspace, layer) + assert layer in wms.contents + wms_layer = wms.contents[layer] + for expected_output_srs in expected_output_srs_list: + assert f"EPSG:{expected_output_srs}" in wms_layer.crsOptions + + def assert_wfs_output_srs_list(workspace, layername, expected_output_srs_list): wfs = geoserver_client.get_wfs_capabilities(workspace) full_layername = f"{workspace}:{layername}" @@ -69,6 +98,11 @@ def assert_wfs_output_srs_list(workspace, layername, expected_output_srs_list): assert f"urn:ogc:def:crs:EPSG::{expected_output_srs}" in crs_names +def assert_qgis_output_srs_list(workspace, layer, expected_srs_list): + with app.app_context(): + assert qgis_util.get_layer_wms_crs_list_values(workspace, layer) == set(expected_srs_list) + + # expected coordinates manually copied from QGIS 3.16.2 in given EPSG # point_id 1: northernmost vertex of fountain at Moravske namesti, Brno @pytest.mark.parametrize('point_id, epsg_code, exp_coordinates, precision', [ @@ -79,14 +113,14 @@ def assert_wfs_output_srs_list(workspace, layername, expected_output_srs_list): (1, 32634, (179991.0748, 5458879.0878), 0.1), (1, 5514, (-598208.8093, -1160307.4484), 0.1), ]) -def test_spatial_precision(ensure_layer, point_id, epsg_code, exp_coordinates, precision): +def test_spatial_precision(ensure_layer, point_id, epsg_code, exp_coordinates, precision, ): process.ensure_layman_function({ 'LAYMAN_OUTPUT_SRS_LIST': ','.join([str(code) for code in OUTPUT_SRS_LIST]) }) workspace = 'test_coordinate_precision_workspace' layername = 'test_coordinate_precision_layer' - ensure_layer(workspace, layername, file_paths=['sample/layman.layer/sample_point_cz.geojson']) + ensure_layer(workspace, layername, file_paths=['sample/layman.layer/sample_point_cz.geojson'], ) feature_collection = geoserver_client.get_features(workspace, layername, epsg_code=epsg_code) feature = next(f for f in feature_collection['features'] if f['properties']['point_id'] == point_id) diff --git a/src/layman/layer/qgis/output_srs.py b/src/layman/layer/qgis/output_srs.py new file mode 100644 index 000000000..10942ec48 --- /dev/null +++ b/src/layman/layer/qgis/output_srs.py @@ -0,0 +1,11 @@ +from . import util, wms +from layman import util as layman_util, settings + + +def ensure_output_srs_for_all(): + layers = layman_util.get_publication_infos(style_type='qgis') + if layers: + (workspace, _, layer) = next(iter(layers.keys())) + if util.get_layer_wms_crs_list_values(workspace, layer) != settings.LAYMAN_OUTPUT_SRS_LIST: + for (workspace, _, layer) in layers.keys(): + wms.save_qgs_file(workspace, layer) diff --git a/src/layman/layer/qgis/wms.py b/src/layman/layer/qgis/wms.py index 7a69237e4..2146659f8 100644 --- a/src/layman/layer/qgis/wms.py +++ b/src/layman/layer/qgis/wms.py @@ -1,4 +1,5 @@ import os +from owslib.wms import WebMapService from . import util, wms from .. import db @@ -72,3 +73,14 @@ def save_qgs_file(workspace, layer): qgs_str = util.fill_project_template(workspace, layer, uuid, layer_qml, settings.LAYMAN_OUTPUT_SRS_LIST, layer_bbox) with open(wms.get_layer_file_path(workspace, layer), "w") as qgs_file: print(qgs_str, file=qgs_file) + + +def wms_direct(wms_url, xml=None, version=None, headers=None): + version = version or VERSION + result_wms = WebMapService(wms_url, xml=xml.encode('utf-8') if xml is not None else xml, version=version, headers=headers) + return result_wms + + +def get_wms_capabilities(workspace=None, layer=None, headers=None): + wms_url = get_layer_capabilities_url(workspace, layer) + return wms_direct(wms_url, headers=headers)