Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Suodata kaikki kaavan liittyvät tasot #84

Merged
merged 7 commits into from
Dec 4, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
36 changes: 6 additions & 30 deletions arho_feature_template/core/feature_template_library.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@

import logging
from collections import defaultdict
from typing import TYPE_CHECKING

from qgis.core import QgsFeature, QgsProject, QgsVectorLayer
from qgis.gui import QgsMapToolDigitizeFeature
from qgis.PyQt.QtCore import QItemSelectionModel
from qgis.PyQt.QtGui import QStandardItem, QStandardItemModel
Expand All @@ -16,40 +16,16 @@
TemplateSyntaxError,
parse_template_library_config,
)
from arho_feature_template.exceptions import LayerNotFoundError, LayerNotVectorTypeError
from arho_feature_template.gui.template_attribute_form import TemplateAttributeForm
from arho_feature_template.gui.template_dock import TemplateLibraryDock
from arho_feature_template.resources.template_libraries import library_config_files
from arho_feature_template.utils.project_utils import get_layer_from_project

logger = logging.getLogger(__name__)


class LayerNotFoundError(Exception):
def __init__(self, layer_name: str):
super().__init__(f"Layer {layer_name} not found")


class LayerNotVectorTypeError(Exception):
def __init__(self, layer_name: str):
super().__init__(f"Layer {layer_name} is not a vector layer")

if TYPE_CHECKING:
from qgis.core import QgsFeature, QgsVectorLayer

def get_layer_from_project(layer_name: str) -> QgsVectorLayer:
project = QgsProject.instance()
if not project:
raise LayerNotFoundError(layer_name)

layers = project.mapLayersByName(layer_name)
if not layers:
raise LayerNotFoundError(layer_name)

if len(layers) > 1:
logger.warning("Multiple layers with the same name found. Using the first one.")

layer = layers[0]
if not isinstance(layer, QgsVectorLayer):
raise LayerNotVectorTypeError(layer_name)

return layer
logger = logging.getLogger(__name__)


class TemplateItem(QStandardItem):
Expand Down
116 changes: 97 additions & 19 deletions arho_feature_template/core/plan_manager.py
Original file line number Diff line number Diff line change
@@ -1,18 +1,83 @@
from __future__ import annotations

import json
import logging
from string import Template
from textwrap import dedent

from qgis.core import QgsExpressionContextUtils, QgsProject, QgsVectorLayer
from qgis.PyQt.QtWidgets import QDialog, QMessageBox
from qgis.utils import iface

from arho_feature_template.core.lambda_service import LambdaService
from arho_feature_template.core.update_plan import LandUsePlan, update_selected_plan
from arho_feature_template.gui.load_plan_dialog import LoadPlanDialog
from arho_feature_template.gui.serialize_plan import SerializePlan
from arho_feature_template.utils.db_utils import get_existing_database_connection_names
from arho_feature_template.utils.misc_utils import get_active_plan_id, get_layer_by_name, handle_unsaved_changes

logger = logging.getLogger(__name__)

LAYER_NAME_PLAN = "Kaava"
LAYER_NAME_LAND_USE_POINT = "Maankäytön kohteet"
LAYER_NAME_OTHER_POINT = "Muut pisteet"
LAYER_NAME_LINE = "Viivat"
LAYER_NAME_OTHER_AREA = "Osa-alue"
LAYER_NAME_LAND_USE_AREA = "Aluevaraus"
LAYER_NAME_PLAN_REGULATION_GROUP = "Kaavamääräysryhmät"
LAYER_NAME_PLAN_REGULATION = "Kaavamääräys"
LAYER_NAME_PLAN_PROPOSITION = "Kaavasuositus"
LAYER_NAME_DOCUMENT = "Asiakirjat"
LAYER_NAME_SOURCE_DATA = "Lähtötietoaineistot"
LAYER_NAME_REGULATION_GROUP_ASSOCIATION = "Kaavamääräysryhmien assosiaatiot"

PLAN_FILTER_TEMPLATES = {
LAYER_NAME_PLAN: Template("id = '$plan_id'"),
LAYER_NAME_LAND_USE_POINT: Template("plan_id = '$plan_id'"),
LAYER_NAME_OTHER_POINT: Template("plan_id = '$plan_id'"),
LAYER_NAME_LINE: Template("plan_id = '$plan_id'"),
LAYER_NAME_LAND_USE_AREA: Template("plan_id = '$plan_id'"),
LAYER_NAME_OTHER_AREA: Template("plan_id = '$plan_id'"),
LAYER_NAME_PLAN_REGULATION_GROUP: Template("plan_id = '$plan_id'"),
LAYER_NAME_REGULATION_GROUP_ASSOCIATION: Template(
dedent(
"""\
EXISTS (
SELECT 1
FROM hame.plan_regulation_group prg
WHERE
hame.regulation_group_association.plan_regulation_group_id = prg.id
AND prg.plan_id = '$plan_id'
)"""
)
),
LAYER_NAME_PLAN_REGULATION: Template(
dedent(
"""\
EXISTS (
SELECT 1
FROM hame.plan_regulation_group prg
WHERE
hame.plan_regulation.plan_regulation_group_id = prg.id
AND prg.plan_id = '$plan_id'
)"""
)
),
LAYER_NAME_PLAN_PROPOSITION: Template(
dedent(
"""\
EXISTS (
SELECT 1
FROM hame.plan_regulation_group rg
WHERE
hame.plan_proposition.plan_regulation_group_id = rg.id
AND rg.plan_id = '$plan_id'
)"""
)
),
LAYER_NAME_DOCUMENT: Template("plan_id = '$plan_id'"),
LAYER_NAME_SOURCE_DATA: Template("plan_id = '$plan_id'"),
}


