Skip to content

Commit 1adde0c

Browse files
committed
[toolbar] Boundaries are restored if an error occurred when executing the build_boundaries function and include tests to check build boundaries function with invalid data
1 parent 34a5e0f commit 1adde0c

16 files changed

+628
-473
lines changed

asistente_ladm_col/asistente_ladm_col_plugin.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1008,7 +1008,7 @@ def show_field_data_capture_dockwidget(self, allocate=True):
10081008
@_survey_model_required
10091009
@_grass_required
10101010
def call_explode_boundaries(self, *args):
1011-
self.toolbar.build_boundary(self.get_db_connection())
1011+
self.toolbar.build_boundaries(self.get_db_connection())
10121012

10131013
@_validate_if_wizard_is_open
10141014
@_qgis_model_baker_required

asistente_ladm_col/core/app_core_interface.py

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1152,7 +1152,7 @@ def copy_csv_to_db(self, csv_layer, db, target_layer_name):
11521152
return True
11531153

11541154
@_activate_processing_plugin
1155-
def run_etl_model_in_backgroud_mode(self, db, input_layer, ladm_col_layer_name, force_reprojection=True):
1155+
def run_etl_model_in_backgroud_mode(self, db, input_layer, ladm_col_layer_name):
11561156
output_layer = self.get_layer(db, ladm_col_layer_name, load=True)
11571157
start_feature_count = output_layer.featureCount()
11581158

