Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
[FEATURE] New processing algorithm "extract/clip by extent"
Allows extract a subset of another layer using an extent, with
optional setting to clip geometries to the extent
  • Loading branch information
nyalldawson committed Sep 14, 2017
1 parent ba9cb87 commit d8db3ec
Show file tree
Hide file tree
Showing 7 changed files with 250 additions and 2 deletions.
@@ -0,0 +1,33 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>extract_by_extent</Name>
<ElementPath>extract_by_extent</ElementPath>
<!--POLYGON-->
<GeometryType>3</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>3</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>10.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
<ExtentYMax>3.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>5</Width>
</PropertyDefn>
<PropertyDefn>
<Name>intval</Name>
<ElementPath>intval</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>floatval</Name>
<ElementPath>floatval</ElementPath>
<Type>String</Type>
<Width>18</Width>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-1</gml:X><gml:Y>-3</gml:Y></gml:coord>
<gml:coord><gml:X>6</gml:X><gml:Y>3</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:extract_by_extent fid="polys.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>aaaaa</ogr:name>
<ogr:intval>33</ogr:intval>
<ogr:floatval>44.123456</ogr:floatval>
</ogr:extract_by_extent>
</gml:featureMember>
<gml:featureMember>
<ogr:extract_by_extent fid="polys.5">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,2 6,1 6,-3 2,-1 2,2 3,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:name>elim</ogr:name>
<ogr:intval>2</ogr:intval>
<ogr:floatval>3.33</ogr:floatval>
</ogr:extract_by_extent>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -0,0 +1,32 @@
<GMLFeatureClassList>
<GMLFeatureClass>
<Name>extract_by_extent_clip</Name>
<ElementPath>extract_by_extent_clip</ElementPath>
<!--MULTIPOLYGON-->
<GeometryType>6</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>2</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>4.77500</ExtentXMax>
<ExtentYMin>-2.38750</ExtentYMin>
<ExtentYMax>3.00000</ExtentYMax>
</DatasetSpecificInfo>
<PropertyDefn>
<Name>name</Name>
<ElementPath>name</ElementPath>
<Type>String</Type>
<Width>5</Width>
</PropertyDefn>
<PropertyDefn>
<Name>intval</Name>
<ElementPath>intval</ElementPath>
<Type>Integer</Type>
</PropertyDefn>
<PropertyDefn>
<Name>floatval</Name>
<ElementPath>floatval</ElementPath>
<Type>Real</Type>
</PropertyDefn>
</GMLFeatureClass>
</GMLFeatureClassList>
@@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation=""
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>-1</gml:X><gml:Y>-2.3875</gml:Y></gml:coord>
<gml:coord><gml:X>4.775</gml:X><gml:Y>3</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:extract_by_extent_clip fid="polys.0">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>-1,-1 -1,3 3,3 3,2 2,2 2,-1 -1,-1</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:name>aaaaa</ogr:name>
<ogr:intval>33</ogr:intval>
<ogr:floatval>44.123456</ogr:floatval>
</ogr:extract_by_extent_clip>
</gml:featureMember>
<gml:featureMember>
<ogr:extract_by_extent_clip fid="polys.5">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,2 4.775,1.40833333333333 4.775,-2.3875 2,-1 2,2 3,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:name>elim</ogr:name>
<ogr:intval>2</ogr:intval>
<ogr:floatval>3.33</ogr:floatval>
</ogr:extract_by_extent_clip>
</gml:featureMember>
</ogr:FeatureCollection>
28 changes: 27 additions & 1 deletion python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml 100644 → 100755
Expand Up @@ -3937,4 +3937,30 @@ tests:
- name
compare:
fields:
fid: skip
fid: skip

- algorithm: native:extractbyextent
name: Extract by extent
params:
CLIP: false
EXTENT: -1.1650000000000003,4.775,-2.444285714285715,3.4171428571428573
INPUT:
name: polys.gml
type: vector
results:
OUTPUT:
name: expected/extract_by_extent.gml
type: vector

