Skip to content
Permalink
Browse files

[processing] restore and improve lines to pol / pol to lines algs (#4850

)

New geometry model for lines to pol / pol to lines agls.
  • Loading branch information
nirvn committed Jul 14, 2017
1 parent f84a3bb commit b5dc9fd3cd9bc6a5e00fd814ac80d0be62928f2b
@@ -29,11 +29,20 @@

from qgis.PyQt.QtGui import QIcon

from qgis.core import QgsFeature, QgsGeometry, QgsWkbTypes, QgsFeatureSink, QgsProcessingUtils
from qgis.core import (QgsFeature,
QgsGeometry,
QgsGeometryCollection,
QgsPolygonV2,
QgsMultiPolygonV2,
QgsMultiSurface,
QgsWkbTypes,
QgsFeatureSink,
QgsProcessing,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingUtils)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.outputs import OutputVector
from processing.tools import dataobjects, vector

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
@@ -57,10 +66,13 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT,
self.tr('Input layer'),
[dataobjects.TYPE_VECTOR_LINE]))
self.addOutput(OutputVector(self.OUTPUT, self.tr('Polygons from lines'), datatype=[dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Input layer'),
[QgsProcessing.TypeVectorLine]))

self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
self.tr('Lines to polygons'),
QgsProcessing.TypeVectorPolygon))

def name(self):
return 'linestopolygons'
@@ -69,39 +81,75 @@ def displayName(self):
return self.tr('Lines to polygons')

def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context)
source = self.parameterAsSource(parameters, self.INPUT, context)

geomType = self.convertWkbToPolygons(source.wkbType())

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(layer.fields(), QgsWkbTypes.Polygon,
layer.crs(), context)
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), geomType, source.sourceCrs())

outFeat = QgsFeature()
features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
for current, f in enumerate(features):
outGeomList = []
if f.geometry().isMultipart():
outGeomList = f.geometry().asMultiPolyline()
else:
outGeomList.append(f.geometry().asPolyline())

polyGeom = self.removeBadLines(outGeomList)
if len(polyGeom) != 0:
outFeat.setGeometry(QgsGeometry.fromPolygon(polyGeom))
attrs = f.attributes()
total = 100.0 / source.featureCount() if source.featureCount() else 0
count = 0

for feat in source.getFeatures():
if feedback.isCanceled():
break

if feat.hasGeometry():
outFeat.setGeometry(QgsGeometry(self.convertToPolygons(feat.geometry())))
attrs = feat.attributes()
outFeat.setAttributes(attrs)
writer.addFeature(outFeat, QgsFeatureSink.FastInsert)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
if outFeat.geometry().isEmpty():
feedback.reportError(self.tr("One or more line ignored due to geometry not having a minimum of three vertices."))
else:
sink.addFeature(feat, QgsFeatureSink.FastInsert)

count += 1
feedback.setProgress(int(count * total))

return {self.OUTPUT: dest_id}

def convertWkbToPolygons(self, wkb):
multi_wkb = None
if QgsWkbTypes.singleType(QgsWkbTypes.flatType(wkb)) == QgsWkbTypes.LineString:
multi_wkb = QgsWkbTypes.MultiPolygon
elif QgsWkbTypes.singleType(QgsWkbTypes.flatType(wkb)) == QgsWkbTypes.CompoundCurve:
multi_wkb = QgsWkbTypes.MultiSurface
if QgsWkbTypes.hasM(wkb):
multi_wkb = QgsWkbTypes.addM(multi_wkb)
if QgsWkbTypes.hasZ(wkb):
multi_wkb = QgsWkbTypes.addZ(multi_wkb)

return multi_wkb

def convertToPolygons(self, geometry):
surfaces = self.getSurfaces(geometry.geometry())
output_wkb = self.convertWkbToPolygons(geometry.wkbType())
out_geom = None
if QgsWkbTypes.flatType(output_wkb) == QgsWkbTypes.MultiPolygon:
out_geom = QgsMultiPolygonV2()
else:
out_geom = QgsMultiSurface()

feedback.setProgress(int(current * total))
for surface in surfaces:
out_geom.addGeometry(surface)

del writer
return out_geom

def removeBadLines(self, lines):
geom = []
if len(lines) == 1:
if len(lines[0]) > 2:
geom = lines
else:
geom = []
def getSurfaces(self, geometry):
surfaces = []
if isinstance(geometry, QgsGeometryCollection):
# collection
for i in range(geometry.numGeometries()):
surfaces.extend(self.getSurfaces(geometry.geometryN(i)))
else:
geom = [elem for elem in lines if len(elem) > 2]
return geom
# not collection
if geometry.vertexCount() > 2:
surface = QgsPolygonV2()
surface.setExteriorRing(geometry.clone())
surfaces.append(surface)

return surfaces
@@ -29,11 +29,19 @@

from qgis.PyQt.QtGui import QIcon

