mirror of
https://github.com/qgis/QGIS.git
synced 2025-12-30 00:29:39 -05:00
Port Union algorithm to c++ using existing intersection+difference algs
This commit is contained in:
parent
85b2efae2c
commit
8a96573880
@ -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.
|
||||
|
||||
|
||||
@ -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(),
|
||||
|
||||
@ -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}
|
||||
98
python/plugins/processing/tests/testdata/expected/union1.gml
vendored
Normal file
98
python/plugins/processing/tests/testdata/expected/union1.gml
vendored
Normal 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>
|
||||
37
python/plugins/processing/tests/testdata/expected/union1.xsd
vendored
Normal file
37
python/plugins/processing/tests/testdata/expected/union1.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="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>
|
||||
63
python/plugins/processing/tests/testdata/expected/union3.gml
vendored
Normal file
63
python/plugins/processing/tests/testdata/expected/union3.gml
vendored
Normal 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>
|
||||
37
python/plugins/processing/tests/testdata/expected/union3.xsd
vendored
Normal file
37
python/plugins/processing/tests/testdata/expected/union3.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="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>
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
97
src/analysis/processing/qgsalgorithmunion.cpp
Normal file
97
src/analysis/processing/qgsalgorithmunion.cpp
Normal 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 ¶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() );
|
||||
|
||||
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
|
||||
46
src/analysis/processing/qgsalgorithmunion.h
Normal file
46
src/analysis/processing/qgsalgorithmunion.h
Normal 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 ¶meters, QgsProcessingContext &context, QgsProcessingFeedback *feedback ) override;
|
||||
|
||||
};
|
||||
|
||||
///@endcond PRIVATE
|
||||
|
||||
#endif // QGSALGORITHMUNION_H
|
||||
@ -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() );
|
||||
}
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user