Skip to content

Commit

Permalink
Add tests about layer with expression, simplify code
Browse files Browse the repository at this point in the history
  • Loading branch information
Gustry committed Aug 14, 2024
1 parent 1d6bbd1 commit d78fe93
Show file tree
Hide file tree
Showing 7 changed files with 155 additions and 99 deletions.
54 changes: 12 additions & 42 deletions dynamic_layers/core/dynamic_layers_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
QgsExpression,
QgsFeature,
QgsFeatureRequest,
QgsMapLayer,
QgsMessageLog,
QgsProject,
QgsRectangle,
Expand All @@ -32,42 +31,18 @@ class DynamicLayersEngine:

def __init__(self):
""" Dynamic Layers Engine constructor. """
self._extent_layer = None
self._extent_margin = None
self._dynamic_layers: dict = {}
self._search_and_replace_dictionary: dict = {}
self.extent_layer = None
self.extent_margin = None
self.dynamic_layers: dict = {}
self.variables: dict = {}
self.iface = iface

# For expressions
self.project = None
self.layer = None
self.feature = None

@property
def extent_layer(self) -> QgsMapLayer:
return self._extent_layer

@extent_layer.setter
def extent_layer(self, layer: QgsMapLayer):
self._extent_layer = layer

@property
def extent_margin(self) -> int:
return self._extent_margin

@extent_margin.setter
def extent_margin(self, extent: int):
self._extent_margin = extent

@property
def search_and_replace_dictionary(self) -> dict:
return self._search_and_replace_dictionary

@search_and_replace_dictionary.setter
def search_and_replace_dictionary(self, values: dict):
self._search_and_replace_dictionary = values

def set_search_and_replace_dictionary_from_layer(self, layer: QgsVectorLayer, expression: str):
def set_layer_and_expression(self, layer: QgsVectorLayer, expression: str):
""" Set the search and replace dictionary from a given layer and an expression.
The first found feature is the data source
Expand All @@ -85,36 +60,31 @@ def set_search_and_replace_dictionary_from_layer(self, layer: QgsVectorLayer, ex
# Take only first feature
feature = QgsFeature()
features.nextFeature(feature)
self.set_search_and_replace_dictionary_from_feature(layer, feature)
self.set_layer_and_feature(layer, feature)

def set_search_and_replace_dictionary_from_feature(self, layer: QgsVectorLayer, feature: QgsFeature):
def set_layer_and_feature(self, layer: QgsVectorLayer, feature: QgsFeature):
""" Set a feature for the dictionary. """
self.layer = layer
self.feature = feature
self.search_and_replace_dictionary = dict(zip(layer.fields().names(), feature.attributes()))

def discover_dynamic_layers_from_project(self, project: QgsProject):
""" Check all maplayers in the given project which are dynamic. """
self.project = project
self._dynamic_layers = {
self.dynamic_layers = {
lid: layer for lid, layer in project.mapLayers().items() if
layer.customProperty(CustomProperty.DynamicDatasourceActive) and layer.customProperty(
CustomProperty.DynamicDatasourceContent)
}

def update_dynamic_layers_datasource_from_dict(self):
def update_dynamic_layers_datasource(self):
"""
For each layers with "active" status,
Change the datasource by using the dynamicDatasourceContent
And the given search&replace dictionary
"""
if len(self.search_and_replace_dictionary) < 1:
return

for lid, layer in self._dynamic_layers.items():
# Change datasource
for layer in self.dynamic_layers.values():
a = LayerDataSourceModifier(layer, self.project, self.layer, self.feature)
a.set_new_source_uri_from_dict(self.search_and_replace_dictionary)
a.compute_new_uri(self.variables)

if not self.iface:
continue
Expand Down Expand Up @@ -161,7 +131,7 @@ def set_project_property(self, project_property: Annotated[str, WmsProjectProper
# Replace variable in given val via dictionary
val = string_substitution(
input_string=val,
variables=self.search_and_replace_dictionary,
variables=self.variables,
project=self.project,
layer=self.layer,
feature=self.feature,
Expand Down
4 changes: 2 additions & 2 deletions dynamic_layers/core/generate_projects.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,12 +51,12 @@ def process(self) -> bool:
# noinspection PyUnresolvedReferences
request.setFlags(QgsFeatureRequest.NoGeometry)
for feature in self.coverage.getFeatures(request):
engine.set_search_and_replace_dictionary_from_feature(self.coverage, feature)
engine.set_layer_and_feature(self.coverage, feature)

if self.feedback:
self.feedback.pushDebugInfo(tr('Feature : {}').format(feature.id()))

engine.update_dynamic_layers_datasource_from_dict()
engine.update_dynamic_layers_datasource()
engine.update_dynamic_project_properties()

new_file = string_substitution(
Expand Down
14 changes: 11 additions & 3 deletions dynamic_layers/core/layer_datasource_modifier.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,13 @@

import typing

from qgis._core import QgsProject, QgsVectorLayer, QgsFeature
from qgis.core import QgsMapLayer, QgsReadWriteContext
from qgis.core import (
QgsFeature,
QgsMapLayer,
QgsProject,
QgsReadWriteContext,
QgsVectorLayer,
)
from qgis.PyQt.QtXml import QDomDocument

from dynamic_layers.definitions import CustomProperty
Expand All @@ -27,7 +32,7 @@ def __init__(self, layer: QgsMapLayer, project: QgsProject, layer_context: QgsVe
# Content of the dynamic datasource
self.dynamic_datasource_content = layer.customProperty(CustomProperty.DynamicDatasourceContent)

def set_new_source_uri_from_dict(self, search_and_replace_dictionary: dict = None):
def compute_new_uri(self, search_and_replace_dictionary: dict = None):
"""
Get the dynamic datasource template,
Replace variable with passed data,
Expand All @@ -45,6 +50,9 @@ def set_new_source_uri_from_dict(self, search_and_replace_dictionary: dict = Non
feature=self.feature
)