from qgis.core import QgsGeometry, QgsWkbTypes, QgsFeatureSink, QgsProcessingUtils
from qgis.core import (QgsFeature,
QgsGeometry,
QgsGeometryCollection,
QgsMultiLineString,
QgsMultiCurve,
QgsWkbTypes,
QgsFeatureSink,
QgsProcessing,
QgsProcessingParameterFeatureSource,
QgsProcessingParameterFeatureSink,
QgsProcessingUtils)

from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
from processing.core.parameters import ParameterVector
from processing.core.outputs import OutputVector
from processing.tools import dataobjects

pluginPath = os.path.split(os.path.split(os.path.dirname(__file__))[0])[0]
@@ -57,10 +65,13 @@ def __init__(self):
super().__init__()

def initAlgorithm(self, config=None):
self.addParameter(ParameterVector(self.INPUT,
self.tr('Input layer'), [dataobjects.TYPE_VECTOR_POLYGON]))
self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
self.tr('Input layer'),
[QgsProcessing.TypeVectorPolygon]))

self.addOutput(OutputVector(self.OUTPUT, self.tr('Lines from polygons'), datatype=[dataobjects.TYPE_VECTOR_LINE]))
self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
self.tr('Polygons to lines'),
QgsProcessing.TypeVectorLine))

def name(self):
return 'polygonstolines'
@@ -69,22 +80,72 @@ def displayName(self):
return self.tr('Polygons to lines')

def processAlgorithm(self, parameters, context, feedback):
layer = QgsProcessingUtils.mapLayerFromString(self.getParameterValue(self.INPUT), context)

writer = self.getOutputFromName(self.OUTPUT).getVectorWriter(layer.fields(), QgsWkbTypes.LineString,
layer.crs(), context)

features = QgsProcessingUtils.getFeatures(layer, context)
total = 100.0 / layer.featureCount() if layer.featureCount() else 0
for current, f in enumerate(features):
if f.hasGeometry():
lines = QgsGeometry(f.geometry().geometry().boundary()).asGeometryCollection()
for line in lines:
f.setGeometry(line)
writer.addFeature(f, QgsFeatureSink.FastInsert)
else:
writer.addFeature(f, QgsFeatureSink.FastInsert)
source = self.parameterAsSource(parameters, self.INPUT, context)

geomType = self.convertWkbToLines(source.wkbType())

(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
source.fields(), geomType, source.sourceCrs())

feedback.setProgress(int(current * total))
outFeat = QgsFeature()

del writer
total = 100.0 / source.featureCount() if source.featureCount() else 0
count = 0

for feat in source.getFeatures():
if feedback.isCanceled():
break

if feat.hasGeometry():
outFeat.setGeometry(QgsGeometry(self.convertToLines(feat.geometry())))
attrs = feat.attributes()
outFeat.setAttributes(attrs)
sink.addFeature(outFeat, QgsFeatureSink.FastInsert)
else:
sink.addFeature(feat, QgsFeatureSink.FastInsert)

count += 1
feedback.setProgress(int(count * total))

return {self.OUTPUT: dest_id}

def convertWkbToLines(self, wkb):
multi_wkb = None
if QgsWkbTypes.singleType(QgsWkbTypes.flatType(wkb)) == QgsWkbTypes.Polygon:
multi_wkb = QgsWkbTypes.MultiLineString
elif QgsWkbTypes.singleType(QgsWkbTypes.flatType(wkb)) == QgsWkbTypes.CurvePolygon:
multi_wkb = QgsWkbTypes.MultiCurve
if QgsWkbTypes.hasM(wkb):
multi_wkb = QgsWkbTypes.addM(multi_wkb)
if QgsWkbTypes.hasZ(wkb):
multi_wkb = QgsWkbTypes.addZ(multi_wkb)

return multi_wkb

def convertToLines(self, geometry):
rings = self.getRings(geometry.geometry())
output_wkb = self.convertWkbToLines(geometry.wkbType())
out_geom = None
if QgsWkbTypes.flatType(output_wkb) == QgsWkbTypes.MultiLineString:
out_geom = QgsMultiLineString()
else:
out_geom = QgsMultiCurve()

for ring in rings:
out_geom.addGeometry(ring)

return out_geom

def getRings(self, geometry):
rings = []
if isinstance(geometry, QgsGeometryCollection):
# collection
for i in range(geometry.numGeometries()):
rings.extend(self.getRings(geometry.geometryN(i)))
else:
# not collection
rings.append(geometry.exteriorRing().clone())
for i in range(geometry.numInteriorRings()):
rings.append(geometry.interiorRing(i).clone())

