mirror of
https://github.com/qgis/QGIS.git
synced 2025-04-14 00:07:35 -04:00
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:
parent
3400199eb8
commit
4f829fde62
10
python/plugins/processing/tests/testdata/custom/overlay1_a.geojson
vendored
Normal file
10
python/plugins/processing/tests/testdata/custom/overlay1_a.geojson
vendored
Normal 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 ] ] ] } }
|
||||
]
|
||||
}
|
10
python/plugins/processing/tests/testdata/custom/overlay1_b.geojson
vendored
Normal file
10
python/plugins/processing/tests/testdata/custom/overlay1_b.geojson
vendored
Normal 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 ] ] ] } }
|
||||
]
|
||||
}
|
7
python/plugins/processing/tests/testdata/custom/overlay2_a.geojson
vendored
Normal file
7
python/plugins/processing/tests/testdata/custom/overlay2_a.geojson
vendored
Normal 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 ] ] ] } }
|
||||
]
|
||||
}
|
8
python/plugins/processing/tests/testdata/custom/overlay2_b.geojson
vendored
Normal file
8
python/plugins/processing/tests/testdata/custom/overlay2_b.geojson
vendored
Normal 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 ] ] ] } }
|
||||
]
|
||||
}
|
7
python/plugins/processing/tests/testdata/custom/overlay3_a.geojson
vendored
Normal file
7
python/plugins/processing/tests/testdata/custom/overlay3_a.geojson
vendored
Normal 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 ] ] ] } }
|
||||
]
|
||||
}
|
10
python/plugins/processing/tests/testdata/custom/overlay3_b.geojson
vendored
Normal file
10
python/plugins/processing/tests/testdata/custom/overlay3_b.geojson
vendored
Normal 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 ] ] ] } }
|
||||
]
|
||||
}
|
42
python/plugins/processing/tests/testdata/expected/intersection1.gml
vendored
Normal file
42
python/plugins/processing/tests/testdata/expected/intersection1.gml
vendored
Normal 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>
|
37
python/plugins/processing/tests/testdata/expected/intersection1.xsd
vendored
Normal file
37
python/plugins/processing/tests/testdata/expected/intersection1.xsd
vendored
Normal 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>
|
28
python/plugins/processing/tests/testdata/expected/intersection3.gml
vendored
Normal file
28
python/plugins/processing/tests/testdata/expected/intersection3.gml
vendored
Normal 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>
|
37
python/plugins/processing/tests/testdata/expected/intersection3.xsd
vendored
Normal file
37
python/plugins/processing/tests/testdata/expected/intersection3.xsd
vendored
Normal 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>
|
@ -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
|
||||
|
@ -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
|
||||
|
268
src/analysis/processing/qgsalgorithmintersection.cpp
Normal file
268
src/analysis/processing/qgsalgorithmintersection.cpp
Normal 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 ¶meters, 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
|
44
src/analysis/processing/qgsalgorithmintersection.h
Normal file
44
src/analysis/processing/qgsalgorithmintersection.h
Normal 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 ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
|
||||
};
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
||||
#endif // QGSALGORITHMINTERSECTION_H
|
@ -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() );
|
||||
|
Loading…
x
Reference in New Issue
Block a user