Skip to content

Commit 6ee05c4

Browse files
authored
Merge pull request #1 from danpejobo/reproject
Reproject, access violation, clip, resample
2 parents 9015ebc + 5f01c60 commit 6ee05c4

File tree

10 files changed

+925
-336
lines changed

10 files changed

+925
-336
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -51,11 +51,11 @@ If you want to enable `vulnerability analysis` you will need some information on
5151
| B | Raft | Reinforced concrete | Good |
5252
| B | Betong | B - Armert betong | B - God tilstand |
5353
| B | B - På løsmasser - Hel plate (betong, såle) | - | - |
54-
| C | Strip | Mixed | - |
55-
| C | Grunnmur | C - Tre eller varierende | - |
54+
| C | Strip | Mixed | Medium |
55+
| C | Grunnmur | C - Tre eller varierende | C - Brukbar tilstand |
5656
| C | C - På løsmasser - Stripefundament (heller) | - | - |
57-
| D | Wooden piles | Masonry | Medium |
58-
| D | Trepeler | D - Murstein eller spesiell type | C - Brukbar tilstand |
57+
| D | Wooden piles | Masonry | Bad |
58+
| D | Trepeler | D - Murstein eller spesiell type | D - Dårlig |
5959
| D | D - På løsmasser - Punkt- og trefundamenter (banketter) | - | - |
6060

6161
## Results

__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@
2626
__date__ = '2024-01-17'
2727
__copyright__ = '(C) 2024 by DPE'
2828

29-
__version__ = "1.0.2"
29+
__version__ = "2.0.0"
3030

3131
import sys
3232
from pathlib import Path

algorithms/BegrensSkadeExcavation.py

Lines changed: 164 additions & 105 deletions
Large diffs are not rendered by default.

algorithms/BegrensSkadeImpactMap.py

Lines changed: 122 additions & 62 deletions
Large diffs are not rendered by default.

algorithms/BegrensSkadeTunnel.py

Lines changed: 172 additions & 51 deletions
Large diffs are not rendered by default.

geovita_processing_plugin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030

3131
__revision__ = '$Format:%H$'
3232

33-
from qgis.core import QgsProcessingAlgorithm, QgsApplication
33+
from qgis.core import QgsApplication
3434
from .geovita_processing_plugin_provider import GeovitaProcessingPluginProvider
3535

3636

metadata.txt

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,14 +4,21 @@
44

55
[general]
66
name=Geovita GIS Processing provider
7-
qgisMinimumVersion=3.4
7+
qgisMinimumVersion=3.28
88
qgisMaximumVersion=3.99
99
description=This plugin adds different Geovita custom processing algorithms to QGIS
10-
version=1.0.2
10+
version=2.0.0
1111
author=DPE
1212
1313

14-
about=Geovita plugin provider. This plugin enables different processing algorithms and is under development. Now it implements NGI's REMEDY_GIS_RiskTool to QGIS. File bug-reports here: https://github.com/danpejobo/geovita_processing_plugin/issues
14+
about=Geovita plugin provider. This plugin enables different processing algorithms and is under development.
15+
16+
Now it implements NGI's REMEDY_GIS_RiskTool to QGIS:
17+
* Begrens Skade - Excavation: The Begrens Skade - Excavation algorithm provides a comprehensive analysis of building settlements and risks associated with subsidence and inclination.
18+
* Begrens Skade - ImpactMap: The BegrensSkade ImpactMap alorithm calculates both short-term and long-term settlements that occur due to the establishment of a construction pit (this alg. takes a bit of time to run, open the log and refresh it to see the logged progress).
19+
* Begrens Skade - Tunnel: The BegrensSkade Tunnel alorithm provides a comprehensive analysis of building settlements and risks associated with subsidence and inclination due to tunnel excavation.
20+
21+
- File bug-reports here: https://github.com/danpejobo/geovita_processing_plugin/issues
1522

