Port Union algorithm to c++ using existing intersection+difference algs

This commit is contained in:
Martin Dobias 2018-04-25 17:22:40 +02:00
parent 85b2efae2c
commit 8a96573880
12 changed files with 417 additions and 256 deletions

View File

@ -530,10 +530,6 @@ qgis:truncatetable: >
Warning - this algorithm modifies the layer in place, and deleted features cannot be restored!
qgis:union: >
This algorithm creates a layer containing all the features from both input layers. In the case of polygon layers, separate features are created for overlapping and non-overlapping features. The attribute table of the union layer contains attribute values from the respective input layer for non-overlapping features, and attribute values from both input layers for overlapping features.
qgis:variabledistancebuffer: >
This algorithm computes a buffer area for all the features in an input layer. The size of the buffer for a given feature is defined by an attribute, so it allows different features to have different buffer sizes.

View File

@ -137,7 +137,6 @@ from .TextToFloat import TextToFloat
from .TinInterpolation import TinInterpolation
from .TopoColors import TopoColor
from .TruncateTable import TruncateTable
from .Union import Union
from .UniqueValues import UniqueValues
from .VariableDistanceBuffer import VariableDistanceBuffer
from .VectorSplit import VectorSplit
@ -253,7 +252,6 @@ class QgisAlgorithmProvider(QgsProcessingProvider):
TinInterpolation(),
TopoColor(),
TruncateTable(),
Union(),
UniqueValues(),
VariableDistanceBuffer(),
VectorSplit(),

View File

