Skip to content

Commit

Permalink
[processing] restore and improve lines to pol / pol to lines algs (#4850
Browse files Browse the repository at this point in the history
)

New geometry model for lines to pol / pol to lines agls.
  • Loading branch information
nirvn committed Jul 14, 2017
1 parent f84a3bb commit b5dc9fd
Show file tree
Hide file tree
Showing 9 changed files with 229 additions and 103 deletions.
116 changes: 82 additions & 34 deletions python/plugins/processing/algs/qgis/LinesToPolygons.py
Expand Up @@ -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]
Expand All @@ -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'
Expand All @@ -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
107 changes: 84 additions & 23 deletions python/plugins/processing/algs/qgis/PolygonsToLines.py
Expand Up @@ -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]
Expand All @@ -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'
Expand All @@ -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
10 changes: 6 additions & 4 deletions python/plugins/processing/algs/qgis/QGISAlgorithmProvider.py
Expand Up @@ -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
Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -190,8 +190,8 @@ def getAlgs(self):
# NearestNeighbourAnalysis(), MeanCoords(),
# LinesIntersection(), UniqueValues(), PointDistance(),
# ExportGeometryInfo(),
# , SinglePartsToMultiparts(),
# PolygonsToLines(), LinesToPolygons(), ExtractNodes(),
# SinglePartsToMultiparts(),
# ExtractNodes(),
# ConvexHull(), FixedDistanceBuffer(),
# VariableDistanceBuffer(),
# RandomSelection(), RandomSelectionWithinSubsets(),
Expand Down Expand Up @@ -260,8 +260,10 @@ def getAlgs(self):
ImportIntoPostGIS(),
ImportIntoSpatialite(),
Intersection(),
LinesToPolygons(),
Merge(),
PointsLayerFromTable(),
PolygonsToLines(),
PostGISExecuteSQL(),
RandomExtract(),
RandomExtractWithinSubsets(),
Expand Down
2 changes: 1 addition & 1 deletion python/plugins/processing/gui/menus.py
Expand Up @@ -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})
Expand Down
Expand Up @@ -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>
Expand Down
Expand Up @@ -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>
Expand Up @@ -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>
Expand Down

0 comments on commit b5dc9fd

Please sign in to comment.