2016-09-28 16:41:12 +02:00
|
|
|
# -*- coding: utf-8 -*-
|
|
|
|
|
|
|
|
"""
|
|
|
|
***************************************************************************
|
|
|
|
SplitWithLines.py
|
|
|
|
---------------------
|
|
|
|
Date : November 2014
|
|
|
|
Revised : November 2016
|
|
|
|
Copyright : (C) 2014 by Bernhard Ströbl
|
|
|
|
Email : bernhard dot stroebl at jena dot de
|
|
|
|
***************************************************************************
|
|
|
|
* *
|
|
|
|
* 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__ = 'Bernhard Ströbl'
|
|
|
|
__date__ = 'November 2014'
|
|
|
|
__copyright__ = '(C) 2014, Bernhard Ströbl'
|
|
|
|
|
|
|
|
# This will get replaced with a git SHA1 when you do a git archive
|
|
|
|
|
|
|
|
__revision__ = '$Format:%H$'
|
|
|
|
|
2017-03-29 10:42:42 +10:00
|
|
|
from qgis.core import (QgsApplication,
|
|
|
|
QgsFeatureRequest,
|
|
|
|
QgsFeature,
|
2017-06-23 14:34:38 +10:00
|
|
|
QgsFeatureSink,
|
2017-03-29 10:42:42 +10:00
|
|
|
QgsGeometry,
|
|
|
|
QgsSpatialIndex,
|
2017-04-25 17:53:32 +10:00
|
|
|
QgsWkbTypes,
|
2017-04-24 14:35:50 +10:00
|
|
|
QgsMessageLog,
|
2017-04-25 17:53:32 +10:00
|
|
|
QgsProcessingUtils)
|
2017-06-06 13:41:42 +10:00
|
|
|
from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
|
2016-09-28 16:41:12 +02:00
|
|
|
from processing.core.parameters import ParameterVector
|
|
|
|
from processing.core.outputs import OutputVector
|
|
|
|
from processing.tools import dataobjects
|
|
|
|
from processing.tools import vector
|
|
|
|
|
|
|
|
|
2017-05-19 11:27:16 +10:00
|
|
|
class SplitWithLines(QgisAlgorithm):
|
2016-09-28 16:41:12 +02:00
|
|
|
|
|
|
|
INPUT_A = 'INPUT_A'
|
|
|
|
INPUT_B = 'INPUT_B'
|
|
|
|
|
|
|
|
OUTPUT = 'OUTPUT'
|
|
|
|
|
2017-03-29 12:04:09 +10:00
|
|
|
def group(self):
|
|
|
|
return self.tr('Vector overlay tools')
|
|
|
|
|
2017-05-15 13:40:38 +10:00
|
|
|
def __init__(self):
|
|
|
|
super().__init__()
|
2016-09-28 16:41:12 +02:00
|
|
|
self.addParameter(ParameterVector(self.INPUT_A,
|
|
|
|
self.tr('Input layer, single geometries only'), [dataobjects.TYPE_VECTOR_POLYGON,
|
2016-11-24 08:31:13 +10:00
|
|
|
dataobjects.TYPE_VECTOR_LINE]))
|
2016-09-28 16:41:12 +02:00
|
|
|
self.addParameter(ParameterVector(self.INPUT_B,
|
|
|
|
self.tr('Split layer'), [dataobjects.TYPE_VECTOR_LINE]))
|
|
|
|
self.addOutput(OutputVector(self.OUTPUT, self.tr('Split')))
|
|
|
|
|
2017-05-15 13:40:38 +10:00
|
|
|
def name(self):
|
|
|
|
return 'splitwithlines'
|
|
|
|
|
|
|
|
def displayName(self):
|
|
|
|
return self.tr('Split with lines')
|
|
|
|
|
2017-05-15 16:19:46 +10:00
|
|
|
def processAlgorithm(self, parameters, context, feedback):
|
2017-05-02 13:40:49 +10:00
|
|
|
layerA = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_A), context)
|
|
|
|
splitLayer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT_B), context)
|
2016-09-28 16:41:12 +02:00
|
|
|
|
|
|
|
sameLayer = self.getParameterValue(self.INPUT_A) == self.getParameterValue(self.INPUT_B)
|
|
|
|
fieldList = layerA.fields()
|
|
|
|
|
2017-04-26 14:33:53 +10:00
|
|
|
writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fieldList, QgsWkbTypes.multiType(layerA.wkbType()),
|
|
|
|
layerA.crs(), context)
|
2016-09-28 16:41:12 +02:00
|
|
|
|
|
|
|
spatialIndex = QgsSpatialIndex()
|
|
|
|
splitGeoms = {}
|
|
|
|
request = QgsFeatureRequest()
|
|
|
|
request.setSubsetOfAttributes([])
|
|
|
|
|
2017-04-25 17:59:35 +10:00
|
|
|
for aSplitFeature in QgsProcessingUtils.getFeatures(splitLayer, context, request):
|
2016-09-28 16:41:12 +02:00
|
|
|
splitGeoms[aSplitFeature.id()] = aSplitFeature.geometry()
|
|
|
|
spatialIndex.insertFeature(aSplitFeature)
|
|
|
|
# honor the case that user has selection on split layer and has setting "use selection"
|
|
|
|
|
|
|
|
outFeat = QgsFeature()
|
2017-04-25 17:59:35 +10:00
|
|
|
features = QgsProcessingUtils.getFeatures(layerA, context)
|
2016-09-28 16:41:12 +02:00
|
|
|
|
2017-04-25 17:59:35 +10:00
|
|
|
if QgsProcessingUtils.featureCount(layerA, context) == 0:
|
2016-09-28 16:41:12 +02:00
|
|
|
total = 100
|
|
|
|
else:
|
2017-06-23 13:49:32 +10:00
|
|
|
total = 100.0 / layerA.featureCount() if layerA.featureCount() else 0
|
2016-09-28 16:41:12 +02:00
|
|
|
|
|
|
|
for current, inFeatA in enumerate(features):
|
|
|
|
inGeom = inFeatA.geometry()
|
2016-11-24 09:08:22 +01:00
|
|
|
attrsA = inFeatA.attributes()
|
|
|
|
outFeat.setAttributes(attrsA)
|
2016-09-28 16:41:12 +02:00
|
|
|
|
|
|
|
if inGeom.isMultipart():
|
2016-11-24 09:08:22 +01:00
|
|
|
inGeoms = []
|
|
|
|
|
|
|
|
for g in inGeom.asGeometryCollection():
|
|
|
|
inGeoms.append(g)
|
2016-09-28 16:41:12 +02:00
|
|
|
else:
|
|
|
|
inGeoms = [inGeom]
|
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
lines = spatialIndex.intersects(inGeom.boundingBox())
|
|
|
|
|
|
|
|
if len(lines) > 0: # has intersection of bounding boxes
|
|
|
|
splittingLines = []
|
|
|
|
|
|
|
|
engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
|
|
|
|
engine.prepareGeometry()
|
2016-09-28 16:41:12 +02:00
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
for i in lines:
|
|
|
|
try:
|
|
|
|
splitGeom = splitGeoms[i]
|
|
|
|
except:
|
|
|
|
continue
|
2016-11-24 08:31:13 +10:00
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
# check if trying to self-intersect
|
|
|
|
if sameLayer:
|
|
|
|
if inFeatA.id() == i:
|
2016-09-28 16:41:12 +02:00
|
|
|
continue
|
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
if engine.intersects(splitGeom.geometry()):
|
|
|
|
splittingLines.append(splitGeom)
|
|
|
|
|
|
|
|
if len(splittingLines) > 0:
|
|
|
|
for splitGeom in splittingLines:
|
|
|
|
splitterPList = None
|
|
|
|
outGeoms = []
|
|
|
|
|
|
|
|
split_geom_engine = QgsGeometry.createGeometryEngine(splitGeom.geometry())
|
|
|
|
split_geom_engine.prepareGeometry()
|
|
|
|
|
|
|
|
while len(inGeoms) > 0:
|
|
|
|
inGeom = inGeoms.pop()
|
|
|
|
|
2017-03-04 19:41:23 +01:00
|
|
|
if inGeom.isNull(): # this has been encountered and created a run-time error
|
2016-09-28 16:41:12 +02:00
|
|
|
continue
|
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
if split_geom_engine.intersects(inGeom.geometry()):
|
|
|
|
inPoints = vector.extractPoints(inGeom)
|
2017-03-04 19:41:23 +01:00
|
|
|
if splitterPList is None:
|
2016-11-24 09:08:22 +01:00
|
|
|
splitterPList = vector.extractPoints(splitGeom)
|
|
|
|
|
|
|
|
try:
|
|
|
|
result, newGeometries, topoTestPoints = inGeom.splitGeometry(splitterPList, False)
|
|
|
|
except:
|
2017-04-26 12:52:23 +10:00
|
|
|
QgsMessageLog.logMessage(self.tr('Geometry exception while splitting'), self.tr('Processing'), QgsMessageLog.WARNING)
|
2016-11-24 09:08:22 +01:00
|
|
|
result = 1
|
|
|
|
|
|
|
|
# splitGeometry: If there are several intersections
|
|
|
|
# between geometry and splitLine, only the first one is considered.
|
|
|
|
if result == 0: # split occurred
|
|
|
|
if inPoints == vector.extractPoints(inGeom):
|
|
|
|
# bug in splitGeometry: sometimes it returns 0 but
|
|
|
|
# the geometry is unchanged
|
2016-09-28 16:41:12 +02:00
|
|
|
outGeoms.append(inGeom)
|
2016-11-24 09:08:22 +01:00
|
|
|
else:
|
|
|
|
inGeoms.append(inGeom)
|
|
|
|
|
|
|
|
for aNewGeom in newGeometries:
|
|
|
|
inGeoms.append(aNewGeom)
|
2016-09-28 16:41:12 +02:00
|
|
|
else:
|
|
|
|
outGeoms.append(inGeom)
|
2016-11-24 09:08:22 +01:00
|
|
|
else:
|
|
|
|
outGeoms.append(inGeom)
|
2016-09-28 16:41:12 +02:00
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
inGeoms = outGeoms
|
2016-09-28 16:41:12 +02:00
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
parts = []
|
2016-09-28 16:41:12 +02:00
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
for aGeom in inGeoms:
|
|
|
|
passed = True
|
2016-09-28 16:41:12 +02:00
|
|
|
|
2016-12-14 14:13:31 +01:00
|
|
|
if QgsWkbTypes.geometryType(aGeom.wkbType()) == QgsWkbTypes.LineGeometry:
|
2016-11-24 09:08:22 +01:00
|
|
|
numPoints = aGeom.geometry().numPoints()
|
2016-09-28 16:41:12 +02:00
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
if numPoints <= 2:
|
|
|
|
if numPoints == 2:
|
2017-03-04 19:41:23 +01:00
|
|
|
passed = not aGeom.geometry().isClosed() # tests if vertex 0 = vertex 1
|
2016-11-24 09:08:22 +01:00
|
|
|
else:
|
|
|
|
passed = False
|
|
|
|
# sometimes splitting results in lines of zero length
|
2016-09-28 16:41:12 +02:00
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
if passed:
|
|
|
|
parts.append(aGeom)
|
2016-09-28 16:41:12 +02:00
|
|
|
|
2016-11-24 09:08:22 +01:00
|
|
|
if len(parts) > 0:
|
|
|
|
outFeat.setGeometry(QgsGeometry.collectGeometry(parts))
|
2017-06-23 14:34:38 +10:00
|
|
|
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
|
2016-09-28 16:41:12 +02:00
|
|
|
|
2017-01-06 20:04:00 +10:00
|
|
|
feedback.setProgress(int(current * total))
|
2016-09-28 16:41:12 +02:00
|
|
|
del writer
|