Skip to content

Commit

Permalink
[toolbar] Boundaries are restored if an error occurred when executing…
Browse files Browse the repository at this point in the history
… the build_boundaries function and include tests to check build boundaries function with invalid data
  • Loading branch information
lacardonap committed Sep 2, 2021
1 parent 12c3bfa commit a6ea115
Show file tree
Hide file tree
Showing 16 changed files with 628 additions and 473 deletions.
2 changes: 1 addition & 1 deletion asistente_ladm_col/asistente_ladm_col_plugin.py
Original file line number Diff line number Diff line change
Expand Up @@ -1002,7 +1002,7 @@ def show_field_data_capture_dockwidget(self, allocate=True):
@_survey_model_required
@_grass_required
def call_explode_boundaries(self, *args):
self.toolbar.build_boundary(self.get_db_connection())
self.toolbar.build_boundaries(self.get_db_connection())

@_validate_if_wizard_is_open
@_qgis_model_baker_required
Expand Down
11 changes: 5 additions & 6 deletions asistente_ladm_col/core/app_core_interface.py
Original file line number Diff line number Diff line change
Expand Up @@ -1151,7 +1151,7 @@ def copy_csv_to_db(self, csv_layer, db, target_layer_name):
return True

@_activate_processing_plugin
def run_etl_model_in_backgroud_mode(self, db, input_layer, ladm_col_layer_name, force_reprojection=True):
def run_etl_model_in_backgroud_mode(self, db, input_layer, ladm_col_layer_name):
output_layer = self.get_layer(db, ladm_col_layer_name, load=True)
start_feature_count = output_layer.featureCount()