if not new_uri:
raise Exception(f"New URI invalid : {new_uri}")

# Set the layer datasource
self.set_data_source(new_uri)

Expand Down
6 changes: 3 additions & 3 deletions dynamic_layers/dynamic_layers.py
Original file line number Diff line number Diff line change
Expand Up @@ -845,14 +845,14 @@ def on_apply_variables_clicked(self):
# Set search and replace dictionary
# Collect variables names and values
if self.dlg.is_table_variable_based:
engine.search_and_replace_dictionary = self.dlg.variables()
engine.variables = self.dlg.variables()
else:
layer = self.dlg.inVariableSourceLayer.currentLayer()
exp = self.dlg.inVariableSourceLayerExpression.text()
engine.set_search_and_replace_dictionary_from_layer(layer, exp)
engine.set_layer_and_expression(layer, exp)

# Change layers datasource
engine.update_dynamic_layers_datasource_from_dict()
engine.update_dynamic_layers_datasource()

# Set project properties
engine.update_dynamic_project_properties()
Expand Down
4 changes: 2 additions & 2 deletions dynamic_layers/dynamic_layers_dialog.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,9 @@
from pathlib import Path
from typing import Union

from qgis.PyQt.QtWidgets import QPlainTextEdit
from qgis.core import QgsExpression
from qgis.core import (
QgsApplication,
QgsExpression,
QgsExpressionContext,
QgsExpressionContextScope,
QgsExpressionContextUtils,
Expand All @@ -21,6 +20,7 @@
QDialog,
QDialogButtonBox,
QLineEdit,
QPlainTextEdit,
QTextEdit,
QWidget,
)
Expand Down
6 changes: 4 additions & 2 deletions dynamic_layers/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -48,13 +48,15 @@ def string_substitution(
context.appendScope(scope)

if is_template:
return QgsExpression.replaceExpressionText(input_string, context)
output = QgsExpression.replaceExpressionText(input_string, context)
return output

expression = QgsExpression(input_string)
if expression.hasEvalError() or expression.hasParserError():
raise QgsProcessingException(f"Invalid QGIS expression : {input_string}")

return expression.evaluate(context)
output = expression.evaluate(context)
return output


def plugin_path(*args) -> Path:
Expand Down
Loading

0 comments on commit d78fe93

Please sign in to comment.