Skip to content

Commit

Permalink
First working pure c++ algorithms
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jun 5, 2017
1 parent 4cb7d18 commit c1d9d57
Show file tree
Hide file tree
Showing 19 changed files with 262 additions and 76 deletions.
28 changes: 21 additions & 7 deletions python/core/processing/qgsprocessingalgorithm.sip
Expand Up @@ -193,17 +193,14 @@ class QgsProcessingAlgorithm
:rtype: QgsProcessingOutputDefinition
%End

virtual QVariantMap run( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const;
QVariantMap run( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const;
%Docstring
Runs the algorithm using the specified ``parameters``. Algorithms should implement
their custom processing logic here.
Executes the algorithm using the specified ``parameters``.

The ``context`` argument specifies the context in which the algorithm is being run.

Algorithm progress should be reported using the supplied ``feedback`` object. Additionally,
well-behaved algorithms should periodically check ``feedback`` to determine whether the
algorithm should be canceled and exited early.
Algorithm progress should be reported using the supplied ``feedback`` object.

:return: A map of algorithm outputs. These may be output layer references, or calculated
values such as statistical calculations.
Expand Down Expand Up @@ -239,6 +236,23 @@ class QgsProcessingAlgorithm
:rtype: bool
%End

virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) const = 0;
%Docstring
Runs the algorithm using the specified ``parameters``. Algorithms should implement
their custom processing logic here.

The ``context`` argument specifies the context in which the algorithm is being run.

Algorithm progress should be reported using the supplied ``feedback`` object. Additionally,
well-behaved algorithms should periodically check ``feedback`` to determine whether the
algorithm should be canceled and exited early.

:return: A map of algorithm outputs. These may be output layer references, or calculated
values such as statistical calculations.
:rtype: QVariantMap
%End

QString parameterAsString( const QVariantMap &parameters, const QString &name, const QgsProcessingContext &context ) const;
%Docstring
Evaluates the parameter with matching ``name`` to a static string value.
Expand Down
26 changes: 26 additions & 0 deletions python/core/processing/qgsprocessingcontext.sip
Expand Up @@ -83,6 +83,28 @@ class QgsProcessingContext
:rtype: QgsMapLayerStore
%End

QStringList layersToLoadOnCompletion() const;
%Docstring
Returns a list of layers (by ID or datasource) to load into the canvas upon completion of the algorithm or model.
.. seealso:: setLayersToLoadOnCompletion()
.. seealso:: addLayerToLoadOnCompletion()
:rtype: list of str
%End

void setLayersToLoadOnCompletion( const QStringList &layers );
%Docstring
Sets the list of ``layers`` (by ID or datasource) to load into the canvas upon completion of the algorithm or model.
.. seealso:: addLayerToLoadOnCompletion()
.. seealso:: layersToLoadOnCompletion()
%End

void addLayerToLoadOnCompletion( const QString &layer );
%Docstring
Adds a ``layer`` to load (by ID or datasource) into the canvas upon completion of the algorithm or model.
.. seealso:: setLayersToLoadOnCompletion()
.. seealso:: layersToLoadOnCompletion()
%End


QgsFeatureRequest::InvalidGeometryCheck invalidGeometryCheck() const;
%Docstring
Expand Down Expand Up @@ -137,6 +159,10 @@ class QgsProcessingContext
QgsProcessingContext( const QgsProcessingContext &other );
};





QFlags<QgsProcessingContext::Flag> operator|(QgsProcessingContext::Flag f1, QFlags<QgsProcessingContext::Flag> f2);


Expand Down
12 changes: 6 additions & 6 deletions python/plugins/processing/algs/qgis/CheckValidity.py
Expand Up @@ -178,9 +178,9 @@ def doCheck(self, context, feedback):
del invalid_writer
del error_writer

