29
29
30
30
from qgis .PyQt .QtGui import QIcon
31
31
32
- from qgis .core import QgsGeometry , QgsFeatureRequest , QgsProcessingUtils
32
+ from qgis .core import (QgsGeometry ,
33
+ QgsFeatureRequest ,
34
+ QgsProcessingUtils ,
35
+ QgsProcessing ,
36
+ QgsProcessingParameterVectorLayer ,
37
+ QgsProcessingParameterFeatureSource ,
38
+ QgsProcessingParameterEnum ,
39
+ QgsProcessingParameterNumber ,
40
+ QgsProcessingOutputVectorLayer ,
41
+ QgsVectorLayer )
42
+
33
43
34
44
from processing .algs .qgis .QgisAlgorithm import QgisAlgorithm
35
- from processing .core .parameters import ParameterSelection
36
- from processing .core .parameters import ParameterVector
37
- from processing .core .parameters import ParameterNumber
38
- from processing .core .outputs import OutputVector
39
45
from processing .tools import vector
40
46
41
47
pluginPath = os .path .split (os .path .split (os .path .dirname (__file__ ))[0 ])[0 ]
@@ -63,32 +69,43 @@ def initAlgorithm(self, config=None):
63
69
self .predicates = (
64
70
('intersects' , self .tr ('intersects' )),
65
71
('contains' , self .tr ('contains' )),
66
- ('disjoint' , self .tr ('disjoint' )),
67
- ('equals ' , self .tr ('equals' )),
72
+ ('disjoint' , self .tr ('is disjoint' )),
73
+ ('isEqual ' , self .tr ('equals' )),
68
74
('touches' , self .tr ('touches' )),
69
75
('overlaps' , self .tr ('overlaps' )),
70
76
('within' , self .tr ('within' )),
71
77
('crosses' , self .tr ('crosses' )))
72
78
79
+ self .reversed_predicates = {'intersects' : 'intersects' ,
80
+ 'contains' : 'within' ,
81
+ 'disjoint' : 'disjoint' ,
82
+ 'isEqual' : 'isEqual' ,
83
+ 'touches' : 'touches' ,
84
+ 'overlaps' : 'overlaps' ,
85
+ 'within' : 'contains' ,
86
+ 'crosses' : 'crosses' }
87
+
73
88
self .methods = [self .tr ('creating new selection' ),
74
89
self .tr ('adding to current selection' ),
90
+ self .tr ('select within current selection' ),
75
91
self .tr ('removing from current selection' )]
76
92
77
- self .addParameter (ParameterVector (self .INPUT ,
78
- self .tr ('Layer to select from' )))
79
- self .addParameter (ParameterVector (self .INTERSECT ,
80
- self .tr ('Additional layer (intersection layer)' )))
81
- self .addParameter (ParameterSelection (self .PREDICATE ,
82
- self .tr ('Geometric predicate' ),
83
- self .predicates ,
84
- multiple = True ))
85
- self .addParameter (ParameterNumber (self .PRECISION ,
86
- self .tr ('Precision' ),
87
- 0.0 , None , 0.0 ))
88
- self .addParameter (ParameterSelection (self .METHOD ,
89
- self .tr ('Modify current selection by' ),
90
- self .methods , 0 ))
91
- self .addOutput (OutputVector (self .OUTPUT , self .tr ('Selected (location)' ), True ))
93
+ self .addParameter (QgsProcessingParameterVectorLayer (self .INPUT ,
94
+ self .tr ('Select features from' ), types = [QgsProcessing .TypeVectorAnyGeometry ]))
95
+ self .addParameter (QgsProcessingParameterEnum (self .PREDICATE ,
96
+ self .tr ('Where the features are (geometric predicate)' ),
97
+ options = [p [1 ] for p in self .predicates ],
98
+ allowMultiple = True , defaultValue = [0 ]))
99
+ self .addParameter (QgsProcessingParameterFeatureSource (self .INTERSECT ,
100
+ self .tr ('By comparing to the features from' ), types = [QgsProcessing .TypeVectorAnyGeometry ]))
101
+ self .addParameter (QgsProcessingParameterNumber (self .PRECISION ,
102
+ self .tr ('Precision' ), type = QgsProcessingParameterNumber .Double ,
103
+ minValue = 0.0 , defaultValue = 0.0 ))
104
+ self .addParameter (QgsProcessingParameterEnum (self .METHOD ,
105
+ self .tr ('Modify current selection by' ),
106
+ options = self .methods , defaultValue = 0 ))
107
+
108
+ self .addOutput (QgsProcessingOutputVectorLayer (self .OUTPUT , self .tr ('Selected (by location)' )))
92
109
93
110
def name (self ):
94
111
return 'selectbylocation'
@@ -97,60 +114,61 @@ def displayName(self):
97
114
return self .tr ('Select by location' )
98
115
99
116
def processAlgorithm (self , parameters , context , feedback ):
100
- filename = self .getParameterValue (self .INPUT )
101
- inputLayer = QgsProcessingUtils .mapLayerFromString (filename , context )
102
- method = self .getParameterValue (self .METHOD )
103
- filename2 = self .getParameterValue (self .INTERSECT )
104
- selectLayer = QgsProcessingUtils .mapLayerFromString (filename2 , context )
105
- predicates = self .getParameterValue (self .PREDICATE )
106
- precision = self .getParameterValue (self .PRECISION )
107
-
108
- oldSelection = set (inputLayer .selectedFeatureIds ())
109
- inputLayer .removeSelection ()
110
- index = QgsProcessingUtils .createSpatialIndex (inputLayer , context )
117
+ select_layer = self .parameterAsVectorLayer (parameters , self .INPUT , context )
118
+ method = QgsVectorLayer .SelectBehavior (self .parameterAsEnum (parameters , self .METHOD , context ))
119
+ intersect_source = self .parameterAsSource (parameters , self .INTERSECT , context )
120
+ # build a list of 'reversed' predicates, because in this function
121
+ # we actually test the reverse of what the user wants (allowing us
122
+ # to prepare geometries and optimise the algorithm)
123
+ predicates = [self .reversed_predicates [self .predicates [i ][0 ]] for i in self .parameterAsEnums (parameters , self .PREDICATE , context )]
124
+ precision = self .parameterAsDouble (parameters , self .PRECISION , context )
111
125
112
126
if 'disjoint' in predicates :
113
- disjoinSet = []
114
- for feat in QgsProcessingUtils . getFeatures ( inputLayer , context ) :
115
- disjoinSet . append ( feat . id ())
116
-
117
- geom = QgsGeometry ()
118
- selectedSet = []
119
- features = QgsProcessingUtils .getFeatures (selectLayer , context )
120
- total = 100.0 / selectLayer .featureCount () if selectLayer .featureCount () else 0
127
+ disjoint_set = select_layer . allFeatureIds ()
128
+ else :
129
+ disjoint_set = None
130
+
131
+ selected_set = set ()
132
+ request = QgsFeatureRequest (). setSubsetOfAttributes ([]). setDestinationCrs ( select_layer . crs ())
133
+ features = intersect_source .getFeatures (request )
134
+ total = 100.0 / intersect_source .featureCount () if intersect_source .featureCount () else 0
121
135
for current , f in enumerate (features ):
122
- geom = vector .snapToPrecision (f .geometry (), precision )
123
- bbox = geom .boundingBox ()
136
+ if feedback .isCanceled ():
137
+ break
138
+
139
+ if not f .hasGeometry ():
140
+ continue
141
+
142
+ engine = QgsGeometry .createGeometryEngine (f .geometry ().geometry ())
143
+ engine .prepareGeometry ()
144
+ bbox = f .geometry ().boundingBox ()
124
145
bbox .grow (0.51 * precision )
125
- intersects = index .intersects (bbox )
126
146
127
- request = QgsFeatureRequest ().setFilterFids (intersects ).setSubsetOfAttributes ([])
128
- for feat in inputLayer .getFeatures (request ):
129
- tmpGeom = vector .snapToPrecision (feat .geometry (), precision )
147
+ request = QgsFeatureRequest ().setFlags (QgsFeatureRequest .NoGeometry ).setFilterRect (bbox ).setSubsetOfAttributes ([])
148
+ for test_feat in select_layer .getFeatures (request ):
149
+ if feedback .isCanceled ():
150
+ break
151
+
152
+ if test_feat in selected_set :
153
+ # already added this one, no need for further tests
154
+ continue
130
155
131
- res = False
132
156
for predicate in predicates :
133
157
if predicate == 'disjoint' :
134
- if tmpGeom . intersects (geom ):
158
+ if test_feat . geometry (). intersects (f . geometry () ):
135
159
try :
136
- disjoinSet .remove (feat .id ())
160
+ disjoint_set .remove (test_feat .id ())
137
161
except :
138
162
pass # already removed
139
163
else :
140
- res = getattr (tmpGeom , predicate )(geom )
141
- if res :
142
- selectedSet .append (feat .id ())
164
+ if getattr (engine , predicate )(test_feat .geometry ().geometry ()):
165
+ selected_set .add (test_feat .id ())
143
166
break
144
167
145
168
feedback .setProgress (int (current * total ))
146
169
147
170
if 'disjoint' in predicates :
148
- selectedSet = selectedSet + disjoinSet
149
-
150
- if method == 1 :
151
- selectedSet = list (oldSelection .union (selectedSet ))
152
- elif method == 2 :
153
- selectedSet = list (oldSelection .difference (selectedSet ))
171
+ selected_set = list (selected_set ) + disjoint_set
154
172
155
- inputLayer .selectByIds (selectedSet )
156
- self .setOutputValue ( self . OUTPUT , filename )
173
+ select_layer .selectByIds (list ( selected_set ), method )
174
+ return { self .OUTPUT : parameters [ self . INPUT ]}
0 commit comments