Skip to content

Commit

Permalink
Port random selection algorithms to new API
Browse files Browse the repository at this point in the history
And heavily optimise random selection within subsets alg
  • Loading branch information
nyalldawson committed Aug 5, 2017
1 parent a64d199 commit 7ab8244
Show file tree
Hide file tree
Showing 3 changed files with 75 additions and 79 deletions.
7 changes: 4 additions & 3 deletions python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -99,6 +99,8 @@
from .RandomPointsExtent import RandomPointsExtent from .RandomPointsExtent import RandomPointsExtent
from .RandomPointsLayer import RandomPointsLayer from .RandomPointsLayer import RandomPointsLayer
from .RandomPointsPolygons import RandomPointsPolygons from .RandomPointsPolygons import RandomPointsPolygons
from .RandomSelection import RandomSelection
from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets
from .RasterLayerStatistics import RasterLayerStatistics from .RasterLayerStatistics import RasterLayerStatistics
from .RegularPoints import RegularPoints from .RegularPoints import RegularPoints
from .ReverseLineDirection import ReverseLineDirection from .ReverseLineDirection import ReverseLineDirection
Expand Down Expand Up @@ -135,8 +137,6 @@
from .ZonalStatistics import ZonalStatistics from .ZonalStatistics import ZonalStatistics


# from .ExtractByLocation import ExtractByLocation # from .ExtractByLocation import ExtractByLocation
# from .RandomSelection import RandomSelection
# from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets
# from .SelectByLocation import SelectByLocation # from .SelectByLocation import SelectByLocation
# from .SpatialJoin import SpatialJoin # from .SpatialJoin import SpatialJoin
# from .GridLine import GridLine # from .GridLine import GridLine
Expand Down Expand Up @@ -185,7 +185,6 @@ def __init__(self):


def getAlgs(self): def getAlgs(self):
# algs = [ # algs = [
# RandomSelection(), RandomSelectionWithinSubsets(),
# SelectByLocation(), # SelectByLocation(),
# ExtractByLocation(), # ExtractByLocation(),
# SpatialJoin(), # SpatialJoin(),
Expand Down Expand Up @@ -270,6 +269,8 @@ def getAlgs(self):
RandomPointsExtent(), RandomPointsExtent(),
RandomPointsLayer(), RandomPointsLayer(),
RandomPointsPolygons(), RandomPointsPolygons(),
RandomSelection(),
RandomSelectionWithinSubsets(),
RasterLayerStatistics(), RasterLayerStatistics(),
RegularPoints(), RegularPoints(),
ReverseLineDirection(), ReverseLineDirection(),
Expand Down
45 changes: 23 additions & 22 deletions python/plugins/processing/algs/qgis/RandomSelection.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -30,13 +30,16 @@
import random import random


from qgis.PyQt.QtGui import QIcon from qgis.PyQt.QtGui import QIcon
from qgis.core import QgsFeatureSink, QgsProcessingUtils from qgis.core import (QgsFeatureSink,
QgsProcessingException,
QgsProcessingUtils,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterEnum,
QgsProcessingParameterNumber,
QgsProcessingParameterFeatureSink,
QgsProcessingOutputVectorLayer)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterSelection
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterNumber
from processing.core.outputs import OutputVector


pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]


Expand All @@ -61,13 +64,14 @@ def initAlgorithm(self, config=None):
self.methods = [self.tr('Number of selected features'), self.methods = [self.tr('Number of selected features'),
self.tr('Percentage of selected features')] self.tr('Percentage of selected features')]


self.addParameter(ParameterVector(self.INPUT, self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT,
self.tr('Input layer'))) self.tr('Input layer')))
self.addParameter(ParameterSelection(self.METHOD, self.addParameter(QgsProcessingParameterEnum(self.METHOD,
self.tr('Method'), self.methods, 0)) self.tr('Method'), self.methods, False, 0))
self.addParameter(ParameterNumber(self.NUMBER, self.addParameter(QgsProcessingParameterNumber(self.NUMBER,
self.tr('Number/percentage of selected features'), 0, None, 10)) self.tr('Number/percentage of selected features'), QgsProcessingParameterNumber.Integer,
self.addOutput(OutputVector(self.OUTPUT, self.tr('Selection'), True)) 10, False, 0.0, 999999999999.0))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Selected (random)')))