- algorithm: native:extractbyextent
name: Extract by extent (clipped)
params:
CLIP: true
EXTENT: -1.1650000000000003,4.775,-2.444285714285715,3.4171428571428573
INPUT:
name: polys.gml
type: vector
results:
OUTPUT:
name: expected/extract_by_extent_clip.gml
type: vector
73 changes: 72 additions & 1 deletion src/core/processing/qgsnativealgorithms.cpp
Expand Up @@ -82,6 +82,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsMergeLinesAlgorithm() );
addAlgorithm( new QgsSmoothAlgorithm() );
addAlgorithm( new QgsSimplifyAlgorithm() );
addAlgorithm( new QgsExtractByExtentAlgorithm() );
}

void QgsCentroidAlgorithm::initAlgorithm( const QVariantMap & )
Expand Down Expand Up @@ -1811,7 +1812,77 @@ QgsFeature QgsSimplifyAlgorithm::processFeature( const QgsFeature &feature, QgsP
return f;
}

///@endcond



void QgsExtractByExtentAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
addParameter( new QgsProcessingParameterExtent( QStringLiteral( "EXTENT" ), QObject::tr( "Extent" ) ) );
addParameter( new QgsProcessingParameterBoolean( QStringLiteral( "CLIP" ), QObject::tr( "Clip features to extent" ), false ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Extracted" ) ) );
}

QString QgsExtractByExtentAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm creates a new vector layer that only contains features which fall within a specified extent. "
"Any features which intersect the extent will be included.\n\n"
"Optionally, feature geometries can also be clipped to the extent. If this option is selected, then the output "
"geometries will automatically be converted to multi geometries to ensure uniform output geometry types." );
}

QgsExtractByExtentAlgorithm *QgsExtractByExtentAlgorithm::createInstance() const
{
return new QgsExtractByExtentAlgorithm();
}

QVariantMap QgsExtractByExtentAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
std::unique_ptr< QgsFeatureSource > featureSource( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !featureSource )
return QVariantMap();

QgsRectangle extent = parameterAsExtent( parameters, QStringLiteral( "EXTENT" ), context );
bool clip = parameterAsBool( parameters, QStringLiteral( "CLIP" ), context );

// if clipping, we force multi output
QgsWkbTypes::Type outType = clip ? QgsWkbTypes::multiType( featureSource->wkbType() ) : featureSource->wkbType();

QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, featureSource->fields(), outType, featureSource->sourceCrs() ) );

if ( !sink )
return QVariantMap();

QgsGeometry clipGeom = QgsGeometry::fromRect( extent );

double step = featureSource->featureCount() > 0 ? 100.0 / featureSource->featureCount() : 1;
QgsFeatureIterator inputIt = featureSource->getFeatures( QgsFeatureRequest().setFilterRect( extent ).setFlags( QgsFeatureRequest::ExactIntersect ) );
QgsFeature f;
int i = -1;
while ( inputIt.nextFeature( f ) )
{
i++;
if ( feedback->isCanceled() )
{
break;
}

if ( clip )
{
QgsGeometry g = f.geometry().intersection( clipGeom );
g.convertToMultiType();
f.setGeometry( g );
}

sink->addFeature( f, QgsFeatureSink::FastInsert );
feedback->setProgress( i * step );
}

QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
return outputs;
}

///@endcond

26 changes: 26 additions & 0 deletions src/core/processing/qgsnativealgorithms.h
Expand Up @@ -662,6 +662,32 @@ class QgsSimplifyAlgorithm : public QgsProcessingFeatureBasedAlgorithm
std::unique_ptr< QgsMapToPixelSimplifier > mSimplifier;

};


/**
* Native extract/clip by extent algorithm.
*/
class QgsExtractByExtentAlgorithm : public QgsProcessingAlgorithm
{

public:

QgsExtractByExtentAlgorithm() = default;
void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
QString name() const override { return QStringLiteral( "extractbyextent" ); }
QString displayName() const override { return QObject::tr( "Extract/clip by extent" ); }
virtual QStringList tags() const override { return QObject::tr( "clip,extract,intersect,intersection,mask,extent" ).split( ',' ); }
QString group() const override { return QObject::tr( "Vector overlay" ); }
QString shortHelpString() const override;
QgsExtractByExtentAlgorithm *createInstance() const override SIP_FACTORY;

protected:

virtual QVariantMap processAlgorithm( const QVariantMap &parameters,
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;

};

///@endcond PRIVATE

#endif // QGSNATIVEALGORITHMS_H
Expand Down

0 comments on commit d8db3ec

Please sign in to comment.