Skip to content

Commit

Permalink
Merge pull request #4684 from nyalldawson/processing_pt3
Browse files Browse the repository at this point in the history
Add sourceExtent method to QgsFeatureSource
  • Loading branch information
nyalldawson committed Jun 6, 2017
2 parents 39b5f4e + 448f019 commit de9e70e
Show file tree
Hide file tree
Showing 13 changed files with 94 additions and 42 deletions.
8 changes: 8 additions & 0 deletions python/core/qgsfeaturesource.sip
Expand Up @@ -78,6 +78,14 @@ class QgsFeatureSource
:rtype: set of QVariant
%End

virtual QgsRectangle sourceExtent() const;
%Docstring
Returns the extent of all geometries from the source.
The base class implementation uses a non-optimised approach of looping through
all features in the source.
:rtype: QgsRectangle
%End

};


Expand Down
2 changes: 2 additions & 0 deletions python/core/qgsvectordataprovider.sip
Expand Up @@ -126,6 +126,8 @@ Bitmask of all provider's editing capabilities

virtual QgsCoordinateReferenceSystem sourceCrs() const;

virtual QgsRectangle sourceExtent() const;


virtual QString dataComment() const;
%Docstring
Expand Down
6 changes: 2 additions & 4 deletions python/core/qgsvectorlayer.sip
Expand Up @@ -1103,10 +1103,8 @@ Synchronises with changes in the datasource

virtual QgsRectangle extent() const;

%Docstring
Return the extent of the layer
:rtype: QgsRectangle
%End
virtual QgsRectangle sourceExtent() const;


virtual QgsFields fields() const;
%Docstring
Expand Down
50 changes: 29 additions & 21 deletions python/plugins/processing/algs/qgis/ExtentFromLayer.py
Expand Up @@ -36,6 +36,11 @@
QgsFeature,
QgsWkbTypes,
QgsProcessingUtils,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingOutputVectorLayer,
QgsProcessingParameterBoolean,
QgsProcessingParameterDefinition,
QgsFields)

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

def __init__(self):
super().__init__()
self.addParameter(ParameterVector(self.INPUT_LAYER,
self.tr('Input layer')))
self.addParameter(ParameterBoolean(self.BY_FEATURE,
self.tr('Calculate extent for each feature separately'), False))

self.addOutput(OutputVector(self.OUTPUT, self.tr('Extent'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT_LAYER, self.tr('Input layer')))
self.addParameter(QgsProcessingParameterBoolean(self.BY_FEATURE,
self.tr('Calculate extent for each feature separately'), False))

self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Extent')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr("Extent"), QgsProcessingParameterDefinition.TypeVectorPolygon))

def name(self):
return 'polygonfromlayerextent'
Expand All @@ -79,8 +85,8 @@ def displayName(self):
return self.tr('Polygon from layer extent')

def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_LAYER), context)
byFeature = self.getParameterValue(self.BY_FEATURE)
source = self.parameterAsSource(parameters, self.INPUT_LAYER, context)
byFeature = self.parameterAsBool(parameters, self.BY_FEATURE, context)

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

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, QgsWkbTypes.Polygon, layer.crs(), context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, QgsWkbTypes.Polygon, source.sourceCrs())

if byFeature:
self.featureExtent(layer, context, writer, feedback)
self.featureExtent(source, context, sink, feedback)
else:
self.layerExtent(layer, writer, feedback)
self.layerExtent(source, sink, feedback)

del writer
return {self.OUTPUT: dest_id}

def layerExtent(self, layer, writer, feedback):
rect = layer.extent()
def layerExtent(self, source, sink, feedback):
rect = source.sourceExtent()
geometry = QgsGeometry.fromRect(rect)
minx = rect.xMinimum()
miny = rect.yMinimum()
maxx = rect.xMaximum()
Expand All @@ -116,9 +124,6 @@ def layerExtent(self, layer, writer, feedback):
area = width * height
perim = 2 * width + 2 * height

rect = [QgsPointXY(minx, miny), QgsPointXY(minx, maxy), QgsPointXY(maxx,
maxy), QgsPointXY(maxx, miny), QgsPointXY(minx, miny)]
geometry = QgsGeometry().fromPolygon([rect])
feat = QgsFeature()
feat.setGeometry(geometry)
attrs = [
Expand All @@ -134,13 +139,16 @@ def layerExtent(self, layer, writer, feedback):
width,
]
feat.setAttributes(attrs)
writer.addFeature(feat)
sink.addFeature(feat)

def featureExtent(self, layer, context, writer, feedback):
features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / QgsProcessingUtils.featureCount(layer, context)
def featureExtent(self, source, context, sink, feedback):
features = source.getFeatures()
total = 100.0 / source.featureCount()
feat = QgsFeature()
for current, f in enumerate(features):
if feedback.isCanceled():
break

rect = f.geometry().boundingBox()
minx = rect.xMinimum()
miny = rect.yMinimum()
Expand Down Expand Up @@ -171,5 +179,5 @@ def featureExtent(self, layer, context, writer, feedback):
]
feat.setAttributes(attrs)

