mirror of
https://github.com/qgis/QGIS.git
synced 2025-10-18 00:06:00 -04:00
[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
This commit is contained in:
parent
ba9cb873ae
commit
d8db3ecc4d
33
python/plugins/processing/tests/testdata/expected/extract_by_extent.gfs
vendored
Executable file
33
python/plugins/processing/tests/testdata/expected/extract_by_extent.gfs
vendored
Executable file
@ -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>
|
30
python/plugins/processing/tests/testdata/expected/extract_by_extent.gml
vendored
Executable file
30
python/plugins/processing/tests/testdata/expected/extract_by_extent.gml
vendored
Executable file
@ -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>
|
32
python/plugins/processing/tests/testdata/expected/extract_by_extent_clip.gfs
vendored
Executable file
32
python/plugins/processing/tests/testdata/expected/extract_by_extent_clip.gfs
vendored
Executable file
@ -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>
|
30
python/plugins/processing/tests/testdata/expected/extract_by_extent_clip.gml
vendored
Executable file
30
python/plugins/processing/tests/testdata/expected/extract_by_extent_clip.gml
vendored
Executable file
@ -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
python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
vendored
Normal file → Executable file
28
python/plugins/processing/tests/testdata/qgis_algorithm_tests.yaml
vendored
Normal file → Executable file
@ -3937,4 +3937,30 @@ tests:
|
|||||||
- name
|
- name
|
||||||
compare:
|
compare:
|
||||||
fields:
|
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
|
||||||
|
@ -82,6 +82,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
|
|||||||
addAlgorithm( new QgsMergeLinesAlgorithm() );
|
addAlgorithm( new QgsMergeLinesAlgorithm() );
|
||||||
addAlgorithm( new QgsSmoothAlgorithm() );
|
addAlgorithm( new QgsSmoothAlgorithm() );
|
||||||
addAlgorithm( new QgsSimplifyAlgorithm() );
|
addAlgorithm( new QgsSimplifyAlgorithm() );
|
||||||
|
addAlgorithm( new QgsExtractByExtentAlgorithm() );
|
||||||
}
|
}
|
||||||
|
|
||||||
void QgsCentroidAlgorithm::initAlgorithm( const QVariantMap & )
|
void QgsCentroidAlgorithm::initAlgorithm( const QVariantMap & )
|
||||||
@ -1811,7 +1812,77 @@ QgsFeature QgsSimplifyAlgorithm::processFeature( const QgsFeature &feature, QgsP
|
|||||||
return f;
|
return f;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
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 ¶meters, 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
|
///@endcond
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -662,6 +662,32 @@ class QgsSimplifyAlgorithm : public QgsProcessingFeatureBasedAlgorithm
|
|||||||
std::unique_ptr< QgsMapToPixelSimplifier > mSimplifier;
|
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 ¶meters,
|
||||||
|
QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
///@endcond PRIVATE
|
///@endcond PRIVATE
|
||||||
|
|
||||||
#endif // QGSNATIVEALGORITHMS_H
|
#endif // QGSNATIVEALGORITHMS_H
|
||||||
|
Loading…
x
Reference in New Issue
Block a user