def name(self): def name(self):
return 'randomselection' return 'randomselection'
Expand All @@ -76,28 +80,25 @@ def displayName(self):
return self.tr('Random selection') return self.tr('Random selection')


def processAlgorithm(self, parameters, context, feedback): def processAlgorithm(self, parameters, context, feedback):
filename = self.getParameterValue(self.INPUT) layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)
layer = QgsProcessingUtils.mapLayerFromString(filename, context) method = self.parameterAsEnum(parameters, self.METHOD, context)
method = self.getParameterValue(self.METHOD)


featureCount = layer.featureCount() featureCount = layer.featureCount()
value = int(self.getParameterValue(self.NUMBER)) value = self.parameterAsInt(parameters, self.NUMBER, context)

layer.removeSelection()


if method == 0: if method == 0:
if value > featureCount: if value > featureCount:
raise GeoAlgorithmExecutionException( raise QgsProcessingException(
self.tr('Selected number is greater than feature count. ' self.tr('Selected number is greater than feature count. '
'Choose a lower value and try again.')) 'Choose a lower value and try again.'))
else: else:
if value > 100: if value > 100:
raise GeoAlgorithmExecutionException( raise QgsProcessingException(
self.tr("Percentage can't be greater than 100. Set a " self.tr("Percentage can't be greater than 100. Set a "
"different value and try again.")) "different value and try again."))
value = int(round(value / 100.0, 4) * featureCount) value = int(round(value / 100.0, 4) * featureCount)


selran = random.sample(list(range(featureCount)), value) selran = random.sample(list(range(featureCount)), value)


layer.selectByIds(selran) layer.selectByIds(selran)
self.setOutputValue(self.OUTPUT, filename) return {self.OUTPUT: parameters[self.INPUT]}
102 changes: 48 additions & 54 deletions python/plugins/processing/algs/qgis/RandomSelectionWithinSubsets.py
Original file line number Original file line Diff line number Diff line change
Expand Up @@ -31,15 +31,17 @@


from qgis.PyQt.QtGui import QIcon from qgis.PyQt.QtGui import QIcon


from qgis.core import QgsFeature, QgsFeatureSink, QgsProcessingUtils from qgis.core import (QgsFeatureRequest,

QgsProcessingException,
QgsProcessingUtils,
QgsProcessingParameterVectorLayer,
QgsProcessingParameterEnum,
QgsProcessingParameterField,
QgsProcessingParameterNumber,
QgsProcessingParameterFeatureSink,
QgsProcessingOutputVectorLayer)
from collections import defaultdict
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.GeoAlgorithmExecutionException import GeoAlgorithmExecutionException
from processing.core.parameters import ParameterSelection
from processing.core.parameters import ParameterVector
from processing.core.parameters import ParameterNumber
from processing.core.parameters import ParameterTableField
from processing.core.outputs import OutputVector


pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0] pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]


Expand All @@ -65,16 +67,17 @@ def initAlgorithm(self, config=None):
self.methods = [self.tr('Number of selected features'), self.methods = [self.tr('Number of selected features'),
self.tr('Percentage of selected features')] self.tr('Percentage of selected features')]


self.addParameter(ParameterVector(self.INPUT, self.addParameter(QgsProcessingParameterVectorLayer(self.INPUT,
self.tr('Input layer'))) self.tr('Input layer')))
self.addParameter(ParameterTableField(self.FIELD, self.addParameter(QgsProcessingParameterField(self.FIELD,
self.tr('ID Field'), self.INPUT)) self.tr('ID field'), None, self.INPUT))
self.addParameter(ParameterSelection(self.METHOD, self.addParameter(QgsProcessingParameterEnum(self.METHOD,
self.tr('Method'), self.methods, 0)) self.tr('Method'), self.methods, False, 0))
self.addParameter(ParameterNumber(self.NUMBER, self.addParameter(QgsProcessingParameterNumber(self.NUMBER,
self.tr('Number/percentage of selected features'), 1, None, 10)) self.tr('Number/percentage of selected features'),

QgsProcessingParameterNumber.Integer,
self.addOutput(OutputVector(self.OUTPUT, self.tr('Selection stratified'), True)) 10, False, 0.0, 999999999999.0))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Selected (stratified random)')))