@@ -1166,10 +1166,9 @@ def run_etl_model_in_backgroud_mode(self, db, input_layer, ladm_col_layer_name,
11661166
return False
11671167

11681168
model_name = ETL_MODEL_NAME
1169-
if force_reprojection:
1170-
if get_crs_authid(input_layer.crs()) != DEFAULT_SRS_AUTHID and output_layer.isSpatial():
1171-
model_name = ETL_MODEL_WITH_REPROJECTION_NAME # We need to reproject the layer first (transparently)
1172-
self.logger.info(__name__, "Using ETL model with reprojection since source layer's CRS is not {}!".format(DEFAULT_SRS_AUTHID))
1169+
if get_crs_authid(input_layer.crs()) != DEFAULT_SRS_AUTHID and output_layer.isSpatial():
1170+
model_name = ETL_MODEL_WITH_REPROJECTION_NAME # We need to reproject the layer first (transparently)
1171+
self.logger.info(__name__, "Using ETL model with reprojection since source layer's CRS is not {}!".format(DEFAULT_SRS_AUTHID))
11731172

11741173
model = QgsApplication.processingRegistry().algorithmById(model_name)
11751174
if model:
@@ -1186,7 +1185,7 @@ def run_etl_model_in_backgroud_mode(self, db, input_layer, ladm_col_layer_name,
11861185

11871186
if not finish_feature_count:
11881187
self.logger.warning(__name__, QCoreApplication.translate("AppCoreInterface",
1189-
"The output of the ETL-model has no features! Most likely, the CSV does not have the required structure."))
1188+
"The output of the ETL-model has no features! Most likely, the layer does not have the required structure."))
11901189
return finish_feature_count > start_feature_count
11911190
else:
11921191
self.logger.info_msg(__name__, QCoreApplication.translate("AppCoreInterface",

asistente_ladm_col/gui/toolbar.py

Lines changed: 50 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,14 @@ def __init__(self, iface):
4545
self.app = AppInterface()
4646
self.geometry = GeometryUtils()
4747

48-
def build_boundary(self, db):
48+
def build_boundaries(self, db, skip_selection=False):
49+
"""
50+
Builds the boundaries correctly and update boundary layer
51+
52+
:param db: db connection instance
53+
:param skip_selection: Boolean True if we omit the boundaries selected by the user, False if we validate the user's selection.
54+
:return:
55+
"""
4956
QgsProject.instance().setAutoTransaction(False)
5057
use_selection = True
5158

@@ -58,6 +65,9 @@ def build_boundary(self, db):
5865
}
5966
self.app.core.get_layers(db, layers, load=True)
6067

68+
if skip_selection:
69+
layers[db.names.LC_BOUNDARY_T].selectAll()
70+
6171
if layers[db.names.LC_BOUNDARY_T].selectedFeatureCount() == 0:
6272
reply = QMessageBox.question(None,
6373
QCoreApplication.translate("ToolBar", "Continue?"),
@@ -72,12 +82,16 @@ def build_boundary(self, db):
7282

7383
boundary_t_ids = list()
7484
if use_selection:
85+
copy_boundary_layer = processing.run("native:saveselectedfeatures",{'INPUT': layers[db.names.LC_BOUNDARY_T], 'OUTPUT': 'memory:'})['OUTPUT']
7586
boundary_t_ids = QgsVectorLayerUtils.getValues(layers[db.names.LC_BOUNDARY_T], db.names.T_ID_F, selectedOnly=True)[0]
76-
num_boundaries = layers[db.names.LC_BOUNDARY_T].selectedFeatureCount()
7787
else:
88+
copy_boundary_layer = self.app.core.get_layer_copy(layers[db.names.LC_BOUNDARY_T])
7889
boundary_t_ids = QgsVectorLayerUtils.getValues(layers[db.names.LC_BOUNDARY_T], db.names.T_ID_F)[0]
7990
layers[db.names.LC_BOUNDARY_T].selectAll()
80-
num_boundaries = layers[db.names.LC_BOUNDARY_T].featureCount()
91+
92+
boundaries_count = layers[db.names.LC_BOUNDARY_T].featureCount()
93+
selected_boundaries_count = layers[db.names.LC_BOUNDARY_T].selectedFeatureCount()
94+
boundary_t_ili_tids = QgsVectorLayerUtils.getValues(layers[db.names.LC_BOUNDARY_T], db.names.T_ILI_TID_F)[0]
8195

8296
if boundary_t_ids:
8397
boundary_topology_relation = {
@@ -113,15 +127,41 @@ def build_boundary(self, db):
113127
selected_boundaries_layer = processing.run("native:saveselectedfeatures", {'INPUT': layers[db.names.LC_BOUNDARY_T], 'OUTPUT': 'TEMPORARY_OUTPUT'})['OUTPUT']
114128
build_boundaries_layer = self.geometry.build_boundaries(selected_boundaries_layer)
115129

116-
with edit(layers[db.names.LC_BOUNDARY_T]):
117-
# Delete selected features as they will be imported again from a newly created layer after processed
118-
layers[db.names.LC_BOUNDARY_T].deleteSelectedFeatures()
130+
build_boundaries_count = build_boundaries_layer.featureCount()
131+
expected_boundaries_count = boundaries_count - selected_boundaries_count + build_boundaries_count
132+
133+
# Build boundaries should have generated at least one boundary.
134+
if build_boundaries_count > 0:
135+
with edit(layers[db.names.LC_BOUNDARY_T]):
136+
# Delete selected features as they will be imported again from a newly created layer after processed
137+
layers[db.names.LC_BOUNDARY_T].deleteSelectedFeatures()
138+
139+
# Bring back the features we deleted before, but this time, with the boundaries fixed
140+
self.app.core.run_etl_model_in_backgroud_mode(db, build_boundaries_layer, db.names.LC_BOUNDARY_T)
141+
142+
# check if features were inserted successfully
143+
if layers[db.names.LC_BOUNDARY_T].featureCount() == expected_boundaries_count:
144+
self.logger.info_msg(__name__, QCoreApplication.translate("ToolBar",
145+
"{} feature(s) was(were) analyzed generating {} boundary(ies)!").format(selected_boundaries_count, build_boundaries_layer.featureCount()))
119146

120-
# Bring back the features we deleted before, but this time, with the boundaries fixed
121-
self.app.core.run_etl_model_in_backgroud_mode(db, build_boundaries_layer, db.names.LC_BOUNDARY_T, False)
147+
else:
148+
if layers[db.names.LC_BOUNDARY_T].featureCount() != boundaries_count - selected_boundaries_count:
149+
# Clean layer because wrong data could have been inserted previously
150+
expr = "{} NOT IN ('{}')".format(db.names.T_ILI_TID_F, "','".join([t_ili_tid for t_ili_tid in boundary_t_ili_tids]))
151+
layers[db.names.LC_BOUNDARY_T].selectByExpression(expr)
152+
153+
with edit(layers[db.names.LC_BOUNDARY_T]):
154+
layers[db.names.LC_BOUNDARY_T].deleteSelectedFeatures()
155+
156+
# the previously deleted boundaries are restored because an error occurred when trying to insert the building boundaries
157+
self.app.core.run_etl_model_in_backgroud_mode(db, copy_boundary_layer, db.names.LC_BOUNDARY_T)
158+
159+
self.logger.warning_msg(__name__, QCoreApplication.translate("ToolBar",
160+
"An error occurred when trying to build the boundary(ies). No changes are made!"))
161+
else:
162+
self.logger.warning_msg(__name__, QCoreApplication.translate("ToolBar",
163+
"An error occurred when trying to build the boundary(ies). No changes are made!"))
122164

123-
self.logger.info_msg(__name__, QCoreApplication.translate("ToolBar",
124-
"{} feature(s) was(were) analyzed generating {} boundary(ies)!").format(num_boundaries, build_boundaries_layer.featureCount()))
125165
self.iface.mapCanvas().refresh()
126166

127167
# topology tables are recalculated with the new boundaries

asistente_ladm_col/lib/geometry.py

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -720,8 +720,19 @@ def build_boundaries(self, boundary_layer):
720720
"""
721721
:return: QgsVectorLayer
722722
"""
723-
params = {'boundaries': boundary_layer, 'native:refactorfields_2:built_boundaries': 'TEMPORARY_OUTPUT'}
724-
return processing.run("model:Build_Boundaries", params)['native:refactorfields_2:built_boundaries']
723+
feedback = CustomFeedbackWithErrors()
724+
try:
725+
params = {'boundaries': boundary_layer, 'native:refactorfields_2:built_boundaries': 'TEMPORARY_OUTPUT'}
726+
build_boundary_layer = processing.run("model:Build_Boundaries", params, feedback=feedback)['native:refactorfields_2:built_boundaries']
727+
except QgsProcessingException as e:
728+
self.logger.warning(__name__, QCoreApplication.translate("Geometry",
729+
"Error running the model to build boundaries. Details: {}".format(feedback.msg)))
730+
build_boundary_layer = QgsVectorLayer("LineString?crs={}".format(get_crs_authid(boundary_layer.sourceCrs())), "build_boundaries", "memory")
731+
732+
# For CTM12 the output layer remains without projection. The projection of the input layer is assigned
733+
if not build_boundary_layer.crs().authid():
734+
build_boundary_layer.setCrs(boundary_layer.crs())
735+
return build_boundary_layer
725736

726737
@staticmethod
727738
def get_relationships_among_polygons(input_layer, intersect_layer, key=None, attrs=list(), get_geometry=False):

0 commit comments

Comments
 (0)