Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Plots: Add title and axis titles; scatterplot option for logarithmic axes [FEATURE] #59366

Merged
merged 14 commits into from
Nov 29, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions doc/CONTRIBUTORS
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ Even Rouault
Fernando Pacheco
Florian El Ahdab
Florian Hof
Florian Neukirchen
Frank Warmerdam
Germán Carrillo
Giovanni Manghi
Expand Down
45 changes: 43 additions & 2 deletions python/plugins/processing/algs/qgis/BarPlot.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@
QgsProcessingParameterFeatureSource,
QgsProcessingParameterField,
QgsProcessingException,
QgsProcessingParameterFileDestination)
QgsProcessingParameterFileDestination,
QgsProcessingParameterString,)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.tools import vector

Expand All @@ -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')
Expand All @@ -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'

Expand All @@ -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)
Expand All @@ -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}
44 changes: 42 additions & 2 deletions python/plugins/processing/algs/qgis/BoxPlot.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,8 @@
QgsProcessingParameterField,
QgsProcessingParameterEnum,
QgsProcessingParameterFileDestination,
QgsFeatureRequest)
QgsFeatureRequest,
QgsProcessingParameterString)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.tools import vector

Expand All @@ -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')
Expand Down Expand Up @@ -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):
Expand All @@ -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)
Expand All @@ -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}
4 changes: 3 additions & 1 deletion python/plugins/processing/algs/qgis/MeanAndStdDevPlot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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)')))

Expand Down
68 changes: 66 additions & 2 deletions python/plugins/processing/algs/qgis/VectorLayerScatterplot.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand All @@ -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')
Expand All @@ -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):
Expand All @@ -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}
55 changes: 52 additions & 3 deletions python/plugins/processing/algs/qgis/VectorLayerScatterplot3D.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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')
Expand All @@ -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'
Expand All @@ -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)
Expand All @@ -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}
Loading