if valid_count == 0:
valid_output.open = False
if invalid_count == 0:
invalid_output.open = False
if error_count == 0:
error_output.open = False
if valid_count != 0:
context.addLayerToLoadOnCompletion(valid_output.value)
if invalid_count != 0:
context.addLayerToLoadOnCompletion(invalid_output.value)
if error_count != 0:
context.addLayerToLoadOnCompletion(error_output.value)
4 changes: 2 additions & 2 deletions python/plugins/processing/algs/qgis/SetRasterStyle.py
Expand Up @@ -75,13 +75,13 @@ def processAlgorithm(self, parameters, context, feedback):
style = self.getParameterValue(self.STYLE)
if layer is None:
dataobjects.load(filename, os.path.basename(filename), style=style)
self.getOutputFromName(self.OUTPUT).open = False
else:
with open(style) as f:
xml = "".join(f.readlines())
d = QDomDocument()
d.setContent(xml)
n = d.firstChild()
layer.readSymbology(n, '')
context.addLayerToLoadOnCompletion(self.getOutputFromName(self.OUTPUT).value)
self.setOutputValue(self.OUTPUT, filename)
iface.mapCanvas().refresh()
layer.triggerRepaint()
3 changes: 1 addition & 2 deletions python/plugins/processing/algs/qgis/SetVectorStyle.py
Expand Up @@ -72,8 +72,7 @@ def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(filename, context, False)
if layer is None:
dataobjects.load(filename, os.path.basename(filename), style=style)
self.getOutputFromName(self.OUTPUT).open = False
else:
layer.loadNamedStyle(style)
iface.mapCanvas().refresh()
context.addLayerToLoadOnCompletion(layer.id())
layer.triggerRepaint()
3 changes: 2 additions & 1 deletion python/plugins/processing/core/GeoAlgorithm.py
Expand Up @@ -462,9 +462,10 @@ def executeAlgorithm(alg, parameters, context=None, feedback=None, model=None):
#self.resolveOutputs()
#self.evaluateParameterValues()
#self.runPreExecutionScript(feedback)
alg.run(parameters, context, feedback)
result = alg.run(parameters, context, feedback)
#self.processAlgorithm(parameters, context, feedback)
feedback.setProgress(100)
return result
#self.convertUnsupportedFormats(context, feedback)
#self.runPostExecutionScript(feedback)
#except GeoAlgorithmExecutionException as gaee:
Expand Down
4 changes: 0 additions & 4 deletions python/plugins/processing/core/outputs.py
Expand Up @@ -78,10 +78,6 @@ def __init__(self, name='', description='', hidden=False):
# outputs not representing layers or tables should always be hidden.
self.hidden = str(hidden).lower() == str(True).lower()

# This value indicates whether the output has to be opened
# after being produced by the algorithm or not
self.open = True

def __str__(self):
return u'{} <{}>'.format(self.name, self.__class__.__name__)

Expand Down
34 changes: 26 additions & 8 deletions python/plugins/processing/gui/AlgorithmDialog.py
Expand Up @@ -33,7 +33,9 @@
from qgis.core import (QgsProject,
QgsProcessingUtils,
QgsMessageLog,
QgsProcessingParameterDefinition)
QgsProcessingParameterDefinition,
QgsProcessingOutputVectorLayer,
QgsProcessingParameterOutputVectorLayer)
from qgis.gui import QgsMessageBar
from qgis.utils import iface

Expand Down Expand Up @@ -114,6 +116,17 @@ def getParamValues(self):

return parameters

def getLayersToOpen(self):
layer_outputs = []
for param in self.alg.destinationParameterDefinitions():
if param.flags() & QgsProcessingParameterDefinition.FlagHidden:
continue
if isinstance(param, (OutputRaster, QgsProcessingParameterOutputVectorLayer, OutputTable)):
if self.mainWidget.checkBoxes[param.name()].isChecked():
layer_outputs.append(param.name())

return layer_outputs

def setParamValue(self, param, wrapper):
if wrapper.widget:
return param.setValue(wrapper.value())
Expand Down Expand Up @@ -220,7 +233,7 @@ def accept(self):

