Port random selection algorithms to new API

And heavily optimise random selection within subsets alg
This commit is contained in:
Nyall Dawson 2017-08-02 23:09:47 +10:00
parent a64d199e6f
commit 7ab82444f1
3 changed files with 72 additions and 76 deletions

View File

@ -99,6 +99,8 @@ from .RandomPointsAlongLines import RandomPointsAlongLines
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 .VoronoiPolygons import VoronoiPolygons
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 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
def getAlgs(self):
# algs = [
# RandomSelection(), RandomSelectionWithinSubsets(),
# SelectByLocation(),
# ExtractByLocation(),
# SpatialJoin(),
@ -270,6 +269,8 @@ class QGISAlgorithmProvider(QgsProcessingProvider):
RandomPointsExtent(),
RandomPointsLayer(),
RandomPointsPolygons(),
RandomSelection(),
RandomSelectionWithinSubsets(),
RasterLayerStatistics(),
RegularPoints(),
ReverseLineDirection(),

View File

@ -30,13 +30,16 @@ import os
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 @@ class RandomSelection(QgisAlgorithm):
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,23 +80,20 @@ class RandomSelection(QgisAlgorithm):
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)
@ -100,4 +101,4 @@ class RandomSelection(QgisAlgorithm):
selran = random.sample(list(range(featureCount)), value)
layer.selectByIds(selran)
self.setOutputValue(self.OUTPUT, filename)
return {self.OUTPUT: parameters[self.INPUT]}

View File

@ -31,15 +31,17 @@ import random
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 @@ class RandomSelectionWithinSubsets(QgisAlgorithm):
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 @@ class RandomSelectionWithinSubsets(QgisAlgorithm):
return self.tr('Random selection within subsets')
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)
field = self.parameterAsString(parameters, self.FIELD, context)
layer = QgsProcessingUtils.mapLayerFromString(filename, context)
field = self.getParameterValue(self.FIELD)
method = self.getParameterValue(self.METHOD)
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))
classes = defaultdict(list)
if method == 1:
selValue = int(round(value * len(FIDs), 0))
else:
selValue = value
features = layer.getFeatures(QgsFeatureRequest().setFlags(QgsFeatureRequest.NoGeometry).setSubsetOfAttributes([index]))
if selValue >= len(FIDs):
selFeat = FIDs
else:
selFeat = random.sample(FIDs, selValue)
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))
selran.extend(selFeat)
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]}