Expand All @@ -1165,10 +1165,9 @@ def run_etl_model_in_backgroud_mode(self, db, input_layer, ladm_col_layer_name,
return False

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

model = QgsApplication.processingRegistry().algorithmById(model_name)
if model:
Expand All @@ -1185,7 +1184,7 @@ def run_etl_model_in_backgroud_mode(self, db, input_layer, ladm_col_layer_name,

if not finish_feature_count:
self.logger.warning(__name__, QCoreApplication.translate("AppCoreInterface",
"The output of the ETL-model has no features! Most likely, the CSV does not have the required structure."))
"The output of the ETL-model has no features! Most likely, the layer does not have the required structure."))
return finish_feature_count > start_feature_count
else:
self.logger.info_msg(__name__, QCoreApplication.translate("AppCoreInterface",
Expand Down
60 changes: 50 additions & 10 deletions asistente_ladm_col/gui/toolbar.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,14 @@ def __init__(self, iface):
self.app = AppInterface()
self.geometry = GeometryUtils()

def build_boundary(self, db):
def build_boundaries(self, db, skip_selection=False):
"""
Builds the boundaries correctly and update boundary layer
:param db: db connection instance
:param skip_selection: Boolean True if we omit the boundaries selected by the user, False if we validate the user's selection.
:return:
"""
QgsProject.instance().setAutoTransaction(False)
use_selection = True

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

if skip_selection:
layers[db.names.LC_BOUNDARY_T].selectAll()

if layers[db.names.LC_BOUNDARY_T].selectedFeatureCount() == 0:
reply = QMessageBox.question(None,
QCoreApplication.translate("ToolBar", "Continue?"),
Expand All @@ -72,12 +82,16 @@ def build_boundary(self, db):

boundary_t_ids = list()
if use_selection:
copy_boundary_layer = processing.run("native:saveselectedfeatures",{'INPUT': layers[db.names.LC_BOUNDARY_T], 'OUTPUT': 'memory:'})['OUTPUT']
boundary_t_ids = QgsVectorLayerUtils.getValues(layers[db.names.LC_BOUNDARY_T], db.names.T_ID_F, selectedOnly=True)[0]
num_boundaries = layers[db.names.LC_BOUNDARY_T].selectedFeatureCount()
else:
copy_boundary_layer = self.app.core.get_layer_copy(layers[db.names.LC_BOUNDARY_T])
boundary_t_ids = QgsVectorLayerUtils.getValues(layers[db.names.LC_BOUNDARY_T], db.names.T_ID_F)[0]
layers[db.names.LC_BOUNDARY_T].selectAll()
num_boundaries = layers[db.names.LC_BOUNDARY_T].featureCount()

boundaries_count = layers[db.names.LC_BOUNDARY_T].featureCount()
selected_boundaries_count = layers[db.names.LC_BOUNDARY_T].selectedFeatureCount()
boundary_t_ili_tids = QgsVectorLayerUtils.getValues(layers[db.names.LC_BOUNDARY_T], db.names.T_ILI_TID_F)[0]

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

with edit(layers[db.names.LC_BOUNDARY_T]):
# Delete selected features as they will be imported again from a newly created layer after processed
layers[db.names.LC_BOUNDARY_T].deleteSelectedFeatures()
build_boundaries_count = build_boundaries_layer.featureCount()
expected_boundaries_count = boundaries_count - selected_boundaries_count + build_boundaries_count

# Build boundaries should have generated at least one boundary.
if build_boundaries_count > 0:
with edit(layers[db.names.LC_BOUNDARY_T]):
# Delete selected features as they will be imported again from a newly created layer after processed
layers[db.names.LC_BOUNDARY_T].deleteSelectedFeatures()

# Bring back the features we deleted before, but this time, with the boundaries fixed
self.app.core.run_etl_model_in_backgroud_mode(db, build_boundaries_layer, db.names.LC_BOUNDARY_T)

# check if features were inserted successfully
if layers[db.names.LC_BOUNDARY_T].featureCount() == expected_boundaries_count:
self.logger.info_msg(__name__, QCoreApplication.translate("ToolBar",
"{} feature(s) was(were) analyzed generating {} boundary(ies)!").format(selected_boundaries_count, build_boundaries_layer.featureCount()))

# Bring back the features we deleted before, but this time, with the boundaries fixed
self.app.core.run_etl_model_in_backgroud_mode(db, build_boundaries_layer, db.names.LC_BOUNDARY_T, False)
else:
if layers[db.names.LC_BOUNDARY_T].featureCount() != boundaries_count - selected_boundaries_count:
# Clean layer because wrong data could have been inserted previously
expr = "{} NOT IN ('{}')".format(db.names.T_ILI_TID_F, "','".join([t_ili_tid for t_ili_tid in boundary_t_ili_tids]))
layers[db.names.LC_BOUNDARY_T].selectByExpression(expr)

with edit(layers[db.names.LC_BOUNDARY_T]):
layers[db.names.LC_BOUNDARY_T].deleteSelectedFeatures()

# the previously deleted boundaries are restored because an error occurred when trying to insert the building boundaries
self.app.core.run_etl_model_in_backgroud_mode(db, copy_boundary_layer, db.names.LC_BOUNDARY_T)

self.logger.warning_msg(__name__, QCoreApplication.translate("ToolBar",
"An error occurred when trying to build the boundary(ies). No changes are made!"))
else:
self.logger.warning_msg(__name__, QCoreApplication.translate("ToolBar",
"An error occurred when trying to build the boundary(ies). No changes are made!"))

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

# topology tables are recalculated with the new boundaries
Expand Down
15 changes: 13 additions & 2 deletions asistente_ladm_col/lib/geometry.py
Original file line number Diff line number Diff line change
Expand Up @@ -720,8 +720,19 @@ def build_boundaries(self, boundary_layer):
"""
:return: QgsVectorLayer
"""
params = {'boundaries': boundary_layer, 'native:refactorfields_2:built_boundaries': 'TEMPORARY_OUTPUT'}
return processing.run("model:Build_Boundaries", params)['native:refactorfields_2:built_boundaries']
feedback = CustomFeedbackWithErrors()
try:
params = {'boundaries': boundary_layer, 'native:refactorfields_2:built_boundaries': 'TEMPORARY_OUTPUT'}
build_boundary_layer = processing.run("model:Build_Boundaries", params, feedback=feedback)['native:refactorfields_2:built_boundaries']
except QgsProcessingException as e:
self.logger.warning(__name__, QCoreApplication.translate("Geometry",
"Error running the model to build boundaries. Details: {}".format(feedback.msg)))
build_boundary_layer = QgsVectorLayer("LineString?crs={}".format(get_crs_authid(boundary_layer.sourceCrs())), "build_boundaries", "memory")

# For CTM12 the output layer remains without projection. The projection of the input layer is assigned
if not build_boundary_layer.crs().authid():
build_boundary_layer.setCrs(boundary_layer.crs())
return build_boundary_layer

@staticmethod
def get_relationships_among_polygons(input_layer, intersect_layer, key=None, attrs=list(), get_geometry=False):
Expand Down
Loading

0 comments on commit a6ea115

Please sign in to comment.