Skip to content

Commit de9e70e

Browse files
authored
Merge pull request #4684 from nyalldawson/processing_pt3
Add sourceExtent method to QgsFeatureSource
2 parents 39b5f4e + 448f019 commit de9e70e

13 files changed

Lines changed: 94 additions & 42 deletions

‎python/core/qgsfeaturesource.sip‎

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,14 @@ class QgsFeatureSource
7878
:rtype: set of QVariant
7979
%End
8080

81+
virtual QgsRectangle sourceExtent() const;
82+
%Docstring
83+
Returns the extent of all geometries from the source.
84+
The base class implementation uses a non-optimised approach of looping through
85+
all features in the source.
86+
:rtype: QgsRectangle
87+
%End
88+
8189
};
8290

8391

‎python/core/qgsvectordataprovider.sip‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,6 +126,8 @@ Bitmask of all provider's editing capabilities
126126
127127
virtual QgsCoordinateReferenceSystem sourceCrs() const;
128128
129+
virtual QgsRectangle sourceExtent() const;
130+
129131
130132
virtual QString dataComment() const;
131133
%Docstring

‎python/core/qgsvectorlayer.sip‎

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1103,10 +1103,8 @@ Synchronises with changes in the datasource
11031103

11041104
virtual QgsRectangle extent() const;
11051105

1106-
%Docstring
1107-
Return the extent of the layer
1108-
:rtype: QgsRectangle
1109-
%End
1106+
virtual QgsRectangle sourceExtent() const;
1107+
11101108

11111109
virtual QgsFields fields() const;
11121110
%Docstring

‎python/plugins/processing/algs/qgis/ExtentFromLayer.py‎

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@
3636
QgsFeature,
3737
QgsWkbTypes,
3838
QgsProcessingUtils,
39+
QgsProcessingParameterFeatureSource,
40+
QgsProcessingParameterFeatureSink,
41+
QgsProcessingOutputVectorLayer,
42+
QgsProcessingParameterBoolean,
43+
QgsProcessingParameterDefinition,
3944
QgsFields)
4045

4146
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
@@ -65,12 +70,13 @@ def group(self):
6570

6671
def __init__(self):
6772
super().__init__()
68-
self.addParameter(ParameterVector(self.INPUT_LAYER,
69-
self.tr('Input layer')))
70-
self.addParameter(ParameterBoolean(self.BY_FEATURE,
71-
self.tr('Calculate extent for each feature separately'), False))
7273

73-
self.addOutput(OutputVector(self.OUTPUT, self.tr('Extent'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
74+
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT_LAYER, self.tr('Input layer')))
75+
self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE,
76+
self.tr('Calculate extent for each feature separately'), False))
77+
78+
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extent')))
79+
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Extent"), QgsProcessingParameterDefinition.TypeVectorPolygon))
7480

7581
def name(self):
7682
return 'polygonfromlayerextent'
@@ -79,8 +85,8 @@ def displayName(self):
7985
return self.tr('Polygon from layer extent')
8086

8187
def processAlgorithm(self, parameters, context, feedback):
82-
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)
83-
byFeature = self.getParameterValue(self.BY_FEATURE)
88+
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
89+
byFeature = self.parameterAsBool(parameters, self.BY_FEATURE, context)
8490

8591
fields = QgsFields()
8692
fields.append(QgsField('MINX', QVariant.Double))
@@ -94,17 +100,19 @@ def processAlgorithm(self, parameters, context, feedback):
94100
fields.append(QgsField('HEIGHT', QVariant.Double))
95101
fields.append(QgsField('WIDTH', QVariant.Double))
96102

97-
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs(), context)
103+
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
104+
fields, QgsWkbTypes.Polygon, source.sourceCrs())
98105

99106
if byFeature:
100-
self.featureExtent(layer, context, writer, feedback)
107+
self.featureExtent(source, context, sink, feedback)
101108
else:
102-
self.layerExtent(layer, writer, feedback)
109+
self.layerExtent(source, sink, feedback)
103110

104-
del writer
111+
return {self.OUTPUT: dest_id}
105112

106-
def layerExtent(self, layer, writer, feedback):
107-
rect = layer.extent()
113+
def layerExtent(self, source, sink, feedback):
114+
rect = source.sourceExtent()
115+
geometry = QgsGeometry.fromRect(rect)
108116
minx = rect.xMinimum()
109117
miny = rect.yMinimum()
110118
maxx = rect.xMaximum()
@@ -116,9 +124,6 @@ def layerExtent(self, layer, writer, feedback):
116124
area = width * height
117125
perim = 2 * width + 2 * height
118126

119-
rect = [QgsPointXY(minx, miny), QgsPointXY(minx, maxy), QgsPointXY(maxx,
120-
maxy), QgsPointXY(maxx, miny), QgsPointXY(minx, miny)]
121-
geometry = QgsGeometry().fromPolygon([rect])
122127
feat = QgsFeature()
123128
feat.setGeometry(geometry)
124129
attrs = [
@@ -134,13 +139,16 @@ def layerExtent(self, layer, writer, feedback):
134139
width,
135140
]
136141
feat.setAttributes(attrs)
137-
writer.addFeature(feat)
142+
sink.addFeature(feat)
138143