writer.addFeature(feat)
sink.addFeature(feat)
feedback.setProgress(int(current * total))
7 changes: 4 additions & 3 deletions python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Expand Up @@ -79,7 +79,7 @@
# from .Difference import Difference
# from .Dissolve import Dissolve
# from .Intersection import Intersection
# from .ExtentFromLayer import ExtentFromLayer
from .ExtentFromLayer import ExtentFromLayer
# from .RandomSelection import RandomSelection
# from .RandomSelectionWithinSubsets import RandomSelectionWithinSubsets
# from .SelectByLocation import SelectByLocation
Expand Down Expand Up @@ -210,7 +210,7 @@ def getAlgs(self):
# PolygonsToLines(), LinesToPolygons(), ExtractNodes(),
# ConvexHull(), FixedDistanceBuffer(),
# VariableDistanceBuffer(), Dissolve(), Difference(),
# Intersection(), Union(), ExtentFromLayer(),
# Intersection(), Union(),
# RandomSelection(), RandomSelectionWithinSubsets(),
# SelectByLocation(), RandomExtract(), DeleteHoles(),
# RandomExtractWithinSubsets(), ExtractByLocation(),
Expand Down Expand Up @@ -265,7 +265,8 @@ def getAlgs(self):
Boundary(),
BoundingBox(),
Clip(),
DeleteColumn()
DeleteColumn(),
ExtentFromLayer()
]

if hasPlotly:
Expand Down
Expand Up @@ -13,7 +13,7 @@

<gml:featureMember>
<ogr:polygon_from_extent fid="polygon_from_extent.0">
<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>
<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>
<ogr:MINX>-1</ogr:MINX>
<ogr:MINY>-3</ogr:MINY>
<ogr:MAXX>10</ogr:MAXX>
Expand Down
24 changes: 12 additions & 12 deletions python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
Expand Up @@ -2342,18 +2342,18 @@ tests:
# name: expected/find_projection.html
# type: file
#
# - algorithm: qgis:polygonfromlayerextent
# name: Standard polygon from layer extent
# params:
# BY_FEATURE: false
# INPUT_LAYER:
# name: polys.gml
# type: vector
# results:
# OUTPUT:
# name: expected/polygon_from_extent.gml
# type: vector
#
- algorithm: qgis:polygonfromlayerextent
name: Standard polygon from layer extent
params:
BY_FEATURE: false
INPUT_LAYER:
name: polys.gml
type: vector
results:
OUTPUT:
name: expected/polygon_from_extent.gml
type: vector

# - algorithm: qgis:topologicalcoloring
# name: Topological coloring
# params:
Expand Down
17 changes: 17 additions & 0 deletions src/core/qgsfeaturesource.cpp
Expand Up @@ -40,3 +40,20 @@ QSet<QVariant> QgsFeatureSource::uniqueValues( int fieldIndex, int limit ) const
return values;
}

QgsRectangle QgsFeatureSource::sourceExtent() const
{
QgsRectangle r;

QgsFeatureRequest req;
req.setSubsetOfAttributes( QgsAttributeList() );

QgsFeatureIterator it = getFeatures( req );
QgsFeature f;
while ( it.nextFeature( f ) )
{
if ( f.hasGeometry() )
r.combineExtentWith( f.geometry().boundingBox() );
}
return r;
}

7 changes: 7 additions & 0 deletions src/core/qgsfeaturesource.h
Expand Up @@ -87,6 +87,13 @@ class CORE_EXPORT QgsFeatureSource
*/
virtual QSet<QVariant> uniqueValues( int fieldIndex, int limit = -1 ) const;

/**
* Returns the extent of all geometries from the source.
* The base class implementation uses a non-optimised approach of looping through
* all features in the source.
*/
virtual QgsRectangle sourceExtent() const;

};

Q_DECLARE_METATYPE( QgsFeatureSource * )
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsvectordataprovider.cpp
Expand Up @@ -52,6 +52,11 @@ QgsCoordinateReferenceSystem QgsVectorDataProvider::sourceCrs() const
return crs();
}

QgsRectangle QgsVectorDataProvider::sourceExtent() const
{
return extent();
}

QString QgsVectorDataProvider::dataComment() const
{
return QString();
Expand Down
1 change: 1 addition & 0 deletions src/core/qgsvectordataprovider.h
Expand Up @@ -159,6 +159,7 @@ class CORE_EXPORT QgsVectorDataProvider : public QgsDataProvider, public QgsFeat
virtual QgsFields fields() const override = 0;

QgsCoordinateReferenceSystem sourceCrs() const override;
QgsRectangle sourceExtent() const override;

/**
* Return a short comment for the data that this provider is
Expand Down
5 changes: 5 additions & 0 deletions src/core/qgsvectorlayer.cpp
Expand Up @@ -879,6 +879,11 @@ QgsRectangle QgsVectorLayer::extent() const
return rect;
}

QgsRectangle QgsVectorLayer::sourceExtent() const
{
return extent();
}

QString QgsVectorLayer::subsetString() const
{
if ( !mValid || !mDataProvider )
Expand Down
2 changes: 1 addition & 1 deletion src/core/qgsvectorlayer.h
Expand Up @@ -1077,8 +1077,8 @@ class CORE_EXPORT QgsVectorLayer : public QgsMapLayer, public QgsExpressionConte
*/
virtual QgsMapLayerRenderer *createMapRenderer( QgsRenderContext &rendererContext ) override SIP_FACTORY;

//! Return the extent of the layer
QgsRectangle extent() const override;
QgsRectangle sourceExtent() const override;

/**
* Returns the list of fields of this layer.
Expand Down

0 comments on commit de9e70e

Please sign in to comment.