Skip to content

Commit

Permalink
Restore symmetrical difference alg
Browse files Browse the repository at this point in the history
  • Loading branch information
nyalldawson committed Jun 22, 2017
1 parent 66d1a58 commit 6be4875
Show file tree
Hide file tree
Showing 4 changed files with 51 additions and 37 deletions.
7 changes: 4 additions & 3 deletions python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Expand Up @@ -59,8 +59,8 @@
from .Merge import Merge
from .PostGISExecuteSQL import PostGISExecuteSQL
from .RegularPoints import RegularPoints
from .SymmetricalDifference import SymmetricalDifference

# from .SymmetricalDifference import SymmetricalDifference
# from .VectorSplit import VectorSplit
# from .RandomExtract import RandomExtract
# from .RandomExtractWithinSubsets import RandomExtractWithinSubsets
Expand Down Expand Up @@ -200,7 +200,7 @@ def getAlgs(self):
# RandomSelection(), RandomSelectionWithinSubsets(),
# SelectByLocation(), RandomExtract(),
# RandomExtractWithinSubsets(), ExtractByLocation(),
# SpatialJoin(), SymmetricalDifference(),
# SpatialJoin(),
# VectorSplit(),
# DeleteDuplicateGeometries(), TextToFloat(),
# SelectByAttribute(),
Expand Down Expand Up @@ -262,7 +262,8 @@ def getAlgs(self):
ImportIntoPostGIS(),
Merge(),
PostGISExecuteSQL(),
RegularPoints()
RegularPoints(),
SymmetricalDifference()
]

if hasPlotly:
Expand Down
75 changes: 45 additions & 30 deletions python/plugins/processing/algs/qgis/SymmetricalDifference.py
Expand Up @@ -35,10 +35,12 @@
NULL,
QgsWkbTypes,
QgsMessageLog,
QgsProcessingUtils)
QgsProcessingUtils,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingOutputVectorLayer,
QgsSpatialIndex)
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.outputs import OutputVector
from processing.tools import vector

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
Expand All @@ -58,12 +60,14 @@ def group(self):

def __init__(self):
super().__init__()
self.addParameter(ParameterVector(self.INPUT,
self.tr('Input layer')))
self.addParameter(ParameterVector(self.OVERLAY,
self.tr('Difference layer')))
self.addOutput(OutputVector(self.OUTPUT,
self.tr('Symmetrical difference')))

self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Input layer')))
self.addParameter(QgsProcessingParameterFeatureSource(self.OVERLAY,
self.tr('Difference layer')))

self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Symmetrical difference')))
self.addOutput(QgsProcessingOutputVectorLayer(self.OUTPUT, self.tr('Symmetrical difference')))

def name(self):
return 'symmetricaldifference'
Expand All @@ -72,40 +76,45 @@ def displayName(self):
return self.tr('Symmetrical difference')

def processAlgorithm(self, parameters, context, feedback):
layerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context)
layerB = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.OVERLAY), context)
sourceA = self.parameterAsSource(parameters, self.INPUT, context)
sourceB = self.parameterAsSource(parameters, self.OVERLAY, context)

geomType = QgsWkbTypes.multiType(sourceA.wkbType())
fields = vector.combineFields(sourceA.fields(), sourceB.fields())

geomType = QgsWkbTypes.multiType(layerA.wkbType())
fields = vector.combineVectorFields(layerA, layerB)
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fields, geomType, layerA.crs(), context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
fields, geomType, sourceA.sourceCrs())

featB = QgsFeature()
outFeat = QgsFeature()

indexA = QgsProcessingUtils.createSpatialIndex(layerB, context)
indexB = QgsProcessingUtils.createSpatialIndex(layerA, context)
indexA = QgsSpatialIndex(sourceA)
indexB = QgsSpatialIndex(sourceB.getFeatures(QgsFeatureRequest().setSubsetOfAttributes([]).setDestinationCrs(sourceA.sourceCrs())))