139-
def featureExtent(self, layer, context, writer, feedback):
140-
features = QgsProcessingUtils.getFeatures(layer, context)
141-
total = 100.0 / QgsProcessingUtils.featureCount(layer, context)
144+
def featureExtent(self, source, context, sink, feedback):
145+
features = source.getFeatures()
146+
total = 100.0 / source.featureCount()
142147
feat = QgsFeature()
143148
for current, f in enumerate(features):
149+
if feedback.isCanceled():
150+
break
151+
144152
rect = f.geometry().boundingBox()
145153
minx = rect.xMinimum()
146154
miny = rect.yMinimum()
@@ -171,5 +179,5 @@ def featureExtent(self, layer, context, writer, feedback):
171179
]
172180
feat.setAttributes(attrs)
173181

174-
writer.addFeature(feat)
182+
sink.addFeature(feat)
175183
feedback.setProgress(int(current * total))

‎python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py‎

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@
7979
# from .Difference import Difference
8080
# from .Dissolve import Dissolve
8181
# from .Intersection import Intersection
82-
# from .ExtentFromLayer import ExtentFromLayer
82+
from .ExtentFromLayer import ExtentFromLayer
8383
# from .RandomSelection import RandomSelection
8484
# from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets
8585
# from .SelectByLocation import SelectByLocation
@@ -210,7 +210,7 @@ def getAlgs(self):
210210
# PolygonsToLines(), LinesToPolygons(), ExtractNodes(),
211211
# ConvexHull(), FixedDistanceBuffer(),
212212
# VariableDistanceBuffer(), Dissolve(), Difference(),
213-
# Intersection(), Union(), ExtentFromLayer(),
213+
# Intersection(), Union(),
214214
# RandomSelection(), RandomSelectionWithinSubsets(),
215215
# SelectByLocation(), RandomExtract(), DeleteHoles(),
216216
# RandomExtractWithinSubsets(), ExtractByLocation(),
@@ -265,7 +265,8 @@ def getAlgs(self):
265265
Boundary(),
266266
BoundingBox(),
267267
Clip(),
268-
DeleteColumn()
268+
DeleteColumn(),
269+
ExtentFromLayer()
269270
]
270271

271272
if hasPlotly:

‎python/plugins/processing/tests/testdata/expected/polygon_from_extent.gml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@
1313

1414
<gml:featureMember>
1515
<ogr:polygon_from_extent fid="polygon_from_extent.0">
16-
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-1,-3 -1,6 10,6 10,-3 -1,-3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
16+
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-1,-3 10,-3 10,6 -1,6 -1,-3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
1717
<ogr:MINX>-1</ogr:MINX>
1818
<ogr:MINY>-3</ogr:MINY>
1919
<ogr:MAXX>10</ogr:MAXX>

‎python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml‎

Lines changed: 12 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2342,18 +2342,18 @@ tests:
23422342
# name: expected/find_projection.html
23432343
# type: file
23442344
#
2345-
# - algorithm: qgis:polygonfromlayerextent
2346-
# name: Standard polygon from layer extent
2347-
# params:
2348-
# BY_FEATURE: false
2349-
# INPUT_LAYER:
2350-
# name: polys.gml
2351-
# type: vector
2352-
# results:
2353-
# OUTPUT:
2354-
# name: expected/polygon_from_extent.gml
2355-
# type: vector
2356-
#
2345+
- algorithm: qgis:polygonfromlayerextent
2346+
name: Standard polygon from layer extent
2347+
params:
2348+
BY_FEATURE: false
2349+
INPUT_LAYER:
2350+
name: polys.gml
2351+
type: vector
2352+
results:
2353+
OUTPUT:
2354+
name: expected/polygon_from_extent.gml
2355+
type: vector
2356+
23572357
# - algorithm: qgis:topologicalcoloring
23582358
# name: Topological coloring
23592359
# params:

‎src/core/qgsfeaturesource.cpp‎

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,3 +40,20 @@ QSet<QVariant> QgsFeatureSource::uniqueValues( int fieldIndex, int limit ) const
4040
return values;
4141
}
4242

43+
QgsRectangle QgsFeatureSource::sourceExtent() const
44+
{
45+
QgsRectangle r;
46+
47+
QgsFeatureRequest req;
48+
req.setSubsetOfAttributes( QgsAttributeList() );
49+
50+
QgsFeatureIterator it = getFeatures( req );
51+
QgsFeature f;
52+
while ( it.nextFeature( f ) )
53+
{
54+
if ( f.hasGeometry() )
55+
r.combineExtentWith( f.geometry().boundingBox() );
56+
}
57+
return r;
58+
}
59+

‎src/core/qgsfeaturesource.h‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,13 @@ class CORE_EXPORT QgsFeatureSource
8787
*/
8888
virtual QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const;
8989

90+
/**
91+
* Returns the extent of all geometries from the source.
92+
* The base class implementation uses a non-optimised approach of looping through
93+
* all features in the source.
94+
*/
95+
virtual QgsRectangle sourceExtent() const;
96+
9097
};
9198

9299
Q_DECLARE_METATYPE( QgsFeatureSource * )

‎src/core/qgsvectordataprovider.cpp‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,11 @@ QgsCoordinateReferenceSystem QgsVectorDataProvider::sourceCrs() const
5252
return crs();
5353
}
5454

55+
QgsRectangle QgsVectorDataProvider::sourceExtent() const
56+
{
57+
return extent();
58+
}
59+
5560
QString QgsVectorDataProvider::dataComment() const
5661
{
5762
return QString();

0 commit comments

Comments
 (0)