Skip to content

Commit

Permalink
[processing][FEATURE] Return multi geometries from split alg
Browse files Browse the repository at this point in the history
accept multi geometries as input
  • Loading branch information
Bernhard Ströbl authored and nyalldawson committed Dec 2, 2016
1 parent 3a789e5 commit b02c6a3
Show file tree
Hide file tree
Showing 8 changed files with 108 additions and 133 deletions.
2 changes: 2 additions & 0 deletions python/plugins/processing/algs/help/qgis.yaml
Expand Up @@ -504,6 +504,8 @@ qgis:snappointstogrid: >

qgis:splitwithlines: >
This algorithm splits the lines or polygons in one layer using the lines in another layer to define the breaking points. Intersection between geometries in both layers are considered as split points.

Output will contain multi geometries for split features.

qgis:splitvectorlayer: >
This algorithm takes a vector layer and an attribute and generates a set of vector layers in an output folder. Each of the layers created in that folder contains all features from the input layer with the same value for the specified attribute.
Expand Down
162 changes: 81 additions & 81 deletions python/plugins/processing/algs/qgis/SplitWithLines.py
Expand Up @@ -26,7 +26,7 @@

__revision__ = '$Format:%H$'

from qgis.core import QgsFeatureRequest, QgsFeature, QgsGeometry, QgsSpatialIndex, QgsWkbTypes, QgsMessageLog
from qgis.core import QgsFeatureRequest, QgsFeature, QgsGeometry, QgsSpatialIndex, QgsWkbTypes, QgsPoint
from processing.core.GeoAlgorithm import GeoAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.outputs import OutputVector
Expand Down Expand Up @@ -61,7 +61,7 @@ def processAlgorithm(self, progress):
fieldList = layerA.fields()

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(fieldList,
layerA.wkbType(), layerA.crs())
QgsWkbTypes.multiType(layerA.wkbType()), layerA.crs())

spatialIndex = QgsSpatialIndex()
splitGeoms = {}
Expand All @@ -81,107 +81,107 @@ def processAlgorithm(self, progress):
else:
total = 100.0 / float(len(features))

multiGeoms = 0 # how many multi geometries were encountered

for current, inFeatA in enumerate(features):
inGeom = inFeatA.geometry()
attrsA = inFeatA.attributes()
outFeat.setAttributes(attrsA)

if inGeom.isMultipart():
multiGeoms += 1
# MultiGeometries are not allowed because the result of a splitted part cannot be clearly defined:
# 1) add both new parts as new features
# 2) store one part as a new feature and the other one as part of the multi geometry
# 2a) which part should be which, seems arbitrary
inGeoms = []

for g in inGeom.asGeometryCollection():
inGeoms.append(g)
else:
attrsA = inFeatA.attributes()
outFeat.setAttributes(attrsA)
inGeoms = [inGeom]
lines = spatialIndex.intersects(inGeom.boundingBox())

if len(lines) > 0: # has intersection of bounding boxes
splittingLines = []
lines = spatialIndex.intersects(inGeom.boundingBox())

if len(lines) > 0: # has intersection of bounding boxes
splittingLines = []

engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
engine.prepareGeometry()

engine = QgsGeometry.createGeometryEngine(inGeom.geometry())
engine.prepareGeometry()
for i in lines:
try:
splitGeom = splitGeoms[i]
except:
continue

for i in lines:
try:
splitGeom = splitGeoms[i]
except:
# check if trying to self-intersect
if sameLayer:
if inFeatA.id() == i:
continue

# check if trying to self-intersect
if sameLayer:
if inFeatA.id() == i:
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()

if inGeom.isEmpty(): # this has been encountered and created a run-time error
continue

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()

if split_geom_engine.intersects(inGeom.geometry()):
inPoints = vector.extractPoints(inGeom)
if splitterPList == None:
splitterPList = vector.extractPoints(splitGeom)

try:
result, newGeometries, topoTestPoints = inGeom.splitGeometry(splitterPList, False)
except:
ProcessingLog.addToLog(ProcessingLog.LOG_WARNING,
self.tr('Geometry exception while splitting'))
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
QgsMessageLog.logMessage("appending")
outGeoms.append(inGeom)
else:
inGeoms.append(inGeom)

for aNewGeom in newGeometries:
inGeoms.append(aNewGeom)
else:
QgsMessageLog.logMessage("appending else")
if split_geom_engine.intersects(inGeom.geometry()):
inPoints = vector.extractPoints(inGeom)
if splitterPList == None:
splitterPList = vector.extractPoints(splitGeom)

try:
result, newGeometries, topoTestPoints = inGeom.splitGeometry(splitterPList, False)
except:
ProcessingLog.addToLog(ProcessingLog.LOG_WARNING,
self.tr('Geometry exception while splitting'))
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
outGeoms.append(inGeom)
else:
inGeoms.append(inGeom)

for aNewGeom in newGeometries:
inGeoms.append(aNewGeom)
else:
outGeoms.append(inGeom)
else:
outGeoms.append(inGeom)

inGeoms = outGeoms
inGeoms = outGeoms

for aGeom in inGeoms:
passed = True
parts = []

if QgsWkbTypes.geometryType( aGeom.wkbType() ) == QgsWkbTypes.LineGeometry \
and not QgsWkbTypes.isMultiType(aGeom.wkbType()):
passed = len(aGeom.asPolyline()) > 2
for aGeom in inGeoms:
passed = True

