Skip to content

Commit a4573af

Browse files
committed
add regulation groups and underground type to feature template models
Initialization logic of libraries from open project was improved also.
1 parent 8493fd8 commit a4573af

File tree

6 files changed

+109
-43
lines changed

6 files changed

+109
-43
lines changed

arho_feature_template/core/models.py

+25-3
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import yaml
1111

1212
from arho_feature_template.exceptions import ConfigSyntaxError
13+
from arho_feature_template.project.layers.code_layers import UndergroundTypeLayer
1314
from arho_feature_template.qgis_plugin_tools.tools.resources import resources_path
1415
from arho_feature_template.utils.misc_utils import LANGUAGE, get_layer_by_name, iface
1516

@@ -45,7 +46,20 @@ class FeatureTemplateLibrary:
4546
feature_templates: list[PlanFeature]
4647

4748
@classmethod
48-
def from_config_file(cls, config_fp: Path) -> FeatureTemplateLibrary:
49+
def find_matching_group_config(cls, group_name: str, regulation_group_libraries: list[RegulationGroupLibrary]):
50+
for library in regulation_group_libraries:
51+
for category in library.regulation_group_categories:
52+
for group in category.regulation_groups:
53+
if group.name == group_name:
54+
return group
55+
return None
56+
57+
@classmethod
58+
def from_config_file(
59+
cls, config_fp: Path, regulation_group_libraries: list[RegulationGroupLibrary]
60+
) -> FeatureTemplateLibrary:
61+
get_underground_id = UndergroundTypeLayer.get_attribute_value_by_another_attribute_value
62+
4963
with config_fp.open(encoding="utf-8") as f:
5064
data = yaml.safe_load(f)
5165
try:
@@ -56,11 +70,19 @@ def from_config_file(cls, config_fp: Path) -> FeatureTemplateLibrary:
5670
feature_templates=[
5771
PlanFeature(
5872
geom=None,
59-
type_of_underground_id=feature_data.get("type_of_underground"), # Need ID conversion?
73+
type_of_underground_id=(
74+
get_underground_id("id", "value", feature_data.get("type_of_underground"))
75+
if feature_data.get("type_of_underground")
76+
else None
77+
),
6078
layer_name=feature_data.get("layer_name"),
6179
name=feature_data.get("name"),
6280
description=feature_data.get("description"),
63-
regulation_groups=[], # feature_data.get("regulation_groups"), # Handle regulation group creaton
81+
regulation_groups=[
82+
group
83+
for group_name in feature_data.get("regulation_groups", [])
84+
if (group := cls.find_matching_group_config(group_name, regulation_group_libraries))
85+
],
6486
plan_id=None,
6587
id_=None,
6688
)

arho_feature_template/core/plan_manager.py

+38-18
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@
2424
from arho_feature_template.gui.dialogs.serialize_plan import SerializePlan
2525
from arho_feature_template.gui.docks.new_feature_dock import NewFeatureDock
2626
from arho_feature_template.gui.tools.inspect_plan_features_tool import InspectPlanFeatures
27-
from arho_feature_template.project.layers.code_layers import PlanRegulationGroupTypeLayer
27+
from arho_feature_template.project.layers.code_layers import PlanRegulationGroupTypeLayer, code_layers
2828
from arho_feature_template.project.layers.plan_layers import (
2929
LandUseAreaLayer,
3030
LandUsePointLayer,
@@ -79,14 +79,11 @@ def __init__(self):
7979
self.json_plan_path = None
8080
self.json_plan_outline_path = None
8181

82-
# Initialize libraries
83-
self.feature_template_libraries = [
84-
FeatureTemplateLibrary.from_config_file(file) for file in feature_template_library_config_files()
85-
]
86-
self.regulation_group_libraries = None
82+
self.feature_template_libraries = []
83+
self.regulation_group_libraries = []
8784

8885
# Initialize new feature dock
89-
self.new_feature_dock = NewFeatureDock(self.feature_template_libraries)
86+
self.new_feature_dock = NewFeatureDock()
9087
self.new_feature_dock.tool_activated.connect(self.add_new_plan_feature)
9188
self.new_feature_dock.hide()
9289

@@ -107,13 +104,36 @@ def __init__(self):
107104
self.lambda_service = LambdaService()
108105
self.lambda_service.jsons_received.connect(self.save_plan_jsons)
109106

110-
def get_regulation_group_libraries(self):
111-
# Lazy initialization of regulation_group_libraries to avoid reading regulation code layer too early
112-
if self.regulation_group_libraries is None:
113-
self.regulation_group_libraries = [
114-
RegulationGroupLibrary.from_config_file(file) for file in regulation_group_library_config_files()
115-
]
116-
return self.regulation_group_libraries
107+
def initialize_from_project(self):
108+
# # If project is not open, don't try to initialize
109+
# # NOTE: QgsProject.instance(), QgsProject().fileInfo() and QgsProject().fileIName()
110+
# # don't seem to work as indicators whether a project is open?
111+
# if not QgsProject.instance() or not QgsProject().fileInfo():
112+
# return
113+
self.initialize_libraries()
114+
115+
def check_required_layers(self):
116+
missing_layers = []
117+
for layer in code_layers + plan_layers:
118+
if not layer.exists():
119+
missing_layers.append(layer.name) # noqa: PERF401
120+
if len(missing_layers) > 0: # noqa: SIM103
121+
# iface.messageBar().pushWarning("", f"Project is missing required layers: {', '.join(missing_layers)}")
122+
return False
123+
return True
124+
125+
def initialize_libraries(self):
126+
if not self.check_required_layers():
127+
return
128+
129+
self.regulation_group_libraries = [
130+
RegulationGroupLibrary.from_config_file(file) for file in regulation_group_library_config_files()
131+
]
132+
self.feature_template_libraries = [
133+
FeatureTemplateLibrary.from_config_file(file, self.regulation_group_libraries)
134+
for file in feature_template_library_config_files()
135+
]
136+
self.new_feature_dock.initialize_feature_template_libraries(self.feature_template_libraries)
117137

118138
def toggle_identify_plan_features(self, activate: bool): # noqa: FBT001
119139
if activate:
@@ -179,7 +199,7 @@ def edit_plan(self):
179199
return
180200
plan_model = PlanLayer.model_from_feature(feature)
181201

182-
attribute_form = PlanAttributeForm(plan_model, self.get_regulation_group_libraries())
202+
attribute_form = PlanAttributeForm(plan_model, self.regulation_group_libraries)
183203
if attribute_form.exec_():
184204
feature = save_plan(attribute_form.model)
185205

@@ -210,7 +230,7 @@ def _plan_geom_digitized(self, feature: QgsFeature):
210230
return
211231

212232
plan_model = Plan(geom=feature.geometry())
213-
attribute_form = PlanAttributeForm(plan_model, self.get_regulation_group_libraries())
233+
attribute_form = PlanAttributeForm(plan_model, self.regulation_group_libraries)
214234
if attribute_form.exec_():
215235
feature = save_plan(attribute_form.model)
216236
plan_to_be_activated = feature["id"]
@@ -237,7 +257,7 @@ def _plan_feature_geom_digitized(self, feature: QgsFeature):
237257

238258
plan_feature.geom = feature.geometry()
239259
attribute_form = PlanFeatureForm(
240-
plan_feature, title, [*self.get_regulation_group_libraries(), regulation_group_library_from_active_plan()]
260+
plan_feature, title, [*self.regulation_group_libraries, regulation_group_library_from_active_plan()]
241261
)
242262
if attribute_form.exec_():
243263
save_plan_feature(attribute_form.model)
@@ -249,7 +269,7 @@ def edit_plan_feature(self, feature: QgsFeature, layer_name: str):
249269
# Geom editing handled with basic QGIS vertex editing?
250270
title = plan_feature.name if plan_feature.name else layer_name
251271
attribute_form = PlanFeatureForm(
252-
plan_feature, title, [*self.get_regulation_group_libraries(), regulation_group_library_from_active_plan()]
272+
plan_feature, title, [*self.regulation_group_libraries, regulation_group_library_from_active_plan()]
253273
)
254274
if attribute_form.exec_():
255275
save_plan_feature(attribute_form.model)

arho_feature_template/gui/docks/new_feature_dock.py

+17-11
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
from __future__ import annotations
2+
13
from importlib import resources
24
from typing import TYPE_CHECKING
35

@@ -21,7 +23,7 @@
2123
class NewFeatureDock(QgsDockWidget, DockClass): # type: ignore
2224
tool_activated = pyqtSignal()
2325

24-
def __init__(self, feature_template_libraries: list):
26+
def __init__(self):
2527
super().__init__()
2628
self.setupUi(self)
2729

@@ -41,11 +43,8 @@ def __init__(self, feature_template_libraries: list):
4143
self.active_feature_type: str | None = None
4244
self.active_feature_layer: str | None = None
4345
self.active_template: PlanFeature | None = None
44-
self.feature_template_libraries: list[FeatureTemplateLibrary] = feature_template_libraries
45-
self.library_selection.addItems([library.name for library in self.feature_template_libraries])
46+
self.feature_template_libraries: list[FeatureTemplateLibrary] | None = None
4647
self.library_selection.currentIndexChanged.connect(self.set_active_feature_template_library)
47-
if len(self.feature_template_libraries) > 0:
48-
self.set_active_feature_template_library(0)
4948

5049
self.template_list.setSelectionMode(self.template_list.SingleSelection)
5150
self.search_box.valueChanged.connect(self.filter_plan_feature_templates)
@@ -54,6 +53,12 @@ def __init__(self, feature_template_libraries: list):
5453
self.template_list.clicked.connect(self.on_template_item_clicked)
5554
self.new_feature_grid.active_feature_type_changed.connect(self.on_active_feature_type_changed)
5655

56+
def initialize_feature_template_libraries(self, feature_template_libraries: list[FeatureTemplateLibrary]):
57+
self.feature_template_libraries = feature_template_libraries
58+
self.library_selection.clear()
59+
self.library_selection.addItems([library.name for library in self.feature_template_libraries])
60+
self.set_active_feature_template_library(0)
61+
5762
def on_active_feature_type_changed(self, feature_name: str, layer_name: str):
5863
self.active_feature_type = feature_name if feature_name else None
5964
self.active_feature_layer = layer_name if layer_name else None
@@ -100,9 +105,10 @@ def deactivate_and_clear_selections(self):
100105
self.template_list.clearSelection()
101106

102107
def set_active_feature_template_library(self, index: int) -> None:
103-
self.template_list.clear()
104-
library = self.feature_template_libraries[index]
105-
for feature_template in library.feature_templates:
106-
item = QListWidgetItem(feature_template.name)
107-
item.setData(Qt.UserRole, feature_template)
108-
self.template_list.addItem(item)
108+
if self.feature_template_libraries and len(self.feature_template_libraries) > 0:
109+
self.template_list.clear()
110+
library = self.feature_template_libraries[index]
111+
for feature_template in library.feature_templates:
112+
item = QListWidgetItem(feature_template.name)
113+
item.setData(Qt.UserRole, feature_template)
114+
self.template_list.addItem(item)

arho_feature_template/plugin.py

+13-9
Original file line numberDiff line numberDiff line change
@@ -127,27 +127,30 @@ def add_action(
127127
def initGui(self) -> None: # noqa N802
128128
# plan_icon_path = os.path.join(PLUGIN_PATH, "resources/icons/city.png") # A placeholder icon
129129
# load_icon_path = os.path.join(PLUGIN_PATH, "resources/icons/folder.png") # A placeholder icon
130+
# icons to consider:
131+
# icon=QgsApplication.getThemeIcon("mActionStreamingDigitize.svg"),
132+
# icon=QgsApplication.getThemeIcon("mIconGeometryCollectionLayer.svg"),
133+
# icon=QgsApplication.getThemeIcon("mActionSharingExport.svg"),
134+
130135
self.plan_manager = PlanManager()
131136

132137
iface.addDockWidget(Qt.RightDockWidgetArea, self.plan_manager.new_feature_dock)
133138
self.plan_manager.new_feature_dock.setUserVisible(False)
134-
135139
self.plan_manager.new_feature_dock.visibilityChanged.connect(self.dock_visibility_changed)
136140

137-
iface.mapCanvas().mapToolSet.connect(self.plan_manager.plan_digitize_map_tool.deactivate)
138-
139141
self.validation_dock = ValidationDock()
140142
iface.addDockWidget(Qt.RightDockWidgetArea, self.validation_dock)
141143
self.validation_dock.setUserVisible(False)
142-
143144
self.validation_dock.visibilityChanged.connect(self.validation_dock_visibility_changed)
144145

145-
# icons to consider:
146-
# icon=QgsApplication.getThemeIcon("mActionStreamingDigitize.svg"),
147-
# icon=QgsApplication.getThemeIcon("mIconGeometryCollectionLayer.svg"),
148-
# icon=QgsApplication.getThemeIcon("mActionSharingExport.svg"),
146+
iface.mapCanvas().mapToolSet.connect(self.plan_manager.plan_digitize_map_tool.deactivate)
147+
148+
# Try initializing the plugin immediately in case the project is already open
149+
self.plan_manager.initialize_from_project()
150+
151+
# (Re)initialize whenever a project is opened
152+
iface.projectRead.connect(self.plan_manager.initialize_from_project)
149153

150-
# Add main plugin action to the toolbar
151154
self.new_land_use_plan_action = self.add_action(
152155
text="Luo kaava",
153156
icon=QgsApplication.getThemeIcon("mActionNewMap.svg"),
@@ -251,6 +254,7 @@ def unload(self) -> None:
251254
# Handle signals
252255
self.plan_manager.new_feature_dock.visibilityChanged.disconnect()
253256
iface.mapCanvas().mapToolSet.disconnect()
257+
iface.projectRead.disconnect()
254258

255259
# Handle actions
256260
for action in self.actions:

arho_feature_template/project/layers/__init__.py

+14-2
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,29 @@
33
from abc import ABC
44
from typing import TYPE_CHECKING, Any, ClassVar, Generator
55

6-
from qgis.core import QgsFeatureRequest
6+
from qgis.core import QgsFeatureRequest, QgsProject, QgsVectorLayer
77

88
from arho_feature_template.utils.project_utils import get_vector_layer_from_project
99

1010
if TYPE_CHECKING:
11-
from qgis.core import QgsFeature, QgsFeatureIterator, QgsVectorLayer
11+
from qgis.core import QgsFeature, QgsFeatureIterator
1212

1313

1414
class AbstractLayer(ABC):
1515
name: ClassVar[str]
1616

17+
@classmethod
18+
def exists(cls) -> bool:
19+
project = QgsProject.instance()
20+
if not project:
21+
return False
22+
23+
layers = project.mapLayersByName(cls.name)
24+
if not layers:
25+
return False
26+
27+
return bool([layer for layer in layers if isinstance(layer, QgsVectorLayer)])
28+
1729
@classmethod
1830
def get_from_project(cls) -> QgsVectorLayer:
1931
return get_vector_layer_from_project(cls.name)

arho_feature_template/resources/libraries/feature_templates/katja_asemakaava.yaml

+2
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ feature_templates:
1616

1717
- name: Asuin-, liike- ja toimistorakennusten alue
1818
layer_name: Aluevaraus
19+
regulation_groups:
20+
- Asuin-, liike- ja toimistorakennusten alue
1921

2022
- name: Sitovan tonttijaon mukainen tontti
2123
layer_name: Osa-alue

0 commit comments

Comments
 (0)