1623
tracker=https://github.com/danpejobo/geovita_processing_plugin/issues
1724
repository=https://github.com/danpejobo/geovita_processing_plugin
@@ -21,11 +28,19 @@ repository=https://github.com/danpejobo/geovita_processing_plugin
2128

2229
hasProcessingProvider=yes
2330
# Uncomment the following line and add your changelog:
24-
# changelog=24.01.2024 v.1.0.2
25-
- Update a bug in the utility function get_shapefile_as_json_pyqgis, where excavations containing Z/M-dimentions did not process correctly, resulting in an bug in the underlying submodule.
31+
changelog=v.2.0.0 (29.01.2024)
32+
- Add reproject functionality
33+
- Fix: Access violation error due to maipulating GUI from process thread
34+
- Fix: Group/layers in TOC updates correctly
35+
- Fixed impact map clip distance
36+
- Fixed impact map resample
37+
- Minor bugs in all modules
38+
39+
v.1.0.2 (24.01.2024)
40+
- Update a bug in the utility function get_shapefile_as_json_pyqgis, where excavations containing Z/M-dimentions (they are dropped) did not process correctly, resulting in an bug in the underlying submodule.
2641

27-
17.01.2024 v.1.0.1
28-
-Initial "release" of the QGIS implementation of NGI's REMEDY_GIS_RiskTool
42+
v.1.0.1 (17.01.2024)
43+
- Initial "release" of the QGIS implementation of NGI's REMEDY_GIS_RiskTool
2944

3045
# Tags are comma separated with spaces allowed
3146
tags=analysis, attribute edit, csv, database, delimitation, digitizing, export, import, point, processing, table, vector, NGI, BegrensSkade, remedy, building, settlements, tunnel, impactmap, excavation, risk, dtm

pb_tool.cfg

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ name: geovita_processing_plugin
4444
# Full path to where you want your plugin directory copied. If empty,
4545
# the QGIS default path will be used. Don't include the plugin name in
4646
# the path.
47-
plugin_path:
47+
plugin_path: C:\Users\dpe\AppData\Roaming\QGIS\QGIS3\profiles\daniel\python\plugins
4848

4949
[files]
5050
# Python files that should be deployed with the plugin

utils/AddLayersTask.py