featuresA = QgsProcessingUtils.getFeatures(layerA, context)
featuresB = QgsProcessingUtils.getFeatures(layerB, context)

total = 100.0 / (QgsProcessingUtils.featureCount(layerA, context) * QgsProcessingUtils.featureCount(layerB, context))
total = 100.0 / (sourceA.featureCount() * sourceB.featureCount())
count = 0

for featA in featuresA:
for featA in sourceA.getFeatures():
if feedback.isCanceled():
break

geom = featA.geometry()
diffGeom = QgsGeometry(geom)
attrs = featA.attributes()
intersects = indexA.intersects(geom.boundingBox())
intersects = indexB.intersects(geom.boundingBox())
request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
for featB in layerB.getFeatures(request):
request.setDestinationCrs(sourceA.sourceCrs())
for featB in sourceB.getFeatures(request):
if feedback.isCanceled():
break
tmpGeom = featB.geometry()
if diffGeom.intersects(tmpGeom):
diffGeom = QgsGeometry(diffGeom.difference(tmpGeom))

try:
outFeat.setGeometry(diffGeom)
outFeat.setAttributes(attrs)
writer.addFeature(outFeat)
sink.addFeature(outFeat)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.WARNING)
Expand All @@ -114,24 +123,30 @@ def processAlgorithm(self, parameters, context, feedback):
count += 1
feedback.setProgress(int(count * total))

length = len(layerA.fields())
length = len(sourceA.fields())

for featA in sourceB.getFeatures(QgsFeatureRequest().setDestinationCrs(sourceA.sourceCrs())):
if feedback.isCanceled():
break

for featA in featuresB:
geom = featA.geometry()
diffGeom = QgsGeometry(geom)
attrs = featA.attributes()
attrs = [NULL] * length + attrs
intersects = indexB.intersects(geom.boundingBox())
intersects = indexA.intersects(geom.boundingBox())
request = QgsFeatureRequest().setFilterFids(intersects).setSubsetOfAttributes([])
for featB in layerA.getFeatures(request):
for featB in sourceA.getFeatures(request):
if feedback.isCanceled():
break

tmpGeom = featB.geometry()
if diffGeom.intersects(tmpGeom):
diffGeom = QgsGeometry(diffGeom.difference(tmpGeom))

try:
outFeat.setGeometry(diffGeom)
outFeat.setAttributes(attrs)
writer.addFeature(outFeat)
sink.addFeature(outFeat)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.WARNING)
Expand All @@ -140,4 +155,4 @@ def processAlgorithm(self, parameters, context, feedback):
count += 1
feedback.setProgress(int(count * total))

del writer
return {self.OUTPUT: dest_id}
2 changes: 1 addition & 1 deletion python/plugins/processing/algs/qgis/Union.py
Expand Up @@ -84,7 +84,7 @@ def processAlgorithm(self, parameters, context, feedback):
vlayerB = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Union.INPUT2), context)

geomType = vlayerA.wkbType()
fields = vector.combineVectorFields(vlayerA, vlayerB)
fields = vector.combineFields(vlayerA.fields(), vlayerB.fields())
writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields, geomType, vlayerA.crs(), context)
inFeatA = QgsFeature()
inFeatB = QgsFeature()
Expand Down
4 changes: 1 addition & 3 deletions python/plugins/processing/tools/vector.py
Expand Up @@ -228,14 +228,12 @@ def simpleMeasure(geom, method=0, ellips=None, crs=None):
return (attr1, attr2)


def combineVectorFields(layerA, layerB):
def combineFields(fieldsA, fieldsB):
"""Create single field map from two input field maps.
"""
fields = []
fieldsA = layerA.fields()
fields.extend(fieldsA)
namesA = [str(f.name()).lower() for f in fieldsA]
fieldsB = layerB.fields()
for field in fieldsB:
name = str(field.name()).lower()
if name in namesA:
Expand Down

0 comments on commit 6be4875

Please sign in to comment.