class PlanManager:
def __init__(self):
Expand All @@ -24,11 +89,11 @@ def add_new_plan(self):
if not handle_unsaved_changes():
return

plan_layer = get_layer_by_name("Kaava")
self.clear_all_filters()
plan_layer = get_layer_by_name(LAYER_NAME_PLAN)

if not plan_layer:
return
self.set_active_plan(None)

if not plan_layer.isEditable():
plan_layer.startEditing()
Expand All @@ -37,17 +102,16 @@ def add_new_plan(self):
iface.actionAddFeature().trigger()

# Connect the featureAdded signal to a callback method
plan_layer.featureAdded.connect(self.feature_added)
plan_layer.featureAdded.connect(self._feature_added)

def feature_added(self):
def _feature_added(self):
"""Callback for when a new feature is added to the Kaava layer."""

plan_layer = get_layer_by_name("Kaava")
plan_layer = get_layer_by_name(LAYER_NAME_PLAN)
if not plan_layer:
return

# Disconnect the signal to avoid repeated triggers
plan_layer.featureAdded.disconnect()
plan_layer.featureAdded.disconnect(self._feature_added)

feature_ids_before_commit = plan_layer.allFeatureIds()

Expand All @@ -60,18 +124,40 @@ def feature_added(self):
return

feature_ids_after_commit = plan_layer.allFeatureIds()
new_feature_id = next((fid for fid in feature_ids_after_commit if fid not in feature_ids_before_commit), None)
new_feature_id = next(
(fid for fid in feature_ids_after_commit if fid not in feature_ids_before_commit),
None,
)

if new_feature_id is not None:
new_feature = plan_layer.getFeature(new_feature_id)
if new_feature.isValid():
feature_id_value = new_feature["id"]
update_selected_plan(LandUsePlan(feature_id_value))
self.set_active_plan(feature_id_value)
else:
iface.messageBar().pushMessage("Error", "Invalid feature retrieved.", level=3)
else:
iface.messageBar().pushMessage("Error", "No new feature was added.", level=3)

def set_active_plan(self, plan_id: str | None):
"""Update the project layers based on the selected land use plan."""
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), "active_plan_id", plan_id)

for layer_name, filter_template in PLAN_FILTER_TEMPLATES.items():
"""Set a filter for the given vector layer."""
filter_expression = filter_template.substitute(plan_id=plan_id) if plan_id else None
layer = get_layer_by_name(layer_name)
if not layer:
logger.warning("Layer %s not found", layer_name)
continue
result = layer.setSubsetString(filter_expression)
if result is False:
iface.messageBar().pushMessage(
"Error",
f"Failed to filter layer {layer_name} with query {filter_expression}",
level=3,
)

def load_land_use_plan(self):
"""Load an existing land use plan using a dialog selection."""
connections = get_existing_database_connection_names()
Expand All @@ -93,15 +179,7 @@ def load_land_use_plan(self):
QMessageBox.critical(None, "Error", "No plan was selected.")
return

plan = LandUsePlan(selected_plan_id)
update_selected_plan(plan)

def clear_all_filters(self):
"""Clear active_plan_id and filters for all vector layers in the project."""
QgsExpressionContextUtils.setProjectVariable(QgsProject.instance(), "active_plan_id", None)
for layer in QgsProject.instance().mapLayers().values():
if isinstance(layer, QgsVectorLayer):
layer.setSubsetString("")
self.set_active_plan(selected_plan_id)

def commit_all_editable_layers(self):
"""Commit all changes in any editable layers."""
Expand Down
63 changes: 0 additions & 63 deletions arho_feature_template/core/update_plan.py

This file was deleted.

8 changes: 8 additions & 0 deletions arho_feature_template/exceptions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
class LayerNotFoundError(Exception):
def __init__(self, layer_name: str):
super().__init__(f"Layer {layer_name} not found")


class LayerNotVectorTypeError(Exception):
def __init__(self, layer_name: str):
super().__init__(f"Layer {layer_name} is not a vector layer")
26 changes: 26 additions & 0 deletions arho_feature_template/utils/project_utils.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import logging

from qgis.core import QgsProject, QgsVectorLayer

from arho_feature_template.exceptions import LayerNotFoundError, LayerNotVectorTypeError

logger = logging.getLogger(__name__)


def get_layer_from_project(layer_name: str) -> QgsVectorLayer:
project = QgsProject.instance()
if not project:
raise LayerNotFoundError(layer_name)

layers = project.mapLayersByName(layer_name)
if not layers:
raise LayerNotFoundError(layer_name)

if len(layers) > 1:
logger.warning("Multiple layers with the same name found. Using the first one.")

layer = layers[0]
if not isinstance(layer, QgsVectorLayer):
raise LayerNotVectorTypeError(layer_name)

return layer
Binary file added qgisprojekti.qgz
Binary file not shown.
Loading