Port Intersection algorithm to C++, cleanups and tests

Finally starting a suite of unit tests for overlay algorithms:
- overlay1 - layers that cover various basic overlay situations
- overlay2 - layers where one input has self-intersecting polygons
- overlay3 - layers where intersections return different geometry types
This commit is contained in:
Martin Dobias 2018-04-17 17:21:15 +02:00
parent 3400199eb8
commit 4f829fde62
15 changed files with 547 additions and 0 deletions

View File

@ -0,0 +1,10 @@
{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3857" } },
"features": [
{ "type": "Feature", "properties": { "id_a": "A1" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 1.0, 3.0 ], [ 2.0, 3.0 ], [ 2.0, 10.0 ], [ 8.0, 10.0 ], [ 8.0, 11.0 ], [ 1.0, 11.0 ], [ 1.0, 3.0 ] ] ] } },
{ "type": "Feature", "properties": { "id_a": "A4" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3.0, 3.0 ], [ 3.0, 4.0 ], [ 4.0, 4.0 ], [ 4.0, 3.0 ], [ 3.0, 3.0 ] ] ] } },
{ "type": "Feature", "properties": { "id_a": "A2" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3.0, 5.0 ], [ 6.0, 5.0 ], [ 6.0, 6.0 ], [ 3.0, 6.0 ], [ 3.0, 5.0 ] ] ] } },
{ "type": "Feature", "properties": { "id_a": "A3" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.0, 7.0 ], [ 9.0, 7.0 ], [ 9.0, 8.0 ], [ 5.0, 8.0 ], [ 5.0, 7.0 ] ] ] } }
]
}

View File

@ -0,0 +1,10 @@
{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3857" } },
"features": [
{ "type": "Feature", "properties": { "id_b": "B1" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 1.0, 1.0 ], [ 8.0, 1.0 ], [ 8.0, 9.0 ], [ 7.0, 9.0 ], [ 7.0, 2.0 ], [ 1.0, 2.0 ], [ 1.0, 1.0 ] ] ] } },
{ "type": "Feature", "properties": { "id_b": "B4" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.0, 3.0 ], [ 6.0, 3.0 ], [ 6.0, 4.0 ], [ 5.0, 4.0 ], [ 5.0, 3.0 ] ] ] } },
{ "type": "Feature", "properties": { "id_b": "B2" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 0.0, 5.0 ], [ 4.0, 5.0 ], [ 4.0, 6.0 ], [ 0.0, 6.0 ], [ 0.0, 5.0 ] ] ] } },
{ "type": "Feature", "properties": { "id_b": "B3" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3.0, 7.0 ], [ 6.0, 7.0 ], [ 6.0, 8.0 ], [ 3.0, 8.0 ], [ 3.0, 7.0 ] ] ] } }
]
}

View File

@ -0,0 +1,7 @@
{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3857" } },
"features": [
{ "type": "Feature", "properties": { "id_a": "A1" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 3.0, 3.0 ], [ 3.0, 7.0 ], [ 6.0, 7.0 ], [ 6.0, 3.0 ], [ 3.0, 3.0 ] ] ] } }
]
}

View File

@ -0,0 +1,8 @@
{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3857" } },
"features": [
{ "type": "Feature", "properties": { "id_b": "B1" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2.0, 4.0 ], [ 5.0, 4.0 ], [ 5.0, 6.0 ], [ 2.0, 6.0 ], [ 2.0, 4.0 ] ] ] } },
{ "type": "Feature", "properties": { "id_b": "B2" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 7.0, 4.0 ], [ 4.0, 4.0 ], [ 4.0, 6.0 ], [ 7.0, 6.0 ], [ 7.0, 4.0 ] ] ] } }
]
}

View File

@ -0,0 +1,7 @@
{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3857" } },
"features": [
{ "type": "Feature", "properties": { "id_a": "A1" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2.0, 2.0 ], [ 7.0, 2.0 ], [ 7.0, 3.0 ], [ 2.0, 3.0 ], [ 2.0, 2.0 ] ] ] } }
]
}

View File