@ -1,250 +0,0 @@
# -*- coding: utf-8 -*-
"""
***************************************************************************
Union.py
---------------------
Date : August 2012
Copyright : (C) 2012 by Victor Olaya
Email : volayaf 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. *
* *
***************************************************************************
"""
__author__ = 'Victor Olaya'
__date__ = 'August 2012'
__copyright__ = '(C) 2012, Victor Olaya'
# This will get replaced with a git SHA1 when you do a git archive
__revision__ = '$Format:%H$'
import os
from qgis.PyQt.QtGui import QIcon
from qgis.core import (QgsFeatureRequest,
QgsFeature,
QgsFeatureSink,
QgsGeometry,
QgsWkbTypes,
QgsProcessingUtils,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsSpatialIndex)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.tools import vector
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
class Union(QgisAlgorithm):
INPUT = 'INPUT'
OVERLAY = 'OVERLAY'
OUTPUT = 'OUTPUT'
def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'union.png'))
def group(self):
return self.tr('Vector overlay')
def groupId(self):
return 'vectoroverlay'
def __init__(self):
super().__init__()
def initAlgorithm(self, config=None):
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Input layer')))
self.addParameter(QgsProcessingParameterFeatureSource(self.OVERLAY,
self.tr('Union layer')))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Union')))
def name(self):
return 'union'
def displayName(self):
return self.tr('Union')
def processAlgorithm(self, parameters, context, feedback):
sourceA = self.parameterAsSource(parameters, self.INPUT, context)
sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)
geomType = QgsWkbTypes.multiType(sourceA.wkbType())
fields = QgsProcessingUtils.combineFields(sourceA.fields(), sourceB.fields())
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, geomType, sourceA.sourceCrs())
featA = QgsFeature()
featB = QgsFeature()
outFeat = QgsFeature()
indexA = QgsSpatialIndex(sourceA, feedback)
indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs(), context.transformContext())), feedback)
total = 100.0 / (sourceA.featureCount() * sourceB.featureCount()) if sourceA.featureCount() and sourceB.featureCount() else 1
count = 0
for featA in sourceA.getFeatures():
if feedback.isCanceled():
break
lstIntersectingB = []
geom = featA.geometry()
atMapA = featA.attributes()
intersects = indexB.intersects(geom.boundingBox())
if len(intersects) < 1:
try:
geom.convertToMultiType()
outFeat.setGeometry(geom)
outFeat.setAttributes(atMapA)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
# This really shouldn't happen, as we haven't
# edited the input geom at all
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
else:
request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext())
engine = QgsGeometry.createGeometryEngine(geom.constGet())
engine.prepareGeometry()
for featB in sourceB.getFeatures(request):
atMapB = featB.attributes()
tmpGeom = featB.geometry()
if engine.intersects(tmpGeom.constGet()):
int_geom = geom.intersection(tmpGeom)
lstIntersectingB.append(tmpGeom)
if not int_geom:
# There was a problem creating the intersection
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
int_geom = QgsGeometry()
else:
int_geom = QgsGeometry(int_geom)
if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.wkbType()) == QgsWkbTypes.GeometryCollection:
# Intersection produced different geomety types
temp_list = int_geom.asGeometryCollection()
for i in temp_list:
if i.type() == geom.type():
int_geom = QgsGeometry(i)
try:
int_geom.convertToMultiType()
outFeat.setGeometry(int_geom)
outFeat.setAttributes(atMapA + atMapB)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
else:
# Geometry list: prevents writing error
# in geometries of different types
# produced by the intersection
# fix #3549
if QgsWkbTypes.geometryType(int_geom.wkbType()) == QgsWkbTypes.geometryType(geomType):
try:
int_geom.convertToMultiType()
outFeat.setGeometry(int_geom)
outFeat.setAttributes(atMapA + atMapB)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
# the remaining bit of featA's geometry
# if there is nothing left, this will just silently fail and we're good
diff_geom = QgsGeometry(geom)
if len(lstIntersectingB) != 0:
intB = QgsGeometry.unaryUnion(lstIntersectingB)
diff_geom = diff_geom.difference(intB)
if diff_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(diff_geom.wkbType()) == QgsWkbTypes.GeometryCollection:
temp_list = diff_geom.asGeometryCollection()
for i in temp_list:
if i.type() == geom.type():
diff_geom = QgsGeometry(i)
try:
diff_geom.convertToMultiType()
outFeat.setGeometry(diff_geom)
outFeat.setAttributes(atMapA)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
count += 1
feedback.setProgress(int(count * total))
length = len(sourceA.fields())
atMapA = [None] * length
for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs(), context.transformContext())):
if feedback.isCanceled():
break
add = False
geom = featA.geometry()
diff_geom = QgsGeometry(geom)
atMap = [None] * length
atMap.extend(featA.attributes())
intersects = indexA.intersects(geom.boundingBox())
if len(intersects) < 1:
try:
geom.convertToMultiType()
outFeat.setGeometry(geom)
outFeat.setAttributes(atMap)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
else:
request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
request.setDestinationCrs(sourceA.sourceCrs(), context.transformContext())
# use prepared geometries for faster intersection tests
engine = QgsGeometry.createGeometryEngine(diff_geom.constGet())
engine.prepareGeometry()
for featB in sourceA.getFeatures(request):
atMapB = featB.attributes()
tmpGeom = featB.geometry()
if engine.intersects(tmpGeom.constGet()):
add = True
diff_geom = QgsGeometry(diff_geom.difference(tmpGeom))
else:
try:
# Ihis only happens if the bounding box
# intersects, but the geometry doesn't
diff_geom.convertToMultiType()
outFeat.setGeometry(diff_geom)
outFeat.setAttributes(atMap)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
if add:
try:
diff_geom.convertToMultiType()
outFeat.setGeometry(diff_geom)
outFeat.setAttributes(atMap)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
feedback.pushInfo(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'))
count += 1
feedback.setProgress(int(count * total))
return {self.OUTPUT: dest_id}

View File

@ -0,0 +1,98 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ union1.xsd"
xmlns:ogr="http://ogr.maptools.org/"
xmlns:gml="http://www.opengis.net/gml">
<gml:boundedBy>
<gml:Box>
<gml:coord><gml:X>0</gml:X><gml:Y>1</gml:Y></gml:coord>
<gml:coord><gml:X>9</gml:X><gml:Y>11</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>
<gml:featureMember>
<ogr:union1 fid="union1.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:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.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:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.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:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.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:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.4">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,5 2,3 1,3 1,5 2,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,6 1,11 8,11 8,10 2,10 2,6 1,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 xsi:nil="true"/>
</ogr:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.5">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,3 3,4 4,4 4,3 3,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a>A4</ogr:id_a>
<ogr:id_b xsi:nil="true"/>
</ogr:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.6">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>4,6 6,6 6,5 4,5 4,6</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a>A2</ogr:id_a>
<ogr:id_b xsi:nil="true"/>
</ogr:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.7">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>7,7 6,7 6,8 7,8 7,7</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>8,8 9,8 9,7 8,7 8,8</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a>A3</ogr:id_a>
<ogr:id_b xsi:nil="true"/>
</ogr:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.8">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>8,7 8,1 1,1 1,2 7,2 7,7 8,7</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>7,8 7,9 8,9 8,8 7,8</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a xsi:nil="true"/>
<ogr:id_b>B1</ogr:id_b>
</ogr:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.9">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,3 6,3 6,4 5,4 5,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a xsi:nil="true"/>
<ogr:id_b>B4</ogr:id_b>
</ogr:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.10">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>1,5 0,5 0,6 1,6 1,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>3,5 2,5 2,6 3,6 3,5</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a xsi:nil="true"/>
<ogr:id_b>B2</ogr:id_b>
</ogr:union1>
</gml:featureMember>
<gml:featureMember>
<ogr:union1 fid="union1.11">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>5,7 3,7 3,8 5,8 5,7</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a xsi:nil="true"/>
<ogr:id_b>B3</ogr:id_b>
</ogr:union1>
</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="union1" type="ogr:union1_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="union1_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,63 @@
<?xml version="1.0" encoding="utf-8" ?>
<ogr:FeatureCollection
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://ogr.maptools.org/ union3.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>1</gml:Y></gml:coord>
<gml:coord><gml:X>8</gml:X><gml:Y>5</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>
<gml:featureMember>
<ogr:union3 fid="union3.0">
<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:union3>
</gml:featureMember>
<gml:featureMember>
<ogr:union3 fid="union3.1">
<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:union3>
</gml:featureMember>
<gml:featureMember>
<ogr:union3 fid="union3.2">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>4,2 3,2 3,3 4,3 5,3 5,2 4,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>7,2 6,2 6,3 7,3 7,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 xsi:nil="true"/>
</ogr:union3>
</gml:featureMember>
<gml:featureMember>
<ogr:union3 fid="union3.3">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>4,2 4,1 2,1 2,2 3,2 4,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a xsi:nil="true"/>
<ogr:id_b>B1</ogr:id_b>
</ogr:union3>
</gml:featureMember>
<gml:featureMember>
<ogr:union3 fid="union3.4">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,3 1,3 1,4 2,4 2,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a xsi:nil="true"/>
<ogr:id_b>B2</ogr:id_b>
</ogr:union3>
</gml:featureMember>
<gml:featureMember>
<ogr:union3 fid="union3.5">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>7,3 7,4 8,4 8,1 7,1 7,2 7,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a xsi:nil="true"/>
<ogr:id_b>B3</ogr:id_b>
</ogr:union3>
</gml:featureMember>
<gml:featureMember>
<ogr:union3 fid="union3.6">
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:3857"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>4,3 4,5 6,5 6,3 5,3 5,4 4,3</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
<ogr:id_a xsi:nil="true"/>
<ogr:id_b>B4</ogr:id_b>
</ogr:union3>
</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="union3" type="ogr:union3_Type" substitutionGroup="gml:_Feature"/>
<xs:complexType name="union3_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

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

View File

@ -78,6 +78,7 @@ SET(QGIS_ANALYSIS_SRCS
processing/qgsalgorithmtransect.cpp
processing/qgsalgorithmtransform.cpp
processing/qgsalgorithmtranslate.cpp
processing/qgsalgorithmunion.cpp
processing/qgsalgorithmuniquevalueindex.cpp
processing/qgsalgorithmwedgebuffers.cpp

View File

@ -0,0 +1,97 @@
/***************************************************************************
qgsalgorithmunion.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 "qgsalgorithmunion.h"
#include "qgsoverlayutils.h"
///@cond PRIVATE
QString QgsUnionAlgorithm::name() const
{
return QStringLiteral( "union" );
}
QString QgsUnionAlgorithm::displayName() const
{
return QObject::tr( "Union" );
}
QString QgsUnionAlgorithm::group() const
{
return QObject::tr( "Vector overlay" );
}
QString QgsUnionAlgorithm::groupId() const
{
return QStringLiteral( "vectoroverlay" );
}
QString QgsUnionAlgorithm::shortHelpString() const
{
return QObject::tr( "This algorithm creates a layer containing all the features from both input layers. In the case of polygon layers, separate features are created for overlapping and non-overlapping features. The attribute table of the union layer contains attribute values from the respective input layer for non-overlapping features, and attribute values from both input layers for overlapping features." );
}
QgsProcessingAlgorithm *QgsUnionAlgorithm::createInstance() const
{
return new QgsUnionAlgorithm();
}
void QgsUnionAlgorithm::initAlgorithm( const QVariantMap & )
{
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "INPUT" ), QObject::tr( "Input layer" ) ) );
addParameter( new QgsProcessingParameterFeatureSource( QStringLiteral( "OVERLAY" ), QObject::tr( "Union layer" ) ) );
addParameter( new QgsProcessingParameterFeatureSink( QStringLiteral( "OUTPUT" ), QObject::tr( "Union" ) ) );
}
QVariantMap QgsUnionAlgorithm::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() );
QgsFields fields = QgsProcessingUtils::combineFields( sourceA->fields(), sourceB->fields() );
QString dest;
std::unique_ptr< QgsFeatureSink > sink( parameterAsSink( parameters, QStringLiteral( "OUTPUT" ), context, dest, fields, geomType, sourceA->sourceCrs() ) );
QVariantMap outputs;
outputs.insert( QStringLiteral( "OUTPUT" ), dest );
QList<int> fieldIndicesA = QgsOverlayUtils::fieldNamesToIndices( QStringList(), sourceA->fields() );
QList<int> fieldIndicesB = QgsOverlayUtils::fieldNamesToIndices( QStringList(), sourceB->fields() );
int count = 0;
int total = sourceA->featureCount() * 2 + sourceB->featureCount();
QgsOverlayUtils::intersection( *sourceA.get(), *sourceB.get(), *sink.get(), context, feedback, count, total, fieldIndicesA, fieldIndicesB );
QgsOverlayUtils::difference( *sourceA.get(), *sourceB.get(), *sink.get(), context, feedback, count, total, QgsOverlayUtils::OutputAB );
QgsOverlayUtils::difference( *sourceB.get(), *sourceA.get(), *sink.get(), context, feedback, count, total, QgsOverlayUtils::OutputBA );
return outputs;
}
///@endcond

View File

@ -0,0 +1,46 @@
/***************************************************************************
qgsalgorithmunion.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 QGSALGORITHMUNION_H
#define QGSALGORITHMUNION_H
#define SIP_NO_FILE
#include "qgsprocessingalgorithm.h"
///@cond PRIVATE
class QgsUnionAlgorithm : public QgsProcessingAlgorithm
{
public:
QgsUnionAlgorithm() = default;
virtual QString name() const override;
virtual QString displayName() const override;
virtual QString group() const override;
virtual QString groupId() const override;
QString shortHelpString() 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 // QGSALGORITHMUNION_H

View File

@ -75,6 +75,7 @@
#include "qgsalgorithmtransect.h"
#include "qgsalgorithmtransform.h"
#include "qgsalgorithmtranslate.h"
#include "qgsalgorithmunion.h"
#include "qgsalgorithmuniquevalueindex.h"
#include "qgsalgorithmwedgebuffers.h"
@ -180,6 +181,7 @@ void QgsNativeAlgorithms::loadAlgorithms()
addAlgorithm( new QgsTransectAlgorithm() );
addAlgorithm( new QgsTransformAlgorithm() );
addAlgorithm( new QgsTranslateAlgorithm() );
addAlgorithm( new QgsUnionAlgorithm() );
addAlgorithm( new QgsWedgeBuffersAlgorithm() );
}