diff --git a/doc/CONTRIBUTORS b/doc/CONTRIBUTORS index 44a1d4ae70b5..ea7bada48870 100644 --- a/doc/CONTRIBUTORS +++ b/doc/CONTRIBUTORS @@ -39,6 +39,7 @@ Even Rouault Fernando Pacheco Florian El Ahdab Florian Hof +Florian Neukirchen Frank Warmerdam Germán Carrillo Giovanni Manghi diff --git a/python/plugins/processing/algs/qgis/BarPlot.py b/python/plugins/processing/algs/qgis/BarPlot.py index ff7dc1cca252..f17162870da9 100644 --- a/python/plugins/processing/algs/qgis/BarPlot.py +++ b/python/plugins/processing/algs/qgis/BarPlot.py @@ -25,7 +25,8 @@ QgsProcessingParameterFeatureSource, QgsProcessingParameterField, QgsProcessingException, - QgsProcessingParameterFileDestination) + QgsProcessingParameterFileDestination, + QgsProcessingParameterString,) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm from processing.tools import vector @@ -37,6 +38,9 @@ class BarPlot(QgisAlgorithm): OUTPUT = 'OUTPUT' NAME_FIELD = 'NAME_FIELD' VALUE_FIELD = 'VALUE_FIELD' + TITLE = 'TITLE' + XAXIS_TITLE = "XAXIS_TITLE" + YAXIS_TITLE = "YAXIS_TITLE" def group(self): return self.tr('Plots') @@ -59,6 +63,21 @@ def initAlgorithm(self, config=None): self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Bar plot'), self.tr('HTML files (*.html)'))) + self.addParameter(QgsProcessingParameterString( + self.TITLE, + self.tr('Title'), + optional=True)) + + self.addParameter(QgsProcessingParameterString( + self.XAXIS_TITLE, + self.tr('X-axis title'), + optional=True)) + + self.addParameter(QgsProcessingParameterString( + self.YAXIS_TITLE, + self.tr('Y-axis title'), + optional=True)) + def name(self): return 'barplot' @@ -83,6 +102,21 @@ def processAlgorithm(self, parameters, context, feedback): namefieldname = self.parameterAsString(parameters, self.NAME_FIELD, context) valuefieldname = self.parameterAsString(parameters, self.VALUE_FIELD, context) + title = self.parameterAsString(parameters, self.TITLE, context) + xaxis_title = self.parameterAsString(parameters, self.XAXIS_TITLE, context) + yaxis_title = self.parameterAsString(parameters, self.YAXIS_TITLE, context) + + if title.strip() == "": + title = None + if xaxis_title == "": + xaxis_title = namefieldname + elif xaxis_title == " ": + xaxis_title = None + if yaxis_title == "": + yaxis_title = valuefieldname + elif yaxis_title == " ": + yaxis_title = None + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) values = vector.values(source, valuefieldname) @@ -91,6 +125,13 @@ def processAlgorithm(self, parameters, context, feedback): data = [go.Bar(x=x_var, y=values[valuefieldname])] - plt.offline.plot(data, filename=output, auto_open=False) + + fig = go.Figure( + data=data, + layout_title_text=title, + layout_xaxis_title=xaxis_title, + layout_yaxis_title=yaxis_title) + + fig.write_html(output) return {self.OUTPUT: output} diff --git a/python/plugins/processing/algs/qgis/BoxPlot.py b/python/plugins/processing/algs/qgis/BoxPlot.py index 9fbd5559d375..55d2d5ba7b7e 100644 --- a/python/plugins/processing/algs/qgis/BoxPlot.py +++ b/python/plugins/processing/algs/qgis/BoxPlot.py @@ -26,7 +26,8 @@ QgsProcessingParameterField, QgsProcessingParameterEnum, QgsProcessingParameterFileDestination, - QgsFeatureRequest) + QgsFeatureRequest, + QgsProcessingParameterString) from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm from processing.tools import vector @@ -39,6 +40,9 @@ class BoxPlot(QgisAlgorithm): NAME_FIELD = 'NAME_FIELD' VALUE_FIELD = 'VALUE_FIELD' MSD = 'MSD' + TITLE = 'TITLE' + XAXIS_TITLE = "XAXIS_TITLE" + YAXIS_TITLE = "YAXIS_TITLE" def group(self): return self.tr('Plots') @@ -69,6 +73,21 @@ def initAlgorithm(self, config=None): self.tr('Additional Statistic Lines'), options=msd, defaultValue=0)) + self.addParameter(QgsProcessingParameterString( + self.TITLE, + self.tr('Title'), + optional=True)) + + self.addParameter(QgsProcessingParameterString( + self.XAXIS_TITLE, + self.tr('X-axis title'), + optional=True)) + + self.addParameter(QgsProcessingParameterString( + self.YAXIS_TITLE, + self.tr('Y-axis title'), + optional=True)) + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Box plot'), self.tr('HTML files (*.html)'))) def name(self): @@ -95,6 +114,21 @@ def processAlgorithm(self, parameters, context, feedback): namefieldname = self.parameterAsString(parameters, self.NAME_FIELD, context) valuefieldname = self.parameterAsString(parameters, self.VALUE_FIELD, context) + title = self.parameterAsString(parameters, self.TITLE, context) + xaxis_title = self.parameterAsString(parameters, self.XAXIS_TITLE, context) + yaxis_title = self.parameterAsString(parameters, self.YAXIS_TITLE, context) + + if title.strip() == "": + title = None + if xaxis_title == "": + xaxis_title = namefieldname + elif xaxis_title == " ": + xaxis_title = None + if yaxis_title.strip() == "": + yaxis_title = valuefieldname + elif yaxis_title == " ": + yaxis_title = None + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) values = vector.values(source, valuefieldname) @@ -115,6 +149,12 @@ def processAlgorithm(self, parameters, context, feedback): y=values[valuefieldname], boxmean=msd)] - plt.offline.plot(data, filename=output, auto_open=False) + fig = go.Figure( + data=data, + layout_title_text=title, + layout_xaxis_title=xaxis_title, + layout_yaxis_title=yaxis_title) + + fig.write_html(output) return {self.OUTPUT: output} diff --git a/python/plugins/processing/algs/qgis/MeanAndStdDevPlot.py b/python/plugins/processing/algs/qgis/MeanAndStdDevPlot.py index 2b665370f758..acf235e7d5a5 100644 --- a/python/plugins/processing/algs/qgis/MeanAndStdDevPlot.py +++ b/python/plugins/processing/algs/qgis/MeanAndStdDevPlot.py @@ -54,7 +54,9 @@ def initAlgorithm(self, config=None): self.tr('Category name field'), parentLayerParameterName=self.INPUT, type=QgsProcessingParameterField.DataType.Any)) self.addParameter(QgsProcessingParameterField(self.VALUE_FIELD, - self.tr('Value field'), parentLayerParameterName=self.INPUT)) + self.tr('Value field'), + parentLayerParameterName=self.INPUT, + type=QgsProcessingParameterField.DataType.Numeric)) self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Plot'), self.tr('HTML files (*.html)'))) diff --git a/python/plugins/processing/algs/qgis/VectorLayerScatterplot.py b/python/plugins/processing/algs/qgis/VectorLayerScatterplot.py index 33db3604c8e1..b53fe434344f 100644 --- a/python/plugins/processing/algs/qgis/VectorLayerScatterplot.py +++ b/python/plugins/processing/algs/qgis/VectorLayerScatterplot.py @@ -23,7 +23,10 @@ from qgis.core import (QgsProcessingException, QgsProcessingParameterFeatureSource, QgsProcessingParameterField, - QgsProcessingParameterFileDestination) + QgsProcessingParameterFileDestination, + QgsProcessingParameterString, + QgsProcessingParameterBoolean) + from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm from processing.tools import vector @@ -35,6 +38,11 @@ class VectorLayerScatterplot(QgisAlgorithm): OUTPUT = 'OUTPUT' XFIELD = 'XFIELD' YFIELD = 'YFIELD' + TITLE = 'TITLE' + XAXIS_TITLE = "XAXIS_TITLE" + YAXIS_TITLE = "YAXIS_TITLE" + XAXIS_LOG = "XAXIS_LOG" + YAXIS_LOG = "YAXIS_LOG" def group(self): return self.tr('Plots') @@ -57,6 +65,33 @@ def initAlgorithm(self, config=None): parentLayerParameterName=self.INPUT, type=QgsProcessingParameterField.DataType.Numeric)) + self.addParameter(QgsProcessingParameterString( + self.TITLE, + self.tr('Title'), + optional=True)) + + self.addParameter(QgsProcessingParameterString( + self.XAXIS_TITLE, + self.tr('X-axis title'), + optional=True)) + + self.addParameter(QgsProcessingParameterString( + self.YAXIS_TITLE, + self.tr('Y-axis title'), + optional=True)) + + self.addParameter(QgsProcessingParameterBoolean( + self.XAXIS_LOG, + self.tr('Use logarithmic scale for x-axis'), + defaultValue=False, + optional=True)) + + self.addParameter(QgsProcessingParameterBoolean( + self.YAXIS_LOG, + self.tr('Use logarithmic scale for y-axis'), + defaultValue=False, + optional=True)) + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Scatterplot'), self.tr('HTML files (*.html)'))) def name(self): @@ -83,12 +118,41 @@ def processAlgorithm(self, parameters, context, feedback): xfieldname = self.parameterAsString(parameters, self.XFIELD, context) yfieldname = self.parameterAsString(parameters, self.YFIELD, context) + title = self.parameterAsString(parameters, self.TITLE, context) + xaxis_title = self.parameterAsString(parameters, self.XAXIS_TITLE, context) + yaxis_title = self.parameterAsString(parameters, self.YAXIS_TITLE, context) + xaxis_log = self.parameterAsBool(parameters, self.XAXIS_LOG, context) + yaxis_log = self.parameterAsBool(parameters, self.YAXIS_LOG, context) + + if title.strip() == "": + title = None + if xaxis_title == "": + xaxis_title = xfieldname + elif xaxis_title == " ": + xaxis_title = None + if yaxis_title == "": + yaxis_title = yfieldname + elif yaxis_title == " ": + yaxis_title = None + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) values = vector.values(source, xfieldname, yfieldname) data = [go.Scatter(x=values[xfieldname], y=values[yfieldname], mode='markers')] - plt.offline.plot(data, filename=output, auto_open=False) + fig = go.Figure( + data=data, + layout_title_text=title, + layout_xaxis_title=xaxis_title, + layout_yaxis_title=yaxis_title) + + if xaxis_log: + fig.update_xaxes(type="log") + + if yaxis_log: + fig.update_yaxes(type="log") + + fig.write_html(output) return {self.OUTPUT: output} diff --git a/python/plugins/processing/algs/qgis/VectorLayerScatterplot3D.py b/python/plugins/processing/algs/qgis/VectorLayerScatterplot3D.py index 52eb032b41b2..2039a4657431 100644 --- a/python/plugins/processing/algs/qgis/VectorLayerScatterplot3D.py +++ b/python/plugins/processing/algs/qgis/VectorLayerScatterplot3D.py @@ -23,7 +23,9 @@ from qgis.core import (QgsProcessingParameterFeatureSource, QgsProcessingParameterField, QgsProcessingParameterFileDestination, - QgsProcessingException) + QgsProcessingException, + QgsProcessingParameterString) + from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm from processing.tools import vector @@ -37,6 +39,10 @@ class VectorLayerScatterplot3D(QgisAlgorithm): XFIELD = 'XFIELD' YFIELD = 'YFIELD' ZFIELD = 'ZFIELD' + TITLE = 'TITLE' + XAXIS_TITLE = "XAXIS_TITLE" + YAXIS_TITLE = "YAXIS_TITLE" + ZAXIS_TITLE = "ZAXIS_TITLE" def group(self): return self.tr('Plots') @@ -63,7 +69,27 @@ def initAlgorithm(self, config=None): parentLayerParameterName=self.INPUT, type=QgsProcessingParameterField.DataType.Numeric)) - self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Histogram'), self.tr('HTML files (*.html)'))) + self.addParameter(QgsProcessingParameterString( + self.TITLE, + self.tr('Title'), + optional=True)) + + self.addParameter(QgsProcessingParameterString( + self.XAXIS_TITLE, + self.tr('X-axis title'), + optional=True)) + + self.addParameter(QgsProcessingParameterString( + self.YAXIS_TITLE, + self.tr('Y-axis title'), + optional=True)) + + self.addParameter(QgsProcessingParameterString( + self.ZAXIS_TITLE, + self.tr('Z-axis title'), + optional=True)) + + self.addParameter(QgsProcessingParameterFileDestination(self.OUTPUT, self.tr('Scatterplot 3D'), self.tr('HTML files (*.html)'))) def name(self): return 'scatter3dplot' @@ -90,6 +116,22 @@ def processAlgorithm(self, parameters, context, feedback): yfieldname = self.parameterAsString(parameters, self.YFIELD, context) zfieldname = self.parameterAsString(parameters, self.ZFIELD, context) + title = self.parameterAsString(parameters, self.TITLE, context) + xaxis_title = self.parameterAsString(parameters, self.XAXIS_TITLE, context) + yaxis_title = self.parameterAsString(parameters, self.YAXIS_TITLE, context) + zaxis_title = self.parameterAsString(parameters, self.ZAXIS_TITLE, context) + + if title.strip() == "": + title = None + # Passing None for axis titles to Plotly does not work in this case: Unlike in 2D charts, Plotly would use x, y, z as axis titles. + # If our users enter space, we still get invisible axes titles, resulting in the same behavior as for the other algs. + if xaxis_title == "": + xaxis_title = xfieldname + if yaxis_title == "": + yaxis_title = yfieldname + if zaxis_title == "": + zaxis_title = zfieldname + output = self.parameterAsFileOutput(parameters, self.OUTPUT, context) values = vector.values(source, xfieldname, yfieldname, zfieldname) @@ -100,6 +142,13 @@ def processAlgorithm(self, parameters, context, feedback): z=values[zfieldname], mode='markers')] - plt.offline.plot(data, filename=output, auto_open=False) + fig = go.Figure( + data=data, + layout_title_text=title, + layout_scene_xaxis_title=xaxis_title, + layout_scene_yaxis_title=yaxis_title, + layout_scene_zaxis_title=zaxis_title) + + fig.write_html(output) return {self.OUTPUT: output}