@ -0,0 +1,10 @@
{
"type": "FeatureCollection",
"crs": { "type": "name", "properties": { "name": "urn:ogc:def:crs:EPSG::3857" } },
"features": [
{ "type": "Feature", "properties": { "id_b": "B1" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 2.0, 1.0 ], [ 4.0, 1.0 ], [ 4.0, 2.0 ], [ 3.0, 2.0 ], [ 3.0, 3.0 ], [ 2.0, 3.0 ], [ 2.0, 1.0 ] ] ] } },
{ "type": "Feature", "properties": { "id_b": "B2" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 1.0, 3.0 ], [ 2.0, 3.0 ], [ 2.0, 4.0 ], [ 1.0, 4.0 ], [ 1.0, 3.0 ] ] ] } },
{ "type": "Feature", "properties": { "id_b": "B3" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 7.0, 1.0 ], [ 8.0, 1.0 ], [ 8.0, 4.0 ], [ 7.0, 4.0 ], [ 7.0, 1.0 ] ] ] } },
{ "type": "Feature", "properties": { "id_b": "B4" }, "geometry": { "type": "Polygon", "coordinates": [ [ [ 5.0, 2.0 ], [ 6.0, 2.0 ], [ 6.0, 5.0 ], [ 4.0, 5.0 ], [ 4.0, 3.0 ], [ 5.0, 4.0 ], [ 5.0, 2.0 ] ] ] } }
]
}

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ intersection1.xsd"
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>5</gml:Y></gml:coord>
<gml:coord><gml:X>8</gml:X><gml:Y>8</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>
<gml:featureMember>
<ogr:intersection1 fid="intersection1.0">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,6 2,5 1,5 1,6 2,6</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a>A1</ogr:id_a>
<ogr:id_b>B2</ogr:id_b>
</ogr:intersection1>
</gml:featureMember>
<gml:featureMember>
<ogr:intersection1 fid="intersection1.1">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>4,5 3,5 3,6 4,6 4,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a>A2</ogr:id_a>
<ogr:id_b>B2</ogr:id_b>
</ogr:intersection1>
</gml:featureMember>
<gml:featureMember>
<ogr:intersection1 fid="intersection1.2">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>8,7 7,7 7,8 8,8 8,7</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a>A3</ogr:id_a>
<ogr:id_b>B1</ogr:id_b>
</ogr:intersection1>
</gml:featureMember>
<gml:featureMember>
<ogr:intersection1 fid="intersection1.3">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,7 5,7 5,8 6,8 6,7</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a>A3</ogr:id_a>
<ogr:id_b>B3</ogr:id_b>
</ogr:intersection1>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="intersection1" type="ogr:intersection1_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="intersection1_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:MultiPolygonPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
<xs:element name="id_a" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="id_b" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -0,0 +1,28 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ intersection3.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>2</gml:X><gml:Y>2</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:intersection3 fid="intersection3.0">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,2 2,2 2,3 3,3 3,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a>A1</ogr:id_a>
<ogr:id_b>B1</ogr:id_b>
</ogr:intersection3>
</gml:featureMember>
<gml:featureMember>
<ogr:intersection3 fid="intersection3.1">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,2 5,2 5,3 6,3 6,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a>A1</ogr:id_a>
<ogr:id_b>B4</ogr:id_b>
</ogr:intersection3>
</gml:featureMember>
</ogr:FeatureCollection>

View File

@ -0,0 +1,37 @@
<?xml version="1.0" encoding="UTF-8"?>
<xs:schema targetNamespace="http://ogr.maptools.org/" xmlns:ogr="http://ogr.maptools.org/" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:gml="http://www.opengis.net/gml" elementFormDefault="qualified" version="1.0">
<xs:import namespace="http://www.opengis.net/gml" schemaLocation="http://schemas.opengis.net/gml/2.1.2/feature.xsd"/>
<xs:element name="FeatureCollection" type="ogr:FeatureCollectionType" substitutionGroup="gml:_FeatureCollection"/>
<xs:complexType name="FeatureCollectionType">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureCollectionType">
<xs:attribute name="lockId" type="xs:string" use="optional"/>
<xs:attribute name="scope" type="xs:string" use="optional"/>
</xs:extension>
</xs:complexContent>
</xs:complexType>
<xs:element name="intersection3" type="ogr:intersection3_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="intersection3_Type">
<xs:complexContent>
<xs:extension base="gml:AbstractFeatureType">
<xs:sequence>
<xs:element name="geometryProperty" type="gml:MultiPolygonPropertyType" nillable="true" minOccurs="0" maxOccurs="1"/>
<xs:element name="id_a" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
<xs:element name="id_b" nillable="true" minOccurs="0" maxOccurs="1">
<xs:simpleType>
<xs:restriction base="xs:string">
<xs:maxLength value="255"/>
</xs:restriction>
</xs:simpleType>
</xs:element>
</xs:sequence>
</xs:extension>
</xs:complexContent>
</xs:complexType>
</xs:schema>