Lines changed: 219 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,219 @@
1+
from qgis.PyQt.QtCore import pyqtSignal
2+
from qgis.core import (QgsTask,
3+
QgsProject,
4+
QgsRasterLayer,
5+
QgsVectorLayer,
6+
QgsLayerTreeGroup,
7+
QgsLayerTreeLayer)
8+
9+
from pathlib import Path
10+
from datetime import datetime
11+
12+
from utils import logger
13+
14+
class AddLayersTask(QgsTask):
15+
"""
16+
A QGIS task for adding layers (vector or raster) to the QGIS interface.
17+
This task handles the addition of layers in a background thread and updates
18+
the GUI in the main thread upon completion.
19+
20+
Attributes:
21+
taskCompleted (pyqtSignal): Signal emitted when the task is completed.
22+
layers_info (list[tuple[str, str, str]]): List of tuples containing layer information (name, path, style).
23+
group_name (str): The name of the group to which layers are related.
24+
styles_dir_path (Path): The directory path where style files are located.
25+
logger (logging.Logger): Logger for logging messages.
26+
prepared_layers (list[tuple[str, str, str, str]]): Prepared layer information for adding to QGIS.
27+
completed (bool): Flag indicating whether the task has completed.
28+
"""
29+
30+
taskCompleted = pyqtSignal(bool)
31+
32+
def __init__(self, description: str, layers_info: list[tuple[str, str, str]], group_name: str, styles_dir_path: Path, logger: logger.CustomLogger) -> None:
33+
"""
34+
Initializes the AddLayersTask.
35+
36+
Args:
37+
description (str): The description of the task.
38+
layers_info (list): A list of tuples containing layer information (name, path, style).
39+
group_name (str): The name of the group to which layers are related.
40+
styles_dir_path (Path): The directory path where style files are located.
41+
logger (Logger): Logger for logging messages.
42+
"""
43+
super().__init__(description, QgsTask.CanCancel)
44+
self.layers_info = layers_info
45+
self.group_name = group_name
46+
self.styles_dir_path = styles_dir_path
47+
self.logger = logger
48+
self.prepared_layers = [] # Initialize prepared layers list
49+
self.completed = False
50+
51+
def run(self) -> bool:
52+
"""
53+
Runs the task. The method that runs when the task is started. Used for non-GUI operations
54+
such as data preparation and validation.
55+
56+
Returns:
57+
bool: True if preparation is successful, False otherwise.
58+
"""
59+
for layer_name, layer_path, style_name in self.layers_info:
60+
# Validate file paths
61+
if not Path(layer_path).is_file():
62+
self.logger.error(f"@AddLayersTask-run()@ - File not found: {layer_path}")
63+
return False
64+
65+
# Generate a timestamp string
66+
timestamp = datetime.now().strftime("_%Y%m%d_%H:%M")
67+
modified_layer_name = f"{layer_name}{timestamp}"
68+
69+
# Determine layer type without loading it
70+
# add supported extensions
71+
raster_ext = ('.tif', '.tiff')
72+
# Determine layer type and create the layer object
73+
if layer_path.endswith(raster_ext):
74+
layer = QgsRasterLayer(layer_path, modified_layer_name)
75+
else:
76+
layer = QgsVectorLayer(layer_path, modified_layer_name, 'ogr')
77+
78+
if not layer.isValid():
79+
self.logger.error(f"@AddLayersTask-run()@ - Failed to load layer: {layer_path}")
80+
return False
81+
82+
# Store the prepared layer along with its style name
83+
self.prepared_layers.append((layer, style_name))
84+
self.logger.info(f"@AddLayersTask-run()@ - Layer prepared for addition: {modified_layer_name}")
85+
return True
86+
87+
def finished(self, success: bool) -> None:
88+
"""
89+
The method that runs when the task is finished. It is executed in the main thread,
90+
making it safe for GUI operations like adding layers and refreshing the layer tree.
91+
92+
Args:
93+
success (bool): Indicates whether the task preparation was successful.
94+
"""
95+
if success:
96+
# GUI operations are performed here
97+
for layer, style_name in self.prepared_layers:
98+
style_path = str(self.styles_dir_path / style_name)
99+
if not self.add_layer_to_qgis(layer, style_path, self.group_name, self.logger):
100+
self.logger.error(f"Failed to add layer {layer.name()}")
101+
self.taskCompleted.emit(False)
102+
return
103+
104+
self.completed = True
105+
self.taskCompleted.emit(success)
106+
107+
def add_layer_to_qgis(self, layer: QgsRasterLayer or QgsVectorLayer, style_path: str, group_name: str = None, logger: logger.CustomLogger = None) -> bool:
108+
"""
109+
Adds a prepared layer to QGIS with a specified style. Optionally adds it to a specified group.
110+
111+
Args:
112+
layer (QgsRasterLayer or QgsVectorLayer): The prepared layer to be added.
113+
style_path (str): Path to the QML style file.
114+
group_name (str, optional): Name of the group to add the layer to. If None, adds without a group.
115+
logger (logging.Logger, optional): Logger for logging messages.
116+
117+
Returns:
118+
bool: True if the layer is added successfully, False otherwise.
119+
"""
120+
# Apply the style and trigger refresh of layer
121+
if Path(style_path).is_file():
122+
layer.loadNamedStyle(style_path)
123+
layer.triggerRepaint()
124+
125+
# Add the layer to the specified group or directly to the project
126+
if group_name:
127+
self._add_layer_to_group(layer, group_name, logger)
128+
else:
129+
QgsProject.instance().addMapLayer(layer, True) # True - add layer directly to the root
130+
if logger:
131+
logger.info(f"@add_layer_to_qgis@ - Layer '{layer.name()}' added to QGIS.")
132+
133+
return True
134+
135+
def _add_layer_to_group(self, layer: QgsRasterLayer or QgsVectorLayer, group_name: str, logger: logger.CustomLogger = None) -> None:
136+
"""
137+
Adds the specified layer to a group in the QGIS project.
138+
139+
Args:
140+
layer (QgsRasterLayer or QgsVectorLayer): The layer to add.
141+
group_name (str): The name of the group.
142+
logger (logging.Logger): Logger for logging messages.
143+
"""
144+
root = QgsProject.instance().layerTreeRoot()
145+
group = root.findGroup(group_name)
146+
if not group:
147+
group = QgsLayerTreeGroup(group_name)
148+
root.insertChildNode(0, group)
149+
if logger:
150+
logger.debug(f"@_add_layer_to_group@ - Created new group '{group_name}' and added it to the top of the Layer Tree.")
151+
else:
152+
if logger:
153+
logger.debug(f"@_add_layer_to_group@ - Found existing group '{group_name}'.")
154+
155+
QgsProject.instance().addMapLayer(layer, False)
156+
node_layer = QgsLayerTreeLayer(layer)
157+
group.addChildNode(node_layer)
158+
# Log layer addition
159+
if logger:
160+
logger.debug(f"@_add_layer_to_group@ - Added layer '{layer.name()}' to group '{group_name}'.")
161+
162+
163+
# The Original code that worked below
164+
165+
# def add_layer_to_qgis(self, layer_path: str, layer_name: str, style_path: str, group_name: str = None, logger: logger.CustomLogger = None) -> bool:
166+
# """
167+
# Adds a layer (vector or raster) to QGIS with a specified style, and optionally adds it to a specified group.
168+
169+
# Parameters:
170+
# layer_path (str): Path to the layer file.
171+
# layer_name (str): Name for the layer in QGIS.
172+
# style_path (str): Path to the QML style file.
173+
# group_name (str, optional): Name of the group to add the layer to. If None, the layer is added without a group.
174+
# logger (logging.Logger, optional): Logger for logging messages.
175+
176+
# Returns:
177+
# bool: True if the layer is added successfully, False otherwise.
178+
# """
179+
180+
# # Determine layer type (raster or vector) based on file extension
181+
# if layer_path.endswith('.tif') or layer_path.endswith('.tiff'):
182+
# layer = QgsRasterLayer(layer_path, f'{layer_name}')
183+
# logger.info(f"Loaded RASTER layer to style it: {layer.name()}")
184+
# else:
185+
# layer = QgsVectorLayer(layer_path, f'{layer_name}', 'ogr')
186+
# logger.info(f"Loaded VECTOR layer to style it: {layer.name()}")
187+
188+
# if not layer.isValid():
189+
# if logger:
190+
# logger.debug(f"Failed to load layer: {layer_path}")
191+
# return False
192+
193+
# # Apply the style
194+
# layer.loadNamedStyle(style_path)
195+
# logger.info(f"Loaded style on this path: {style_path}")
196+
# layer.triggerRepaint()
197+
198+
# # Get the root of the layer tree
199+
# root = QgsProject.instance().layerTreeRoot()
200+
201+
# # Add layer to group if group_name is specified
202+
# if group_name:
203+
# group = root.findGroup(group_name)
204+
# if not group:
205+
# #group = root.insertGroup(0, group_name)
206+
# group = QgsLayerTreeGroup(group_name)
207+
# root.insertChildNode(0, group)
208+
# logger.info(f"Created group node with name: {group.name()}")
209+
210+
# QgsProject.instance().addMapLayer(layer, False) # False means do not add to the layer tree root
211+
# node_layer = QgsLayerTreeLayer(layer)
212+
# group.insertChildNode(1, node_layer)
213+
# #group.addLayer(layer)
214+
# logger.info(f"Added {layer} to group: {group.name()}")
215+
# else:
216+
# QgsProject.instance().addMapLayer(layer, True) # Add layer directly to the layer tree
217+
# logger.info(f"No group prensen. Directly add layer: {layer}")
218+
219+
# return True

0 commit comments

Comments
 (0)