3232import math
3333
3434from qgis .PyQt .QtGui import QIcon
35-
36- from qgis .core import QgsFeatureRequest , QgsProject , QgsDistanceArea , QgsFeatureSink , QgsProcessingUtils
35+ from qgis .PyQt .QtCore import QVariant
36+
37+ from qgis .core import (QgsFeatureRequest ,
38+ QgsField ,
39+ QgsFields ,
40+ QgsProject ,
41+ QgsFeature ,
42+ QgsGeometry ,
43+ QgsDistanceArea ,
44+ QgsFeatureSink ,
45+ QgsProcessingParameterFeatureSource ,
46+ QgsProcessing ,
47+ QgsProcessingParameterEnum ,
48+ QgsProcessingParameterField ,
49+ QgsProcessingParameterNumber ,
50+ QgsProcessingParameterFeatureSink ,
51+ QgsSpatialIndex ,
52+ QgsWkbTypes )
3753
3854from processing .algs .qgis .QgisAlgorithm import QgisAlgorithm
39- from processing .core .parameters import ParameterNumber
40- from processing .core .parameters import ParameterVector
41- from processing .core .parameters import ParameterSelection
42- from processing .core .parameters import ParameterTableField
43- from processing .core .outputs import OutputTable
44- from processing .tools import dataobjects
4555
4656pluginPath = os .path .split (os .path .split (os .path .dirname (__file__ ))[0 ])[0 ]
4757
4858
4959class PointDistance (QgisAlgorithm ):
50-
51- INPUT_LAYER = 'INPUT_LAYER'
60+ INPUT = 'INPUT'
5261 INPUT_FIELD = 'INPUT_FIELD'
53- TARGET_LAYER = 'TARGET_LAYER '
62+ TARGET = 'TARGET '
5463 TARGET_FIELD = 'TARGET_FIELD'
5564 MATRIX_TYPE = 'MATRIX_TYPE'
5665 NEAREST_POINTS = 'NEAREST_POINTS'
57- DISTANCE_MATRIX = 'DISTANCE_MATRIX '
66+ OUTPUT = 'OUTPUT '
5867
5968 def icon (self ):
6069 return QIcon (os .path .join (pluginPath , 'images' , 'ftools' , 'matrix.png' ))
@@ -70,22 +79,26 @@ def initAlgorithm(self, config=None):
7079 self .tr ('Standard (N x T) distance matrix' ),
7180 self .tr ('Summary distance matrix (mean, std. dev., min, max)' )]
7281
73- self .addParameter (ParameterVector (self .INPUT_LAYER ,
74- self .tr ('Input point layer' ), [dataobjects .TYPE_VECTOR_POINT ]))
75- self .addParameter (ParameterTableField (self .INPUT_FIELD ,
76- self .tr ('Input unique ID field' ), self .INPUT_LAYER ,
77- ParameterTableField .DATA_TYPE_ANY ))
78- self .addParameter (ParameterVector (self .TARGET_LAYER ,
79- self .tr ('Target point layer' ), dataobjects .TYPE_VECTOR_POINT ))
80- self .addParameter (ParameterTableField (self .TARGET_FIELD ,
81- self .tr ('Target unique ID field' ), self .TARGET_LAYER ,
82- ParameterTableField .DATA_TYPE_ANY ))
83- self .addParameter (ParameterSelection (self .MATRIX_TYPE ,
84- self .tr ('Output matrix type' ), self .mat_types , 0 ))
85- self .addParameter (ParameterNumber (self .NEAREST_POINTS ,
86- self .tr ('Use only the nearest (k) target points' ), 0 , 9999 , 0 ))
87-
88- self .addOutput (OutputTable (self .DISTANCE_MATRIX , self .tr ('Distance matrix' )))
82+ self .addParameter (QgsProcessingParameterFeatureSource (self .INPUT ,
83+ self .tr ('Input point layer' ),
84+ [QgsProcessing .TypeVectorPoint ]))
85+ self .addParameter (QgsProcessingParameterField (self .INPUT_FIELD ,
86+ self .tr ('Input unique ID field' ),
87+ parentLayerParameterName = self .INPUT ,
88+ type = QgsProcessingParameterField .Any ))
89+ self .addParameter (QgsProcessingParameterFeatureSource (self .TARGET ,
90+ self .tr ('Target point layer' ),
91+ [QgsProcessing .TypeVectorPoint ]))
92+ self .addParameter (QgsProcessingParameterField (self .TARGET_FIELD ,
93+ self .tr ('Target unique ID field' ),
94+ parentLayerParameterName = self .TARGET ,
95+ type = QgsProcessingParameterField .Any ))
96+ self .addParameter (QgsProcessingParameterEnum (self .MATRIX_TYPE ,
97+ self .tr ('Output matrix type' ), options = self .mat_types , defaultValue = 0 ))
98+ self .addParameter (QgsProcessingParameterNumber (self .NEAREST_POINTS ,
99+ self .tr ('Use only the nearest (k) target points' ), type = QgsProcessingParameterNumber .Integer , minValue = 0 , maxValue = 9999 , defaultValue = 0 ))
100+
101+ self .addParameter (QgsProcessingParameterFeatureSink (self .OUTPUT , self .tr ('Distance matrix' ), QgsProcessing .TypeVectorPoint ))
89102
90103 def name (self ):
91104 return 'distancematrix'
@@ -94,65 +107,86 @@ def displayName(self):
94107 return self .tr ('Distance matrix' )
95108
96109 def processAlgorithm (self , parameters , context , feedback ):
97- inLayer = QgsProcessingUtils .mapLayerFromString (self .getParameterValue (self .INPUT_LAYER ), context )
98- inField = self .getParameterValue (self .INPUT_FIELD )
99- targetLayer = QgsProcessingUtils .mapLayerFromString (self .getParameterValue (self .TARGET_LAYER ), context )
100- targetField = self .getParameterValue (self .TARGET_FIELD )
101- matType = self .getParameterValue (self .MATRIX_TYPE )
102- nPoints = self .getParameterValue (self .NEAREST_POINTS )
103-
104- outputFile = self .getOutputFromName (self .DISTANCE_MATRIX )
110+ source = self .parameterAsSource (parameters , self .INPUT , context )
111+ source_field = self .parameterAsString (parameters , self .INPUT_FIELD , context )
112+ target_source = self .parameterAsSource (parameters , self .TARGET , context )
113+ target_field = self .parameterAsString (parameters , self .TARGET_FIELD , context )
114+ matType = self .parameterAsEnum (parameters , self .MATRIX_TYPE , context )
115+ nPoints = self .parameterAsInt (parameters , self .NEAREST_POINTS , context )
105116
106117 if nPoints < 1 :
107- nPoints = QgsProcessingUtils .featureCount (targetLayer , context )
108-
109- self .writer = outputFile .getTableWriter ([])
118+ nPoints = target_source .featureCount ()
110119
111120 if matType == 0 :
112121 # Linear distance matrix
113- self .linearMatrix (context , inLayer , inField , targetLayer , targetField ,
114- matType , nPoints , feedback )
122+ return self .linearMatrix (parameters , context , source , source_field , target_source , target_field ,
123+ matType , nPoints , feedback )
115124 elif matType == 1 :
116125 # Standard distance matrix
117- self .regularMatrix (context , inLayer , inField , targetLayer , targetField ,
118- nPoints , feedback )
126+ return self .regularMatrix (parameters , context , source , source_field , target_source , target_field ,
127+ nPoints , feedback )
119128 elif matType == 2 :
120129 # Summary distance matrix
121- self .linearMatrix (context , inLayer , inField , targetLayer , targetField ,
122- matType , nPoints , feedback )
130+ return self .linearMatrix (parameters , context , source , source_field , target_source , target_field ,
131+ matType , nPoints , feedback )
123132
124- def linearMatrix (self , context , inLayer , inField , targetLayer , targetField ,
133+ def linearMatrix (self , parameters , context , source , inField , target_source , targetField ,
125134 matType , nPoints , feedback ):
135+ inIdx = source .fields ().lookupField (inField )
136+ outIdx = target_source .fields ().lookupField (targetField )
137+
138+ fields = QgsFields ()
139+ input_id_field = source .fields ()[inIdx ]
140+ input_id_field .setName ('InputID' )
141+ fields .append (input_id_field )
126142 if matType == 0 :
127- self .writer .addRecord (['InputID' , 'TargetID' , 'Distance' ])
143+ target_id_field = target_source .fields ()[outIdx ]
144+ target_id_field .setName ('TargetID' )
145+ fields .append (target_id_field )
146+ fields .append (QgsField ('Distance' , QVariant .Double ))
128147 else :
129- self .writer .addRecord (['InputID' , 'MEAN' , 'STDDEV' , 'MIN' , 'MAX' ])
148+ fields .append (QgsField ('MEAN' , QVariant .Double ))
149+ fields .append (QgsField ('STDDEV' , QVariant .Double ))
150+ fields .append (QgsField ('MIN' , QVariant .Double ))
151+ fields .append (QgsField ('MAX' , QVariant .Double ))
130152
131- index = QgsProcessingUtils .createSpatialIndex (targetLayer , context )
153+ out_wkb = QgsWkbTypes .multiType (source .wkbType ()) if matType == 0 else source .wkbType ()
154+ (sink , dest_id ) = self .parameterAsSink (parameters , self .OUTPUT , context ,
155+ fields , out_wkb , source .sourceCrs ())
132156
133- inIdx = inLayer .fields ().lookupField (inField )
134- outIdx = targetLayer .fields ().lookupField (targetField )
157+ index = QgsSpatialIndex (target_source .getFeatures (QgsFeatureRequest ().setSubsetOfAttributes ([]).setDestinationCrs (source .sourceCrs ())), feedback )
135158
136159 distArea = QgsDistanceArea ()
137- distArea .setSourceCrs (inLayer . crs ())
160+ distArea .setSourceCrs (source . sourceCrs ())
138161 distArea .setEllipsoid (QgsProject .instance ().ellipsoid ())
139162
140- features = QgsProcessingUtils .getFeatures (inLayer , context )
141- total = 100.0 / inLayer .featureCount () if inLayer .featureCount () else 0
163+ features = source .getFeatures (QgsFeatureRequest (). setSubsetOfAttributes ([ inIdx ]) )
164+ total = 100.0 / source .featureCount () if source .featureCount () else 0
142165 for current , inFeat in enumerate (features ):
166+ if feedback .isCanceled ():
167+ break
168+
143169 inGeom = inFeat .geometry ()
144170 inID = str (inFeat .attributes ()[inIdx ])
145171 featList = index .nearestNeighbor (inGeom .asPoint (), nPoints )
146172 distList = []
147173 vari = 0.0
148- request = QgsFeatureRequest ().setFilterFids (featList ).setSubsetOfAttributes ([outIdx ])
149- for outFeat in targetLayer .getFeatures (request ):
174+ request = QgsFeatureRequest ().setFilterFids (featList ).setSubsetOfAttributes ([outIdx ]).setDestinationCrs (source .sourceCrs ())
175+ for outFeat in target_source .getFeatures (request ):
176+ if feedback .isCanceled ():
177+ break
178+
150179 outID = outFeat .attributes ()[outIdx ]
151180 outGeom = outFeat .geometry ()
152181 dist = distArea .measureLine (inGeom .asPoint (),
153182 outGeom .asPoint ())
183+
154184 if matType == 0 :
155- self .writer .addRecord ([inID , str (outID ), str (dist )])
185+ out_feature = QgsFeature ()
186+ out_geom = QgsGeometry .unaryUnion ([inFeat .geometry (), outFeat .geometry ()])
187+ out_feature .setGeometry (out_geom )
188+ out_feature .setAttributes ([inID , outID , dist ])
189+ sink .addFeature (out_feature , QgsFeatureSink .FastInsert )
156190 else :
157191 distList .append (float (dist ))
158192
@@ -161,44 +195,61 @@ def linearMatrix(self, context, inLayer, inField, targetLayer, targetField,
161195 for i in distList :
162196 vari += (i - mean ) * (i - mean )
163197 vari = math .sqrt (vari / len (distList ))
164- self .writer .addRecord ([inID , str (mean ),
165- str (vari ), str (min (distList )),
166- str (max (distList ))])
198+
199+ out_feature = QgsFeature ()
200+ out_feature .setGeometry (inFeat .geometry ())
201+ out_feature .setAttributes ([inID , mean , vari , min (distList ), max (distList )])
202+ sink .addFeature (out_feature , QgsFeatureSink .FastInsert )
167203
168204 feedback .setProgress (int (current * total ))
169205
170- def regularMatrix (self , context , inLayer , inField , targetLayer , targetField ,
206+ return {self .OUTPUT : dest_id }
207+
208+ def regularMatrix (self , parameters , context , source , inField , target_source , targetField ,
171209 nPoints , feedback ):
172- index = QgsProcessingUtils .createSpatialIndex (targetLayer , context )
173210
174- inIdx = inLayer .fields ().lookupField (inField )
211+ index = QgsSpatialIndex (target_source .getFeatures (QgsFeatureRequest ().setSubsetOfAttributes ([]).setDestinationCrs (source .sourceCrs ())), feedback )
212+ inIdx = source .fields ().lookupField (inField )
175213
176214 distArea = QgsDistanceArea ()
177- distArea .setSourceCrs (inLayer .sourceCrs ())
215+ distArea .setSourceCrs (source .sourceCrs ())
178216 distArea .setEllipsoid (QgsProject .instance ().ellipsoid ())
179217
180218 first = True
181- features = QgsProcessingUtils .getFeatures (inLayer , context )
182- total = 100.0 / inLayer .featureCount () if inLayer .featureCount () else 0
219+ sink = None
220+ dest_id = None
221+ features = source .getFeatures (QgsFeatureRequest ().setSubsetOfAttributes ([inIdx ]))
222+ total = 100.0 / source .featureCount () if source .featureCount () else 0
183223 for current , inFeat in enumerate (features ):
224+ if feedback .isCanceled ():
225+ break
226+
184227 inGeom = inFeat .geometry ()
185228 inID = str (inFeat .attributes ()[inIdx ])
186229 featList = index .nearestNeighbor (inGeom .asPoint (), nPoints )
187230 if first :
188231 first = False
189- data = ['ID' ]
232+ fields = QgsFields ()
233+ input_id_field = source .fields ()[inIdx ]
234+ input_id_field .setName ('ID' )
235+ fields .append (input_id_field )
190236 for i in range (len (featList )):
191- data .append ('DIST_{0}' .format (i + 1 ))
192- self .writer .addRecord (data )
237+ fields .append (QgsField ('DIST_{0}' .format (i + 1 ), QVariant .Double ))
238+ (sink , dest_id ) = self .parameterAsSink (parameters , self .OUTPUT , context ,
239+ fields , source .wkbType (), source .sourceCrs ())
193240
194241 data = [inID ]
195- for i in featList :
196- request = QgsFeatureRequest (). setFilterFid ( i )
197- outFeat = next ( targetLayer . getFeatures ( request ))
198- outGeom = outFeat .geometry ()
242+ for target in target_source . getFeatures ( QgsFeatureRequest (). setSubsetOfAttributes ([]). setFilterFids ( featList ). setDestinationCrs ( source . sourceCrs ())) :
243+ if feedback . isCanceled ():
244+ break
245+ outGeom = target .geometry ()
199246 dist = distArea .measureLine (inGeom .asPoint (),
200247 outGeom .asPoint ())
201- data .append (str (float (dist )))
202- self .writer .addRecord (data )
203-
248+ data .append (float (dist ))
249+ out_feature = QgsFeature ()
250+ out_feature .setGeometry (inGeom )
251+ out_feature .setAttributes (data )
252+ sink .addFeature (out_feature , QgsFeatureSink .FastInsert )
204253 feedback .setProgress (int (current * total ))
254+
255+ return {self .OUTPUT : dest_id }
0 commit comments