246 lines
12 KiB
Python
Raw Normal View History

2012-10-04 19:33:47 +02:00
# -*- 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'
2012-10-04 19:33:47 +02:00
# This will get replaced with a git SHA1 when you do a git archive
2012-10-04 19:33:47 +02:00
__revision__ = '$Format:%H$'
import os
2016-04-22 10:38:48 +02:00
from qgis.PyQt.QtGui import QIcon
from qgis.core import (QgsFeatureRequest,
QgsFeature,
QgsFeatureSink,
QgsGeometry,
QgsWkbTypes,
QgsMessageLog,
QgsProcessingUtils)
2017-06-06 13:41:42 +10:00
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.outputs import OutputVector
2017-05-02 14:47:58 +10:00
from processing.tools import vector
pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
wkbTypeGroups = {
'Point': (QgsWkbTypes.Point, QgsWkbTypes.MultiPoint, QgsWkbTypes.Point25D, QgsWkbTypes.MultiPoint25D,),
'LineString': (QgsWkbTypes.LineString, QgsWkbTypes.MultiLineString, QgsWkbTypes.LineString25D, QgsWkbTypes.MultiLineString25D,),
'Polygon': (QgsWkbTypes.Polygon, QgsWkbTypes.MultiPolygon, QgsWkbTypes.Polygon25D, QgsWkbTypes.MultiPolygon25D,),
}
2016-09-21 18:24:26 +02:00
for key, value in list(wkbTypeGroups.items()):
for const in value:
wkbTypeGroups[const] = key
2012-09-15 18:25:25 +03:00
class Union(QgisAlgorithm):
2012-09-15 18:25:25 +03:00
INPUT = 'INPUT'
INPUT2 = 'INPUT2'
OUTPUT = 'OUTPUT'
2012-09-15 18:25:25 +03:00
def icon(self):
return QIcon(os.path.join(pluginPath, 'images', 'ftools', 'union.png'))
def group(self):
return self.tr('Vector overlay tools')
def __init__(self):
super().__init__()
self.addParameter(ParameterVector(Union.INPUT,
self.tr('Input layer')))
self.addParameter(ParameterVector(Union.INPUT2,
self.tr('Input layer 2')))
self.addOutput(OutputVector(Union.OUTPUT, self.tr('Union')))
def name(self):
return 'union'
def displayName(self):
return self.tr('Union')
def processAlgorithm(self, parameters, context, feedback):
vlayerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Union.INPUT), context)
vlayerB = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(Union.INPUT2), context)
geomType = vlayerA.wkbType()
2017-06-22 18:21:16 +10:00
fields = vector.combineFields(vlayerA.fields(), vlayerB.fields())
writer = self.getOutputFromName(Union.OUTPUT).getVectorWriter(fields, geomType, vlayerA.crs(), context)
2012-09-15 18:25:25 +03:00
inFeatA = QgsFeature()
inFeatB = QgsFeature()
outFeat = QgsFeature()
indexA = QgsProcessingUtils.createSpatialIndex(vlayerB, context)
indexB = QgsProcessingUtils.createSpatialIndex(vlayerA, context)
2012-09-15 18:25:25 +03:00
count = 0
nElement = 0
featuresA = QgsProcessingUtils.getFeatures(vlayerA, context)
nFeat = QgsProcessingUtils.featureCount(vlayerA, context)
for inFeatA in featuresA:
feedback.setProgress(nElement / float(nFeat) * 50)
nElement += 1
lstIntersectingB = []
geom = inFeatA.geometry()
atMapA = inFeatA.attributes()
intersects = indexA.intersects(geom.boundingBox())
if len(intersects) < 1:
try:
outFeat.setGeometry(geom)
outFeat.setAttributes(atMapA)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
# This really shouldn't happen, as we haven't
# edited the input geom at all
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
else:
request = QgsFeatureRequest().setFilterFids(intersects)
engine = QgsGeometry.createGeometryEngine(geom.geometry())
engine.prepareGeometry()
for inFeatB in vlayerB.getFeatures(request):
count += 1
atMapB = inFeatB.attributes()
tmpGeom = inFeatB.geometry()
if engine.intersects(tmpGeom.geometry()):
int_geom = geom.intersection(tmpGeom)
lstIntersectingB.append(tmpGeom)
if not int_geom:
# There was a problem creating the intersection
QgsMessageLog.logMessage(self.tr('GEOS geoprocessing error: One or more input features have invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
int_geom = QgsGeometry()
else:
int_geom = QgsGeometry(int_geom)
if int_geom.wkbType() == QgsWkbTypes.Unknown or QgsWkbTypes.flatType(int_geom.geometry().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:
outFeat.setGeometry(int_geom)
outFeat.setAttributes(atMapA + atMapB)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
else:
# Geometry list: prevents writing error
# in geometries of different types
# produced by the intersection
# fix #3549
if int_geom.wkbType() in wkbTypeGroups[wkbTypeGroups[int_geom.wkbType()]]:
try:
outFeat.setGeometry(int_geom)
outFeat.setAttributes(atMapA + atMapB)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
# the remaining bit of inFeatA'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() == 0 or QgsWkbTypes.flatType(diff_geom.geometry().wkbType()) == QgsWkbTypes.GeometryCollection:
temp_list = diff_geom.asGeometryCollection()
for i in temp_list:
if i.type() == geom.type():
diff_geom = QgsGeometry(i)
try:
outFeat.setGeometry(diff_geom)
outFeat.setAttributes(atMapA)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
2012-09-15 18:25:25 +03:00
length = len(vlayerA.fields())
atMapA = [None] * length
2012-09-15 18:25:25 +03:00
featuresA = QgsProcessingUtils.getFeatures(vlayerB, context)
nFeat = QgsProcessingUtils.featureCount(vlayerB, context)
for inFeatA in featuresA:
feedback.setProgress(nElement / float(nFeat) * 100)
add = False
geom = inFeatA.geometry()
diff_geom = QgsGeometry(geom)
atMap = [None] * length
2013-02-07 01:09:39 +01:00
atMap.extend(inFeatA.attributes())
intersects = indexB.intersects(geom.boundingBox())
if len(intersects) < 1:
try:
2013-05-06 23:07:42 +02:00
outFeat.setGeometry(geom)
outFeat.setAttributes(atMap)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
else:
request = QgsFeatureRequest().setFilterFids(intersects)
# use prepared geometries for faster intersection tests
engine = QgsGeometry.createGeometryEngine(diff_geom.geometry())
engine.prepareGeometry()
for inFeatB in vlayerA.getFeatures(request):
atMapB = inFeatB.attributes()
tmpGeom = inFeatB.geometry()
if engine.intersects(tmpGeom.geometry()):
add = True
diff_geom = QgsGeometry(diff_geom.difference(tmpGeom))
else:
try:
# Ihis only happens if the bounding box
# intersects, but the geometry doesn't
outFeat.setGeometry(diff_geom)
outFeat.setAttributes(atMap)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
2012-09-15 18:25:25 +03:00
if add:
try:
outFeat.setGeometry(diff_geom)
2013-05-06 23:07:42 +02:00
outFeat.setAttributes(atMap)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
except:
QgsMessageLog.logMessage(self.tr('Feature geometry error: One or more output features ignored due to invalid geometry.'),
self.tr('Processing'), QgsMessageLog.INFO)
2013-02-07 01:09:39 +01:00
nElement += 1
del writer