def name(self): def name(self):
return 'randomselectionwithinsubsets' return 'randomselectionwithinsubsets'
Expand All @@ -83,61 +86,52 @@ def displayName(self):
return self.tr('Random selection within subsets') return self.tr('Random selection within subsets')


def processAlgorithm(self, parameters, context, feedback): def processAlgorithm(self, parameters, context, feedback):
filename = self.getParameterValue(self.INPUT) layer = self.parameterAsVectorLayer(parameters, self.INPUT, context)

method = self.parameterAsEnum(parameters, self.METHOD, context)
layer = QgsProcessingUtils.mapLayerFromString(filename, context) field = self.parameterAsString(parameters, self.FIELD, context)
field = self.getParameterValue(self.FIELD)
method = self.getParameterValue(self.METHOD)


layer.removeSelection()
index = layer.fields().lookupField(field) index = layer.fields().lookupField(field)


unique = QgsProcessingUtils.uniqueValues(layer, index, context) unique = layer.uniqueValues(index)
featureCount = layer.featureCount() featureCount = layer.featureCount()


value = int(self.getParameterValue(self.NUMBER)) value = self.parameterAsInt(parameters, self.NUMBER, context)
if method == 0: if method == 0:
if value > featureCount: if value > featureCount:
raise GeoAlgorithmExecutionException( raise QgsProcessingException(
self.tr('Selected number is greater that feature count. ' self.tr('Selected number is greater that feature count. '
'Choose lesser value and try again.')) 'Choose lesser value and try again.'))
else: else:
if value > 100: if value > 100:
raise GeoAlgorithmExecutionException( raise QgsProcessingException(
self.tr("Percentage can't be greater than 100. Set a " self.tr("Percentage can't be greater than 100. Set a "
"different value and try again.")) "different value and try again."))
value = value / 100.0 value = value / 100.0


selran = []
inFeat = QgsFeature()

current = 0
total = 100.0 / (featureCount * len(unique)) if featureCount else 1 total = 100.0 / (featureCount * len(unique)) if featureCount else 1


if not len(unique) == featureCount: if not len(unique) == featureCount:
for i in unique: classes = defaultdict(list)
features = QgsProcessingUtils.getFeatures(layer, context)
FIDs = [] features = layer.getFeatures(QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([index]))
for inFeat in features:
attrs = inFeat.attributes() for i, feature in enumerate(features):
if attrs[index] == i: if feedback.isCanceled():
FIDs.append(inFeat.id()) break
current += 1
feedback.setProgress(int(current * total)) classes[feature.attributes()[index]].append(feature.id())

feedback.setProgress(int(i * total))
if method == 1:
selValue = int(round(value * len(FIDs), 0)) selran = []
else: for subset in classes.values():
selValue = value if feedback.isCanceled():

break
if selValue >= len(FIDs):
selFeat = FIDs selValue = value if method != 1 else int(round(value * len(subset), 0))
else: selran.extend(random.sample(subset, selValue))
selFeat = random.sample(FIDs, selValue)

selran.extend(selFeat)
layer.selectByIds(selran) layer.selectByIds(selran)
else: else:
layer.selectByIds(list(range(featureCount))) # FIXME: implies continuous feature ids layer.selectByIds(list(range(featureCount))) # FIXME: implies continuous feature ids


self.setOutputValue(self.OUTPUT, filename) return {self.OUTPUT: parameters[self.INPUT]}

0 comments on commit 7ab8244

Please sign in to comment.