return rings
@@ -63,8 +63,10 @@
from .ImportIntoPostGIS import ImportIntoPostGIS
from .ImportIntoSpatialite import ImportIntoSpatialite
from .Intersection import Intersection
from .LinesToPolygons import LinesToPolygons
from .Merge import Merge
from .PointsLayerFromTable import PointsLayerFromTable
from .PolygonsToLines import PolygonsToLines
from .PostGISExecuteSQL import PostGISExecuteSQL
from .RandomExtract import RandomExtract
from .RandomExtractWithinSubsets import RandomExtractWithinSubsets
@@ -93,8 +95,6 @@
# from .PointDistance import PointDistance
# from .UniqueValues import UniqueValues
# from .ExportGeometryInfo import ExportGeometryInfo
# from .LinesToPolygons import LinesToPolygons
# from .PolygonsToLines import PolygonsToLines
# from .SinglePartsToMultiparts import SinglePartsToMultiparts
# from .ExtractNodes import ExtractNodes
# from .ConvexHull import ConvexHull
@@ -190,8 +190,8 @@ def getAlgs(self):
# NearestNeighbourAnalysis(), MeanCoords(),
# LinesIntersection(), UniqueValues(), PointDistance(),
# ExportGeometryInfo(),
# , SinglePartsToMultiparts(),
# PolygonsToLines(), LinesToPolygons(), ExtractNodes(),
# SinglePartsToMultiparts(),
# ExtractNodes(),
# ConvexHull(), FixedDistanceBuffer(),
# VariableDistanceBuffer(),
# RandomSelection(), RandomSelectionWithinSubsets(),
@@ -260,8 +260,10 @@ def getAlgs(self):
ImportIntoPostGIS(),
ImportIntoSpatialite(),
Intersection(),
LinesToPolygons(),
Merge(),
PointsLayerFromTable(),
PolygonsToLines(),
PostGISExecuteSQL(),
RandomExtract(),
RandomExtractWithinSubsets(),
@@ -48,7 +48,7 @@
'qgis:intersection': geoprocessingToolsMenu,
'qgis:union': geoprocessingToolsMenu,
'qgis:symmetricaldifference': geoprocessingToolsMenu,
'qgis:clip': geoprocessingToolsMenu,
'native:clip': geoprocessingToolsMenu,
'qgis:difference': geoprocessingToolsMenu,
'qgis:dissolve': geoprocessingToolsMenu,
'qgis:eliminateselectedpolygons': geoprocessingToolsMenu})
@@ -2,10 +2,10 @@
<GMLFeatureClass>
<Name>lines_to_polygon</Name>
<ElementPath>lines_to_polygon</ElementPath>
<GeometryType>3</GeometryType>
<GeometryType>6</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>2</FeatureCount>
<FeatureCount>7</FeatureCount>
<ExtentXMin>2.00000</ExtentXMin>
<ExtentXMax>11.00000</ExtentXMax>
<ExtentYMin>0.00000</ExtentYMin>
@@ -10,15 +10,35 @@
<gml:coord><gml:X>11</gml:X><gml:Y>5</gml:Y></gml:coord>
</gml:Box>
</gml:boundedBy>

<gml:featureMember>
<ogr:lines_to_polygon fid="lines.0">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,2 9,2 9,3 11,5 6,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>6,2 9,2 9,3 11,5 6,2</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
</ogr:lines_to_polygon>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_to_polygon fid="lines.1">
</ogr:lines_to_polygon>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_to_polygon fid="lines.2">
<ogr:geometryProperty><gml:Polygon srsName="EPSG:4326"><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,0 2,2 3,2 3,3 2,0</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></ogr:geometryProperty>
<ogr:geometryProperty><gml:MultiPolygon srsName="EPSG:4326"><gml:polygonMember><gml:Polygon><gml:outerBoundaryIs><gml:LinearRing><gml:coordinates>2,0 2,2 3,2 3,3 2,0</gml:coordinates></gml:LinearRing></gml:outerBoundaryIs></gml:Polygon></gml:polygonMember></gml:MultiPolygon></ogr:geometryProperty>
</ogr:lines_to_polygon>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_to_polygon fid="lines.3">
</ogr:lines_to_polygon>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_to_polygon fid="lines.4">
</ogr:lines_to_polygon>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_to_polygon fid="lines.5">
</ogr:lines_to_polygon>
</gml:featureMember>
<gml:featureMember>
<ogr:lines_to_polygon fid="lines.6">
</ogr:lines_to_polygon>
</gml:featureMember>
</ogr:FeatureCollection>
@@ -2,11 +2,11 @@
<GMLFeatureClass>
<Name>polys_to_lines</Name>
<ElementPath>polys_to_lines</ElementPath>
<!--LINESTRING-->
<GeometryType>2</GeometryType>
<!--MULTILINESTRING-->
<GeometryType>5</GeometryType>
<SRSName>EPSG:4326</SRSName>
<DatasetSpecificInfo>
<FeatureCount>5</FeatureCount>
<FeatureCount>4</FeatureCount>
<ExtentXMin>0.00000</ExtentXMin>
<ExtentXMax>9.00000</ExtentXMax>
<ExtentYMin>-1.00000</ExtentYMin>

0 comments on commit b5dc9fd

Please sign in to comment.
You can’t perform that action at this time.