Skip to content

Commit bcc6627

Browse files
committedAug 20, 2017
Upgrade Convert Geometries algorithm to maintain Z/M, curves were possible
1 parent 8da29c0 commit bcc6627

File tree

3 files changed

+139
-346
lines changed

3 files changed

+139
-346
lines changed
 

‎python/plugins/processing/algs/qgis/GeometryConvert.py

Lines changed: 131 additions & 135 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,10 @@
2727

2828
from qgis.core import (QgsFeature,
2929
QgsGeometry,
30+
QgsMultiPointV2,
31+
QgsMultiLineString,
32+
QgsLineString,
33+
QgsPolygonV2,
3034
QgsFeatureSink,
3135
QgsWkbTypes,
3236
QgsProcessingException,
@@ -72,20 +76,32 @@ def processAlgorithm(self, parameters, context, feedback):
7276
source = self.parameterAsSource(parameters, self.INPUT, context)
7377
index = self.parameterAsEnum(parameters, self.TYPE, context)
7478

75-
splitNodes = False
7679
if index == 0:
7780
newType = QgsWkbTypes.Point
7881
elif index == 1:
7982
newType = QgsWkbTypes.Point
80-
splitNodes = True
83+
if QgsWkbTypes.hasM(source.wkbType()):
84+
newType = QgsWkbTypes.addM(newType)
85+
if QgsWkbTypes.hasZ(source.wkbType()):
86+
newType = QgsWkbTypes.addZ(newType)
8187
elif index == 2:
8288
newType = QgsWkbTypes.LineString
89+
if QgsWkbTypes.hasM(source.wkbType()):
90+
newType = QgsWkbTypes.addM(newType)
91+
if QgsWkbTypes.hasZ(source.wkbType()):
92+
newType = QgsWkbTypes.addZ(newType)
8393
elif index == 3:
8494
newType = QgsWkbTypes.MultiLineString
85-
elif index == 4:
86-
newType = QgsWkbTypes.Polygon
95+
if QgsWkbTypes.hasM(source.wkbType()):
96+
newType = QgsWkbTypes.addM(newType)
97+
if QgsWkbTypes.hasZ(source.wkbType()):
98+
newType = QgsWkbTypes.addZ(newType)
8799
else:
88-
newType = QgsWkbTypes.Point
100+
newType = QgsWkbTypes.Polygon
101+
if QgsWkbTypes.hasM(source.wkbType()):
102+
newType = QgsWkbTypes.addM(newType)
103+
if QgsWkbTypes.hasZ(source.wkbType()):
104+
newType = QgsWkbTypes.addZ(newType)
89105

90106
(sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
91107
source.fields(), newType, source.sourceCrs())
@@ -99,140 +115,120 @@ def processAlgorithm(self, parameters, context, feedback):
99115

100116
if not f.hasGeometry():
101117
sink.addFeature(f, QgsFeatureSink.FastInsert)
102-
103-
geom = f.geometry()
104-
geomType = geom.wkbType()
105-
106-
if QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.PointGeometry and not QgsWkbTypes.isMultiType(geomType):
107-
if newType == QgsWkbTypes.Point:
108-
sink.addFeature(f, QgsFeatureSink.FastInsert)
109-
else:
110-
raise QgsProcessingException(
111-
self.tr('Cannot convert from {0} to {1}').format(geomType, newType))
112-
elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.PointGeometry and QgsWkbTypes.isMultiType(geomType):
113-
if newType == QgsWkbTypes.Point and splitNodes:
114-
points = geom.asMultiPoint()
115-
for p in points:
116-
feat = QgsFeature()
117-
feat.setAttributes(f.attributes())
118-
feat.setGeometry(QgsGeometry.fromPoint(p))
119-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
120-
elif newType == QgsWkbTypes.Point:
121-
feat = QgsFeature()
122-
feat.setAttributes(f.attributes())
123-
feat.setGeometry(geom.centroid())
124-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
125-
else:
126-
raise QgsProcessingException(
127-
self.tr('Cannot convert from {0} to {1}').format(geomType, newType))
128-
elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.LineGeometry and not QgsWkbTypes.isMultiType(geomType):
129-
if newType == QgsWkbTypes.Point and splitNodes:
130-
points = geom.asPolyline()
131-
for p in points:
132-
feat = QgsFeature()
133-
feat.setAttributes(f.attributes())
134-
feat.setGeometry(QgsGeometry.fromPoint(p))
135-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
136-
elif newType == QgsWkbTypes.Point:
137-
feat = QgsFeature()
138-
feat.setAttributes(f.attributes())
139-
feat.setGeometry(geom.centroid())
140-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
141-
elif newType == QgsWkbTypes.LineString:
142-
sink.addFeature(f, QgsFeatureSink.FastInsert)
143-
else:
144-
raise QgsProcessingException(
145-
self.tr('Cannot convert from {0} to {1}').format(geomType, newType))
146-
elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.LineGeometry and QgsWkbTypes.isMultiType(
147-
geomType):
148-
if newType == QgsWkbTypes.Point and splitNodes:
149-
lines = geom.asMultiPolyline()
150-
for line in lines:
151-
for p in line:
152-
feat = QgsFeature()
153-
feat.setAttributes(f.attributes())
154-
feat.setGeometry(QgsGeometry.fromPoint(p))
155-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
156-
elif newType == QgsWkbTypes.Point:
118+
else:
119+
for p in self.convertGeometry(f.geometry(), index):
157120
feat = QgsFeature()
158121
feat.setAttributes(f.attributes())
159-
feat.setGeometry(geom.centroid())
122+
feat.setGeometry(p)
160123
sink.addFeature(feat, QgsFeatureSink.FastInsert)
161-
elif newType == QgsWkbTypes.LineString:
162-
lines = geom.asMultiPolyline()
163-
for line in lines:
164-
feat = QgsFeature()
165-
feat.setAttributes(f.attributes())
166-
feat.setGeometry(QgsGeometry.fromPolyline(line))
167-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
168-
elif newType == QgsWkbTypes.MultiLineString:
169-
sink.addFeature(f, QgsFeatureSink.FastInsert)
170-
else:
171-
raise QgsProcessingException(
172-
self.tr('Cannot convert from {0} to {1}').format(geomType, newType))
173-
elif QgsWkbTypes.geometryType(geomType) == QgsWkbTypes.PolygonGeometry and not QgsWkbTypes.isMultiType(
174-
geomType):
175-
if newType == QgsWkbTypes.Point and splitNodes:
176-
rings = geom.asPolygon()
177-
for ring in rings:
178-
for p in ring:
179-
feat = QgsFeature()
180-
feat.setAttributes(f.attributes())
181-
feat.setGeometry(QgsGeometry.fromPoint(p))
182-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
183-
elif newType == QgsWkbTypes.Point:
184-
feat = QgsFeature()
185-
feat.setAttributes(f.attributes())
186-
feat.setGeometry(geom.centroid())
187-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
188-
elif newType == QgsWkbTypes.MultiLineString:
189-
rings = geom.asPolygon()
190-
feat = QgsFeature()
191-
feat.setAttributes(f.attributes())
192-
feat.setGeometry(QgsGeometry.fromMultiPolyline(rings))
193-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
194-
elif newType == QgsWkbTypes.Polygon:
195-
sink.addFeature(f, QgsFeatureSink.FastInsert)
196-
else:
197-
raise QgsProcessingException(
198-
self.tr('Cannot convert from {0} to {1}').format(geomType, newType))
199-
elif QgsWkbTypes.geometryType(
200-
geomType) == QgsWkbTypes.PolygonGeometry and QgsWkbTypes.isMultiType(
201-
geomType):
202-
if newType == QgsWkbTypes.Point and splitNodes:
203-
polygons = geom.asMultiPolygon()
204-
for polygon in polygons:
205-
for line in polygon:
206-
for p in line:
207-
feat = QgsFeature()
208-
feat.setAttributes(f.attributes())
209-
feat.setGeometry(QgsGeometry.fromPoint(p))
210-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
211-
elif newType == QgsWkbTypes.Point:
212-
feat = QgsFeature()
213-
feat.setAttributes(f.attributes())
214-
feat.setGeometry(geom.centroid())
215-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
216-
elif newType == QgsWkbTypes.LineString:
217-
polygons = geom.asMultiPolygon()
218-
for polygon in polygons:
219-
feat = QgsFeature()
220-
feat.setAttributes(f.attributes())
221-
feat.setGeometry(QgsGeometry.fromPolyline(polygon))
222-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
223-
elif newType == QgsWkbTypes.Polygon:
224-
polygons = geom.asMultiPolygon()
225-
for polygon in polygons:
226-
feat = QgsFeature()
227-
feat.setAttributes(f.attributes())
228-
feat.setGeometry(QgsGeometry.fromPolygon(polygon))
229-
sink.addFeature(feat, QgsFeatureSink.FastInsert)
230-
elif newType in [QgsWkbTypes.MultiLineString, QgsWkbTypes.MultiPolygon]:
231-
sink.addFeature(f, QgsFeatureSink.FastInsert)
232-
else:
233-
raise QgsProcessingException(
234-
self.tr('Cannot convert from {0} to {1}').format(geomType, newType))
235124

236125
feedback.setProgress(int(current * total))
237126

238127
return {self.OUTPUT: dest_id}
128+
129+
def convertGeometry(self, geom, target_type):
130+
# returns an array of output geometries for the input geometry
131+
if target_type == 0:
132+
#centroid
133+
return self.convertToCentroid(geom)
134+
elif target_type == 1:
135+
#nodes
136+
return self.convertToNodes(geom)
137+
elif target_type == 2:
138+
#linestrings
139+
return self.convertToLineStrings(geom)
140+
elif target_type == 3:
141+
#multilinestrings
142+
return self.convertToMultiLineStrings(geom)
143+
elif target_type == 4:
144+
#polygon
145+
return self.convertToPolygon(geom)
146+
147+
def convertToCentroid(self, geom):
148+
return [geom.centroid()]
149+
150+
def convertToNodes(self, geom):
151+
mp = QgsMultiPointV2()
152+
# TODO: mega inefficient - needs rework when geometry iterators land
153+
# (but at least it doesn't lose Z/M values)
154+
for g in geom.geometry().coordinateSequence():
155+
for r in g:
156+
for p in r:
157+
mp.addGeometry(p)
158+
return [QgsGeometry(mp)]
159+
160+
def convertToLineStrings(self, geom):
161+
if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry:
162+
raise QgsProcessingException(
163+
self.tr('Cannot convert from {0} to LineStrings').format(QgsWkbTypes.displayString(geom.wkbType())))
164+
elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry:
165+
if QgsWkbTypes.isMultiType(geom.wkbType()):
166+
return geom.asGeometryCollection()
167+
else:
168+
#line to line
169+
return [geom]
170+
else:
171+
# polygons to lines
172+
# we just use the boundary here - that consists of all rings in the (multi)polygon
173+
boundary = QgsGeometry(geom.geometry().boundary())
174+
# boundary will be multipart
175+
return boundary.asGeometryCollection()
176+
177+
def convertToMultiLineStrings(self, geom):
178+
if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry:
179+
raise QgsProcessingException(
180+
self.tr('Cannot convert from {0} to MultiLineStrings').format(QgsWkbTypes.displayString(geom.wkbType())))
181+
elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry:
182+
if QgsWkbTypes.isMultiType(geom.wkbType()):
183+
return [geom]
184+
else:
185+
# line to multiLine
186+
ml = QgsMultiLineString()
187+
ml.addGeometry(geom.geometry().clone())
188+
return [QgsGeometry(ml)]
189+
else:
190+
# polygons to multilinestring
191+
# we just use the boundary here - that consists of all rings in the (multi)polygon
192+
return [QgsGeometry(geom.geometry().boundary())]
193+
194+
def convertToPolygon(self, geom):
195+
if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry and geom.geometry().nCoordinates() < 3:
196+
raise QgsProcessingException(
197+
self.tr('Cannot convert from Point to Polygon').format(QgsWkbTypes.displayString(geom.wkbType())))
198+
elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry:
199+
# multipoint with at least 3 points
200+
# TODO: mega inefficient - needs rework when geometry iterators land
201+
# (but at least it doesn't lose Z/M values)
202+
points = []
203+
for g in geom.geometry().coordinateSequence():
204+
for r in g:
205+
for p in r:
206+
points.append(p)
207+
linestring = QgsLineString(points)
208+
linestring.close()
209+
p = QgsPolygonV2()
210+
p.setExteriorRing(linestring)
211+
return [QgsGeometry(p)]
212+
elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry:
213+
if QgsWkbTypes.isMultiType(geom):
214+
parts = []
215+
for i in range(geom.geometry().numGeometries()):
216+
p = QgsPolygonV2()
217+
linestring = geom.geometry().geometryN(i).clone()
218+
linestring.close()
219+
p.setExteriorRing(linestring)
220+
parts.append(QgsGeometry(p))
221+
return QgsGeometry.collectGeometry(parts)
222+
else:
223+
# linestring to polygon
224+
p = QgsPolygonV2()
225+
linestring = geom.geometry().clone()
226+
linestring.close()
227+
p.setExteriorRing(linestring)
228+
return [QgsGeometry(p)]
229+
else:
230+
#polygon
231+
if QgsWkbTypes.isMultiType(geom):
232+
return geom.asGeometryCollection()
233+
else:
234+
return [geom]

‎python/plugins/processing/tests/testdata/expected/convert_poly_nodes.gfs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@
22
<GMLFeatureClass>
33
<Name>convert_poly_nodes</Name>
44
<ElementPath>convert_poly_nodes</ElementPath>
5-
<!--POINT-->
6-
<GeometryType>1</GeometryType>
5+
<!--MULTIPOINT-->
6+
<GeometryType>4</GeometryType>
77
<SRSName>EPSG:4326</SRSName>
88
<DatasetSpecificInfo>
9-
<FeatureCount>33</FeatureCount>
9+
<FeatureCount>6</FeatureCount>
1010
<ExtentXMin>-1.00000</ExtentXMin>
1111
<ExtentXMax>10.00000</ExtentXMax>
1212
<ExtentYMin>-3.00000</ExtentYMin>

0 commit comments

Comments
 (0)
Please sign in to comment.