Skip to content
Permalink
Browse files

Port random selection algorithms to new API

And heavily optimise random selection within subsets alg
  • Loading branch information
nyalldawson committed Aug 2, 2017
1 parent a64d199 commit 7ab82444f1108fba8a26629821a13138a8b50a72
@@ -99,6 +99,8 @@
from .RandomPointsExtent import RandomPointsExtent
from .RandomPointsLayer import RandomPointsLayer
from .RandomPointsPolygons import RandomPointsPolygons
from .RandomSelection import RandomSelection
from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets
from .RasterLayerStatistics import RasterLayerStatistics
from .RegularPoints import RegularPoints
from .ReverseLineDirection import ReverseLineDirection
@@ -135,8 +137,6 @@
from .ZonalStatistics import ZonalStatistics

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

def getAlgs(self):
# algs = [
# RandomSelection(), RandomSelectionWithinSubsets(),
# SelectByLocation(),
# ExtractByLocation(),
# SpatialJoin(),
@@ -270,6 +269,8 @@ def getAlgs(self):
RandomPointsExtent(),
RandomPointsLayer(),
RandomPointsPolygons(),
RandomSelection(),
RandomSelectionWithinSubsets(),
RasterLayerStatistics(),
RegularPoints(),
ReverseLineDirection(),
@@ -30,13 +30,16 @@
import random

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.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]

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

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

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

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

featureCount = layer.featureCount()
value = int(self.getParameterValue(self.NUMBER))

layer.removeSelection()
value = self.parameterAsInt(parameters, self.NUMBER, context)

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

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

layer.selectByIds(selran)
self.setOutputValue(self.OUTPUT, filename)
return {self.OUTPUT: parameters[self.INPUT]}
@@ -31,15 +31,17 @@

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.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]

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

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

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

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

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

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

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

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

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

selran = []
inFeat = QgsFeature()

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

if not len(unique) == featureCount:
for i in unique:
features = QgsProcessingUtils.getFeatures(layer, context)
FIDs = []
for inFeat in features:
attrs = inFeat.attributes()
if attrs[index] == i:
FIDs.append(inFeat.id())
current += 1
feedback.setProgress(int(current * total))

if method == 1:
selValue = int(round(value * len(FIDs), 0))
else:
selValue = value

if selValue >= len(FIDs):
selFeat = FIDs
else:
selFeat = random.sample(FIDs, selValue)

selran.extend(selFeat)
classes = defaultdict(list)

features = layer.getFeatures(QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([index]))

for i, feature in enumerate(features):
if feedback.isCanceled():
break

classes[feature.attributes()[index]].append(feature.id())
feedback.setProgress(int(i * total))

selran = []
for subset in classes.values():
if feedback.isCanceled():
break

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

layer.selectByIds(selran)
else:
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.
You can’t perform that action at this time.