if not passed:
passed = (len(aGeom.asPolyline()) == 2 and
aGeom.asPolyline()[0] != aGeom.asPolyline()[1])
# sometimes splitting results in lines of zero length
if QgsWkbTypes.geometryType( aGeom.wkbType() ) == QgsWkbTypes.LineGeometry:
numPoints = aGeom.geometry().numPoints()

if passed:
outFeat.setGeometry(aGeom)
writer.addFeature(outFeat)
if numPoints <= 2:
if numPoints == 2:
passed = not aGeom.geometry().isClosed() # tests if vertex 0 = vertex 1
else:
passed = False
# sometimes splitting results in lines of zero length

progress.setPercentage(int(current * total))
if passed:
parts.append(aGeom)

if multiGeoms > 0:
ProcessingLog.addToLog(ProcessingLog.LOG_INFO,
self.tr('Feature geometry error: %s input features ignored due to multi-geometry.') % str(multiGeoms))
if len(parts) > 0:
outFeat.setGeometry(QgsGeometry.collectGeometry(parts))
writer.addFeature(outFeat)

progress.setPercentage(int(current * total))
del writer
Expand Up @@ -2,11 +2,11 @@
<GMLFeatureClass>
<Name>split_lines_with_lines</Name>
<ElementPath>split_lines_with_lines</ElementPath>
<!--LINESTRING-->
<GeometryType>2</GeometryType>
<!--MULTILINESTRING-->
<GeometryType>5</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>11</FeatureCount>
<FeatureCount>7</FeatureCount>
<ExtentXMin>-1.00000</ExtentXMin>
<ExtentXMax>11.00000</ExtentXMax>
<ExtentYMin>-3.00000</ExtentYMin>
Expand Down
Expand Up @@ -13,52 +13,32 @@

<gml:featureMember>
<ogr:split_lines_with_lines fid="lines.0">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6,2 9,2 9,3 11,5</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>6,2 9,2 9,3 11,5</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines fid="lines.1">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1,-1 1,-1</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>-1,-1 1,-1</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines fid="lines.2">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>2.0,1.86331771490359 2,2 3,2 3,3</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:split_lines_with_lines>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines fid="lines.2">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>2,0 2.0,1.86331771490359</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:split_lines_with_lines>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines fid="lines.3">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>4.21601489757914,1.0 5,1</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>2.0,1.86331771490359 2,2 3,2 3,3</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates>2,0 2.0,1.86331771490359</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines fid="lines.3">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>3,1 4.21601489757914,1.0</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>4.21601489757914,1.0 5,1</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates>3,1 4.21601489757914,1.0</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines fid="lines.4">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>8.5655671605538,-3.0 10,-3</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:split_lines_with_lines>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines fid="lines.4">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>7,-3 8.5655671605538,-3.0</gml:coordinates></gml:LineString></ogr:geometryProperty>
</ogr:split_lines_with_lines>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines fid="lines.5">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6.91202704418487,-2.08797295581513 10,1</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>8.5655671605538,-3.0 10,-3</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates>7,-3 8.5655671605538,-3.0</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines fid="lines.5">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6,-3 6.91202704418487,-2.08797295581513</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>6.91202704418487,-2.08797295581513 10,1</gml:coordinates></gml:LineString></gml:lineStringMember><gml:lineStringMember><gml:LineString><gml:coordinates>6,-3 6.91202704418487,-2.08797295581513</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines>
</gml:featureMember>
<gml:featureMember>
Expand Down
Expand Up @@ -2,8 +2,8 @@
<GMLFeatureClass>
<Name>split_lines_with_lines_same</Name>
<ElementPath>split_lines_with_lines_same</ElementPath>
<!--LINESTRING-->
<GeometryType>2</GeometryType>
<!--MULTILINESTRING-->
<GeometryType>5</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>7</FeatureCount>
Expand Down
Expand Up @@ -13,32 +13,32 @@

<gml:featureMember>
<ogr:split_lines_with_lines_same fid="lines.0">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6,2 9,2 9,3 11,5</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>6,2 9,2 9,3 11,5</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines_same>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines_same fid="lines.1">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>-1,-1 1,-1</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>-1,-1 1,-1</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines_same>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines_same fid="lines.2">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>2,0 2,2 3,2 3,3</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>2,0 2,2 3,2 3,3</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines_same>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines_same fid="lines.3">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>3,1 5,1</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>3,1 5,1</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines_same>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines_same fid="lines.4">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>7,-3 10,-3</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>7,-3 10,-3</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines_same>
</gml:featureMember>
<gml:featureMember>
<ogr:split_lines_with_lines_same fid="lines.5">
<ogr:geometryProperty><gml:LineString srsName="EPSG:4326"><gml:coordinates>6,-3 10,1</gml:coordinates></gml:LineString></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiLineString srsName="EPSG:4326"><gml:lineStringMember><gml:LineString><gml:coordinates>6,-3 10,1</gml:coordinates></gml:LineString></gml:lineStringMember></gml:MultiLineString></ogr:geometryProperty>
</ogr:split_lines_with_lines_same>
</gml:featureMember>
<gml:featureMember>
Expand Down

0 comments on commit b02c6a3

Please sign in to comment.