View File

@ -5191,4 +5191,40 @@ tests:
name: expected/filter_points_big.gml
type: vector
- algorithm: native:intersection
name: Test Intersection (basic)
params:
INPUT:
name: custom/overlay1_a.geojson
type: vector
OVERLAY:
name: custom/overlay1_b.geojson
type: vector
results:
OUTPUT:
name: expected/intersection1.gml
type: vector
pk: [id_a, id_b]
compare:
fields:
fid: skip
- algorithm: native:intersection
name: Test Intersection (geom types)
params:
INPUT:
name: custom/overlay3_a.geojson
type: vector
OVERLAY:
name: custom/overlay3_b.geojson
type: vector
results:
OUTPUT:
name: expected/intersection3.gml
type: vector
pk: [id_a, id_b]
compare:
fields:
fid: skip
# See ../README.md for a description of the file format

View File

@ -40,6 +40,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmfiledownloader.cpp
processing/qgsalgorithmfilter.cpp
processing/qgsalgorithmfixgeometries.cpp
processing/qgsalgorithmintersection.cpp
processing/qgsalgorithmjoinbyattribute.cpp
processing/qgsalgorithmjoinwithlines.cpp
processing/qgsalgorithmimportphotos.cpp

View File

@ -0,0 +1,268 @@
/***************************************************************************
qgsalgorithmintersection.cpp
---------------------
Date : April 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#include "qgsalgorithmintersection.h"
#include "qgsgeometrycollection.h"
#include "qgsgeometryengine.h"
#include "qgsmultipoint.h"
#include "qgsmultipolygon.h"
#include "qgsmultilinestring.h"
///@cond PRIVATE
static QgsGeometry geometryCollectionToSingleGeometryType( const QgsGeometry &geom, QgsWkbTypes::GeometryType geomType )
{
const QgsGeometryCollection *origGeom = qgsgeometry_cast<const QgsGeometryCollection *>( geom.constGet() );
if ( !origGeom )
return QgsGeometry();
QgsGeometryCollection *resGeom = nullptr;
switch ( geomType )
{
case QgsWkbTypes::PointGeometry:
resGeom = new QgsMultiPoint;
break;
case QgsWkbTypes::LineGeometry:
resGeom = new QgsMultiLineString;
break;
case QgsWkbTypes::PolygonGeometry:
resGeom = new QgsMultiPolygon;
break;
default:
break;
}
if ( !resGeom )
return QgsGeometry();
for ( int i = 0; i < origGeom->numGeometries(); ++i )
{
const QgsAbstractGeometry *g = origGeom->geometryN( i );
if ( QgsWkbTypes::geometryType( g->wkbType() ) == geomType )
resGeom->addGeometry( g->clone() );
}
return QgsGeometry( resGeom );
}
QString QgsIntersectionAlgorithm::name() const
{
return QStringLiteral( "intersection" );
}
QString QgsIntersectionAlgorithm::displayName() const
{
return QObject::tr( "Intersection" );
}
QString QgsIntersectionAlgorithm::group() const
{
return QObject::tr( "Vector overlay" );
}
QString QgsIntersectionAlgorithm::groupId() const
{
return QStringLiteral( "vectoroverlay" );
}
QgsProcessingAlgorithm *QgsIntersectionAlgorithm::createInstance() const
{
return new QgsIntersectionAlgorithm();
}
void QgsIntersectionAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "OVERLAY" ), QObject::tr( "Intersection layer" ) ) );
addParameter( new QgsProcessingParameterField(
QStringLiteral( "INPUT_FIELDS" ),
QObject::tr( "Input fields to keep (leave empty to keep all fields)" ), QVariant(),
QStringLiteral( "INPUT" ), QgsProcessingParameterField::Any, true, true ) );
addParameter( new QgsProcessingParameterField(
QStringLiteral( "OVERLAY_FIELDS" ),
QObject::tr( "Intersect fields to keep (leave empty to keep all fields)" ), QVariant(),
QStringLiteral( "OVERLAY" ), QgsProcessingParameterField::Any, true, true ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Intersection" ) ) );
}
QVariantMap QgsIntersectionAlgorithm::processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback )
{
std::unique_ptr< QgsFeatureSource > sourceA( parameterAsSource( parameters, QStringLiteral( "INPUT" ), context ) );
if ( !sourceA )
throw QgsProcessingException( QObject::tr( "Could not load source layer for INPUT" ) );
std::unique_ptr< QgsFeatureSource > sourceB( parameterAsSource( parameters, QStringLiteral( "OVERLAY" ), context ) );
if ( !sourceB )
throw QgsProcessingException( QObject::tr( "Could not load source layer for OVERLAY" ) );
QgsWkbTypes::Type geomType = QgsWkbTypes::multiType( sourceA->wkbType() );
const QStringList fieldsA = parameterAsFields( parameters, QStringLiteral( "INPUT_FIELDS" ), context );
const QStringList fieldsB = parameterAsFields( parameters, QStringLiteral( "OVERLAY_FIELDS" ), context );
QgsFields fieldListA;
QList<int> fieldIndicesA;
if ( !fieldsA.isEmpty() )
{
for ( const QString &f : fieldsA )
{
int idxA = sourceA->fields().lookupField( f );
if ( idxA >= 0 )
{
fieldIndicesA.append( idxA );
fieldListA.append( sourceA->fields()[idxA] );
}
}
}
else
{
fieldListA = sourceA->fields();
for ( int i = 0; i < fieldListA.count(); ++i )
fieldIndicesA.append( i );
}
QgsFields fieldListB;
QList<int> fieldIndicesB;
if ( !fieldsB.isEmpty() )
{
for ( const QString &f : fieldsB )
{
int idxB = sourceB->fields().lookupField( f );
if ( idxB >= 0 )
{
fieldIndicesB.append( idxB );
fieldListB.append( sourceB->fields()[idxB] );
}
}
}
else
{
fieldListB = sourceB->fields();
for ( int i = 0; i < fieldListB.count(); ++i )
fieldIndicesB.append( i );
}
QgsFields outputFields = QgsProcessingUtils::combineFields( fieldListA, fieldListB );
QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, outputFields, geomType, sourceA->sourceCrs() ) );
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
QgsFeatureRequest request;
request.setSubsetOfAttributes( QgsAttributeList() );
request.setDestinationCrs( sourceA->sourceCrs(), context.transformContext() );
QgsFeature outFeat;
QgsSpatialIndex indexB( sourceB->getFeatures( request ), feedback );
double total = 100.0 / ( sourceA->featureCount() ? sourceA->featureCount() : 1 );
int count = 0;
QgsFeature featA;
QgsFeatureIterator fitA = sourceA->getFeatures( QgsFeatureRequest().setSubsetOfAttributes( fieldIndicesA ) );
while ( fitA.nextFeature( featA ) )
{
if ( feedback->isCanceled() )
break;
if ( !featA.hasGeometry() )
continue;
QgsGeometry geom( featA.geometry() );
QgsFeatureIds intersects = indexB.intersects( geom.boundingBox() ).toSet();
QgsFeatureRequest request;
request.setFilterFids( intersects );
request.setDestinationCrs( sourceA->sourceCrs(), context.transformContext() );
request.setSubsetOfAttributes( fieldIndicesB );
std::unique_ptr< QgsGeometryEngine > engine;
if ( !intersects.isEmpty() )
{
// use prepared geometries for faster intersection tests
engine.reset( QgsGeometry::createGeometryEngine( geom.constGet() ) );
engine->prepareGeometry();
}
QgsAttributes outAttributes( outputFields.count() );
const QgsAttributes attrsA( featA.attributes() );
for ( int i = 0; i < fieldIndicesA.count(); ++i )
outAttributes[i] = attrsA[fieldIndicesA[i]];
QgsFeature featB;
QgsFeatureIterator fitB = sourceB->getFeatures( request );
while ( fitB.nextFeature( featB ) )
{
if ( feedback->isCanceled() )
break;
QgsGeometry tmpGeom( featB.geometry() );
if ( !engine->intersects( tmpGeom.constGet() ) )
continue;
QgsGeometry intGeom = geom.intersection( tmpGeom );
if ( intGeom.isNull() )
{
// TODO: not sure if this ever happens - if it does, that means GEOS failed badly and we should try to provide an error from it
throw QgsProcessingException( QObject::tr( "GEOS geoprocessing error: intersection failed." ) );
}
// Intersection of geometries may give use also geometries we do not want in our results.
// For example, two square polygons touching at the corner have a point as the intersection, but no area.
// In other cases we may get a mixture of geometries in the output - we want to keep only the expected types.
if ( QgsWkbTypes::geometryType( intGeom.wkbType() ) != QgsWkbTypes::geometryType( geomType ) )
{
if ( QgsWkbTypes::flatType( intGeom.wkbType() ) == QgsWkbTypes::GeometryCollection )
{
// try to filter out irrelevant parts with different geometry type than what we want
intGeom = geometryCollectionToSingleGeometryType( intGeom, QgsWkbTypes::geometryType( geomType ) );
if ( intGeom.isEmpty() )
continue;
}
else
{
// we can't make use of this resulting geometry -
continue;
}
}
const QgsAttributes attrsB( featB.attributes() );
for ( int i = 0; i < fieldIndicesB.count(); ++i )
outAttributes[fieldIndicesA.count() + i] = attrsB[fieldIndicesB[i]];
intGeom.convertToMultiType();
outFeat.setGeometry( intGeom );
outFeat.setAttributes( outAttributes );
sink->addFeature( outFeat, QgsFeatureSink::FastInsert );
}
++count;
feedback->setProgress( int( count * total ) );
}
return outputs;
}
///@endcond

View File

@ -0,0 +1,44 @@
/***************************************************************************
qgsalgorithmintersection.h
---------------------
Date : April 2018
Copyright : (C) 2018 by Martin Dobias
Email : wonder dot sk at gmail dot com
***************************************************************************
* *
* This program is free software; you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation; either version 2 of the License, or *
* (at your option) any later version. *
* *
***************************************************************************/
#ifndef QGSALGORITHMINTERSECTION_H
#define QGSALGORITHMINTERSECTION_H
#define SIP_NO_FILE
#include "qgsprocessingalgorithm.h"
///@cond PRIVATE
class QgsIntersectionAlgorithm : public QgsProcessingAlgorithm
{
public:
QgsIntersectionAlgorithm() = default;
virtual QString name() const override;
virtual QString displayName() const override;
virtual QString group() const override;
virtual QString groupId() const override;
protected:
virtual QgsProcessingAlgorithm *createInstance() const override;
virtual void initAlgorithm( const QVariantMap &configuration = QVariantMap() ) override;
virtual QVariantMap processAlgorithm( const QVariantMap &parameters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
};
///@endcond PRIVATE
#endif // QGSALGORITHMINTERSECTION_H

View File

@ -40,6 +40,7 @@
#include "qgsalgorithmjoinbyattribute.h"
#include "qgsalgorithmjoinwithlines.h"
#include "qgsalgorithmimportphotos.h"
#include "qgsalgorithmintersection.h"
#include "qgsalgorithmlineintersection.h"
#include "qgsalgorithmloadlayer.h"
#include "qgsalgorithmmeancoordinates.h"
@ -137,6 +138,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsFilterAlgorithm() );
addAlgorithm( new QgsFixGeometriesAlgorithm() );
addAlgorithm( new QgsImportPhotosAlgorithm() );
addAlgorithm( new QgsIntersectionAlgorithm() );
addAlgorithm( new QgsJoinByAttributeAlgorithm() );
addAlgorithm( new QgsJoinWithLinesAlgorithm() );
addAlgorithm( new QgsLineIntersectionAlgorithm() );