if self.iterateParam:
if executeIterating(self.alg, parameters, self.iterateParam, context, self.feedback):
self.finish(context)
self.finish(parameters, context)
else:
QApplication.restoreOverrideCursor()
self.resetGUI()
Expand All @@ -229,11 +242,12 @@ def accept(self):
#command = self.alg.getAsCommand()
#if command:
# ProcessingLog.addToLog(command)
if executeAlgorithm(self.alg, parameters, context, self.feedback):
self.finish(context)
else:
QApplication.restoreOverrideCursor()
self.resetGUI()
result = executeAlgorithm(self.alg, parameters, context, self.feedback)
self.finish(result, context)
#TODO
#else:
# QApplication.restoreOverrideCursor()
# self.resetGUI()
except AlgorithmDialogBase.InvalidParameterValue as e:
try:
self.buttonBox.accepted.connect(lambda e=e:
Expand All @@ -247,10 +261,14 @@ def accept(self):
self.bar.pushMessage("", self.tr("Wrong or missing parameter value: {0}").format(e.parameter.description),
level=QgsMessageBar.WARNING, duration=5)

def finish(self, context):
def finish(self, result, context):
keepOpen = ProcessingConfig.getSetting(ProcessingConfig.KEEP_DIALOG_OPEN)

if self.iterateParam is None:

for o in self.getLayersToOpen():
context.addLayerToLoadOnCompletion(result[o])

if not handleAlgorithmResults(self.alg, context, self.feedback, not keepOpen):
self.resetGUI()
return
Expand Down
3 changes: 3 additions & 0 deletions python/plugins/processing/gui/ConfigDialog.py
Expand Up @@ -158,6 +158,9 @@ def fillTreeUsingProviders(self):
emptyItem.setEditable(False)

rootItem.insertRow(0, [groupItem, emptyItem])
if not group in settings:
continue

# add menu item only if it has any search matches
for setting in settings[group]:
if setting.hidden or setting.name.startswith("MENU_"):
Expand Down
33 changes: 17 additions & 16 deletions python/plugins/processing/gui/NumberInputPanel.py
Expand Up @@ -33,7 +33,8 @@
from qgis.PyQt.QtCore import pyqtSignal
from qgis.PyQt.QtWidgets import QDialog

from qgis.core import QgsExpression
from qgis.core import (QgsExpression,
QgsProcessingParameterNumber)
from qgis.gui import QgsExpressionBuilderDialog
from processing.core.parameters import ParameterNumber, ParameterVector, ParameterRaster
from processing.core.outputs import OutputNumber, OutputVector, OutputRaster
Expand Down Expand Up @@ -63,8 +64,8 @@ def __init__(self, param, modelParametersDialog):

self.param = param
self.modelParametersDialog = modelParametersDialog
if param.default:
self.setValue(param.default)
if param.defaultValue():
self.setValue(param.defaultValue())
self.btnSelect.clicked.connect(self.showExpressionsBuilder)
self.leText.textChanged.connect(lambda: self.hasChanged.emit())

Expand Down Expand Up @@ -153,36 +154,36 @@ def __init__(self, param):
self.spnValue.setExpressionsEnabled(True)

self.param = param
if self.param.isInteger:
if self.param.dataType() == QgsProcessingParameterNumber.Integer:
self.spnValue.setDecimals(0)
else:
# Guess reasonable step value
if self.param.max is not None and self.param.min is not None:
if self.param.maximum() is not None and self.param.minimum() is not None:
try:
self.spnValue.setSingleStep(self.calculateStep(float(self.param.min), float(self.param.max)))
self.spnValue.setSingleStep(self.calculateStep(float(self.param.minimum()), float(self.param.maximum())))
except:
pass

if self.param.max is not None:
self.spnValue.setMaximum(self.param.max)
if self.param.maximum() is not None:
self.spnValue.setMaximum(self.param.maximum())
else:
self.spnValue.setMaximum(999999999)
if self.param.min is not None:
self.spnValue.setMinimum(self.param.min)
if self.param.minimum() is not None:
self.spnValue.setMinimum(self.param.minimum())
else:
self.spnValue.setMinimum(-999999999)

# set default value
if param.default is not None:
self.setValue(param.default)
if param.defaultValue() is not None:
self.setValue(param.defaultValue())
try:
self.spnValue.setClearValue(float(param.default))
self.spnValue.setClearValue(float(param.defaultValue()))
except:
pass
elif self.param.min is not None:
elif self.param.minimum() is not None:
try:
self.setValue(float(self.param.min))
self.spnValue.setClearValue(float(self.param.min))
self.setValue(float(self.param.minimum()))
self.spnValue.setClearValue(float(self.param.minimum()))
except:
pass
else:
Expand Down
48 changes: 48 additions & 0 deletions python/plugins/processing/gui/Postprocessing.py
Expand Up @@ -55,6 +55,54 @@ def handleAlgorithmResults(alg, context, feedback=None, showResults=True):
feedback = QgsProcessingFeedback()
feedback.setProgressText(QCoreApplication.translate('Postprocessing', 'Loading resulting layers'))
i = 0
#for out in alg.outputs:
# feedback.setProgress(100 * i / float(len(alg.outputs)))
# if out.hidden or not out.open:
# continue
# if isinstance(out, (OutputRaster, OutputVector, OutputTable)):
# try:
# layer = QgsProcessingUtils.mapLayerFromString(out.value, context)
# if layer:
# layer.setName(out.description)
# QgsProject.instance().addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
# else:
# if ProcessingConfig.getSetting(
# ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
# name = os.path.basename(out.value)
# else:
# name = out.description
# dataobjects.load(out.value, name, alg.crs,
# RenderingStyles.getStyle(alg.id(),
# out.name))
# except Exception:
# QgsMessageLog.logMessage("Error loading result layer:\n" + traceback.format_exc(), 'Processing', QgsMessageLog.CRITICAL)
# wrongLayers.append(out.description)
# elif isinstance(out, OutputHTML):
# resultsList.addResult(alg.icon(), out.description, out.value)
# i += 1
i = 0
for l in context.layersToLoadOnCompletion():
feedback.setProgress(100 * i / float(len(context.layersToLoadOnCompletion())))
try:
layer = QgsProcessingUtils.mapLayerFromString(l, context)
if layer:
#TODO
#layer.setName(out.description)
QgsProject.instance().addMapLayer(context.temporaryLayerStore().takeMapLayer(layer))
else:
if ProcessingConfig.getSetting(
ProcessingConfig.USE_FILENAME_AS_LAYER_NAME):
name = os.path.basename(l)
else:
#TODO
name = l # out.description
dataobjects.load(l, name, alg.crs,
RenderingStyles.getStyle(alg.id(), l))
#out.name))
except Exception:
QgsMessageLog.logMessage("Error loading result layer:\n" + traceback.format_exc(), 'Processing', QgsMessageLog.CRITICAL)
#wrongLayers.append(out.description)
wrongLayers.append(l)
for out in alg.outputs:
feedback.setProgress(100 * i / float(len(alg.outputs)))
if out.hidden or not out.open:
Expand Down
4 changes: 2 additions & 2 deletions python/plugins/processing/gui/wrappers.py
Expand Up @@ -723,8 +723,8 @@ def createWidget(self):
return MultipleInputPanel(options=self.param.options())
else:
widget = QComboBox()
for option in self.param.options():
widget.addItem(option[1], option[0])
for i, option in enumerate(self.param.options()):
widget.addItem(option, i)
if self.param.defaultValue():
widget.setCurrentIndex(widget.findData(self.param.defaultValue()))
return widget
Expand Down
15 changes: 10 additions & 5 deletions src/core/geometry/qgsgeos.cpp
Expand Up @@ -1377,11 +1377,16 @@ bool QgsGeos::centroid( QgsPoint &pt, QString *errorMsg ) const
return false;
}

double x, y;
GEOSGeomGetX_r( geosinit.ctxt, geos.get(), &x );
GEOSGeomGetY_r( geosinit.ctxt, geos.get(), &y );
pt.setX( x );
pt.setY( y );
try
{
double x, y;
GEOSGeomGetX_r( geosinit.ctxt, geos.get(), &x );
GEOSGeomGetY_r( geosinit.ctxt, geos.get(), &y );
pt.setX( x );
pt.setY( y );
}
CATCH_GEOS_WITH_ERRMSG( false );

return true;
}

Expand Down

0 comments on commit c1d9d57

Please sign in to comment.