diff --git a/svir/calculations/aggregate_loss_by_zone.py b/svir/calculations/aggregate_loss_by_zone.py index a3065b324..ad48788b5 100644 --- a/svir/calculations/aggregate_loss_by_zone.py +++ b/svir/calculations/aggregate_loss_by_zone.py @@ -65,10 +65,6 @@ def calculate_zonal_stats(callback, zonal_layer, points_layer, join_fields, The full description of the algorithm can be obtained as follows: processing.algorithmHelp('qgis:joinbylocationsummary') and it includes the lists of predicates and summaries. - The code of the algorithm is here: - https://github.com/qgis/QGIS/blob - /483b4ff977e3d36b166fac792254c31e89e3aeae/python/plugins/processing/algs - /qgis/SpatialJoinSummary.py # NOQA :param callback: function to be called once the aggregation is complete, passing the output zonal layer as a parameter @@ -92,11 +88,19 @@ def calculate_zonal_stats(callback, zonal_layer, points_layer, join_fields, processing.Processing.initialize() alg = QgsApplication.processingRegistry().algorithmById( 'qgis:joinbylocationsummary') - # make sure to use the actual lists of predicates and summaries as defined - # in the algorithm when it is instantiated - predicate_keys = [predicate[0] for predicate in alg.predicates] + if alg is None: + raise ImportError('Unable to retrieve processing algorithm' + ' qgis:joinbylocationsummary') + # NOTE: predicates are no more retrieavable in the c++ version of the + # algorithm, so we can't make sure to use the actual lists of predicates + # and summaries as defined in the algorithm when it is instantiated + predicate_keys = ['intersects', 'contains', 'isEqual', 'touches', + 'overlaps', 'within', 'crosses'] PREDICATES = dict(zip(predicate_keys, range(len(predicate_keys)))) - summary_keys = [statistic[0] for statistic in alg.statistics] + summary_keys = [ + 'count', 'unique', 'min', 'max', 'range', 'sum', 'mean', 'median', + 'stddev', 'minority', 'majority', 'q1', 'q3', 'iqr', 'empty', 'filled', + 'min_length', 'max_length', 'mean_length'] SUMMARIES = dict(zip(summary_keys, range(len(summary_keys)))) context = QgsProcessingContext() diff --git a/svir/irmt.py b/svir/irmt.py index 912f2d49c..62aaa7ecc 100644 --- a/svir/irmt.py +++ b/svir/irmt.py @@ -46,6 +46,7 @@ QgsApplication, QgsWkbTypes, ) +from qgis.utils import iface from qgis.PyQt.QtCore import ( QSettings, @@ -55,7 +56,8 @@ QUrl, Qt, ) -from qgis.PyQt.QtWidgets import QAction, QFileDialog, QApplication, QMenu +from qgis.PyQt.QtWidgets import ( + QAction, QFileDialog, QApplication, QMenu, QInputDialog) from qgis.PyQt.QtGui import QIcon, QDesktopServices, QColor from svir.dialogs.viewer_dock import ViewerDock @@ -74,6 +76,7 @@ from svir.dialogs.taxonomy_dialog import TaxonomyDialog from svir.dialogs.drive_oq_engine_server_dialog import ( DriveOqEngineServerDialog) +from svir.dialogs.load_output_as_layer_dialog import LoadOutputAsLayerDialog from svir.thread_worker.abstract_worker import start_worker from svir.thread_worker.download_platform_data_worker import ( @@ -368,12 +371,20 @@ def recovery_settings(self): def aggregate(self): processing.Processing.initialize() - alg_id = 'irmt:joinbylocationsummarystyle' + alg_id = 'qgis:joinbylocationsummary' alg = QgsApplication.processingRegistry().algorithmById(alg_id) - # make sure to use the actual lists of predicates and summaries as - # defined in the algorithm when it is instantiated - predicate_keys = [predicate[0] for predicate in alg.predicates] + # NOTE: predicates are no more retrieavable in the c++ version of the + # algorithm, so we can't make sure to use the actual lists of + # predicates and summaries as defined in the algorithm when it is + # instantiated + predicate_keys = ['intersects', 'contains', 'isEqual', 'touches', + 'overlaps', 'within', 'crosses'] PREDICATES = dict(zip(predicate_keys, range(len(predicate_keys)))) + summary_keys = [ + 'count', 'unique', 'min', 'max', 'range', 'sum', 'mean', 'median', + 'stddev', 'minority', 'majority', 'q1', 'q3', 'iqr', 'empty', + 'filled', 'min_length', 'max_length', 'mean_length'] + SUMMARIES = dict(zip(summary_keys, range(len(summary_keys)))) default_predicates = ['intersects'] summary_keys = [statistic[0] for statistic in alg.statistics] SUMMARIES = dict(zip(summary_keys, range(len(summary_keys)))) @@ -400,6 +411,17 @@ def aggregate(self): res = processing.execAlgorithmDialog(alg_id, initial_params) if 'OUTPUT' in res: processed_layer = res['OUTPUT'] + added_fieldnames = [ + fieldname for fieldname in processed_layer.fields().names() + if fieldname not in zonal_layer.fields().names()] + if len(added_fieldnames) > 1: + style_by = QInputDialog.getItem( + iface.mainWindow(), "Style output by", "Field", + added_fieldnames, editable=False)[0] + else: + style_by = added_fieldnames[0] + LoadOutputAsLayerDialog.style_maps( + processed_layer, style_by, iface) QgsProject.instance().addMapLayer(processed_layer) self.iface.setActiveLayer(processed_layer) self.iface.zoomToActiveLayer() diff --git a/svir/processing_provider/provider.py b/svir/processing_provider/provider.py index 1b7186429..046a33110 100644 --- a/svir/processing_provider/provider.py +++ b/svir/processing_provider/provider.py @@ -32,8 +32,6 @@ from svir.processing_provider.log10_alg import Log10Algorithm from svir.processing_provider.simple_quadratic_alg import ( SimpleQuadraticAlgorithm) -from svir.processing_provider.spatial_join_summary_style import ( - SpatialJoinSummaryStyle) class Provider(QgsProcessingProvider): @@ -46,8 +44,6 @@ def loadAlgorithms(self, *args, **kwargs): self.addAlgorithm(SimpleQuadraticAlgorithm()) self.addAlgorithm(ZScoreAlgorithm()) self.addAlgorithm(Log10Algorithm()) - # Zonal aggregation - self.addAlgorithm(SpatialJoinSummaryStyle()) def id(self, *args, **kwargs): """The ID of your plugin, used for identifying the provider. diff --git a/svir/processing_provider/spatial_join_summary_style.py b/svir/processing_provider/spatial_join_summary_style.py deleted file mode 100644 index 5166b17b9..000000000 --- a/svir/processing_provider/spatial_join_summary_style.py +++ /dev/null @@ -1,125 +0,0 @@ -# -*- coding: utf-8 -*- -# /*************************************************************************** -# Irmt -# A QGIS plugin -# OpenQuake Integrated Risk Modelling Toolkit -# ------------------- -# begin : 2013-10-24 -# copyright : (C) 2013-2019 by GEM Foundation -# email : devops@openquake.org -# ***************************************************************************/ -# -# OpenQuake is free software: you can redistribute it and/or modify it -# under the terms of the GNU Affero General Public License as published -# by the Free Software Foundation, either version 3 of the License, or -# (at your option) any later version. -# -# OpenQuake is distributed in the hope that it will be useful, -# but WITHOUT ANY WARRANTY; without even the implied warranty of -# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -# GNU General Public License for more details. -# -# You should have received a copy of the GNU Affero General Public License -# along with OpenQuake. If not, see . - -from qgis.core import QgsProcessing, QgsProcessingUtils -from qgis.utils import iface -from qgis.PyQt.QtGui import QIcon -from qgis.PyQt.QtCore import QCoreApplication -from qgis.PyQt.QtWidgets import QInputDialog -from processing.algs.qgis.SpatialJoinSummary import SpatialJoinSummary - -from svir.dialogs.load_output_as_layer_dialog import LoadOutputAsLayerDialog - - -class SpatialJoinSummaryStyle(SpatialJoinSummary): - - def group(self): - return self.tr('Zonal aggregation') - - def groupId(self): - return 'aggregate' - - def name(self): - return 'joinbylocationsummarystyle' - - def displayName(self): - return self.tr( - 'Join attributes by location (summary) and style output') - - def shortHelpString(self): - """ - Returns a localised short helper string for the algorithm. This string - should provide a basic description about what the algorithm does and - the parameters and outputs associated with it.. - """ - return self.tr("Run joinbylocationsummary and style the output layer") - - def tags(self): - return self.tr( - "summary,aggregate,join,intersects,intersecting,touching,within," - "contains,overlaps,relation,spatial," - "stats,statistics,sum,maximum,minimum,mean,average," - "standard,deviation,style," - "count,distinct,unique,variance,median,quartile,range," - "majority,minority,histogram,distinct").split(',') - - def icon(self): - return QIcon(":/plugins/irmt/aggregate.svg") - - def svgIconPath(self): - return QIcon(":/plugins/irmt/aggregate.svg") - - def createInstance(self): - return SpatialJoinSummaryStyle() - - def tr(self, string): - """ - Returns a translatable string with the self.tr() function. - """ - return QCoreApplication.translate('Processing', string) - - def initAlgorithm(self, config=None): - super().initAlgorithm(config) - - super().parameterDefinition(self.INPUT).setDataTypes( - [QgsProcessing.TypeVectorPolygon]) - super().parameterDefinition(self.JOIN).setDataTypes( - [QgsProcessing.TypeVectorPoint]) - - stat_idxs = [idx for idx, stat in enumerate(self.statistics) - if stat[0] in ('mean', 'sum')] - super().parameterDefinition(self.SUMMARIES).setDefaultValue(stat_idxs) - - def processAlgorithm(self, parameters, context, feedback): - output = super().processAlgorithm(parameters, context, feedback) - self.parameters = parameters - self.dest_id = output[self.OUTPUT] - return {self.OUTPUT: self.dest_id} - - def postProcessAlgorithm(self, context, feedback): - - input_layer = QgsProcessingUtils.mapLayerFromString( - self.parameters[self.INPUT], context) - - processed_layer = QgsProcessingUtils.mapLayerFromString( - self.dest_id, context) - - added_fieldnames = [ - fieldname for fieldname in processed_layer.fields().names() - if fieldname not in input_layer.fields().names()] - - if len(added_fieldnames) > 1: - style_by = QInputDialog.getItem( - iface.mainWindow(), "Style output by", "Field", - added_fieldnames, editable=False)[0] - else: - style_by = added_fieldnames[0] - LoadOutputAsLayerDialog.style_maps( - processed_layer, style_by, iface) - # NOTE: the user might use the following lines after calling the - # algorithm from python: - # QgsProject.instance().addMapLayer(processed_layer) - # iface.setActiveLayer(processed_layer) - # iface.zoomToActiveLayer() - return {self.OUTPUT: processed_layer}