GeometryConvert.py 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. """
  2. ***************************************************************************
  3. Gridify.py
  4. ---------------------
  5. Date : May 2010
  6. Copyright : (C) 2010 by Michael Minn
  7. Email : pyqgis at michaelminn dot com
  8. ***************************************************************************
  9. * *
  10. * This program is free software; you can redistribute it and/or modify *
  11. * it under the terms of the GNU General Public License as published by *
  12. * the Free Software Foundation; either version 2 of the License, or *
  13. * (at your option) any later version. *
  14. * *
  15. ***************************************************************************
  16. """
  17. __author__ = 'Michael Minn'
  18. __date__ = 'May 2010'
  19. __copyright__ = '(C) 2010, Michael Minn'
  20. from qgis.core import (QgsFeature,
  21. QgsGeometry,
  22. QgsMultiPoint,
  23. QgsMultiLineString,
  24. QgsLineString,
  25. QgsPolygon,
  26. QgsFeatureSink,
  27. QgsWkbTypes,
  28. QgsProcessingException,
  29. QgsProcessingParameterFeatureSource,
  30. QgsProcessingParameterEnum,
  31. QgsProcessingParameterFeatureSink)
  32. from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
  33. class GeometryConvert(QgisAlgorithm):
  34. INPUT = 'INPUT'
  35. TYPE = 'TYPE'
  36. OUTPUT = 'OUTPUT'
  37. def group(self):
  38. return self.tr('Vector geometry')
  39. def groupId(self):
  40. return 'vectorgeometry'
  41. def __init__(self):
  42. super().__init__()
  43. def initAlgorithm(self, config=None):
  44. self.types = [self.tr('Centroids'),
  45. self.tr('Nodes'),
  46. self.tr('Linestrings'),
  47. self.tr('Multilinestrings'),
  48. self.tr('Polygons')]
  49. self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT,
  50. self.tr('Input layer')))
  51. self.addParameter(QgsProcessingParameterEnum(self.TYPE,
  52. self.tr('New geometry type'), options=self.types))
  53. self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT,
  54. self.tr('Converted')))
  55. def name(self):
  56. return 'convertgeometrytype'
  57. def displayName(self):
  58. return self.tr('Convert geometry type')
  59. def processAlgorithm(self, parameters, context, feedback):
  60. source = self.parameterAsSource(parameters, self.INPUT, context)
  61. if source is None:
  62. raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT))
  63. index = self.parameterAsEnum(parameters, self.TYPE, context)
  64. if index == 0:
  65. newType = QgsWkbTypes.Point
  66. elif index == 1:
  67. newType = QgsWkbTypes.Point
  68. if QgsWkbTypes.hasM(source.wkbType()):
  69. newType = QgsWkbTypes.addM(newType)
  70. if QgsWkbTypes.hasZ(source.wkbType()):
  71. newType = QgsWkbTypes.addZ(newType)
  72. elif index == 2:
  73. newType = QgsWkbTypes.LineString
  74. if QgsWkbTypes.hasM(source.wkbType()):
  75. newType = QgsWkbTypes.addM(newType)
  76. if QgsWkbTypes.hasZ(source.wkbType()):
  77. newType = QgsWkbTypes.addZ(newType)
  78. elif index == 3:
  79. newType = QgsWkbTypes.MultiLineString
  80. if QgsWkbTypes.hasM(source.wkbType()):
  81. newType = QgsWkbTypes.addM(newType)
  82. if QgsWkbTypes.hasZ(source.wkbType()):
  83. newType = QgsWkbTypes.addZ(newType)
  84. else:
  85. newType = QgsWkbTypes.Polygon
  86. if QgsWkbTypes.hasM(source.wkbType()):
  87. newType = QgsWkbTypes.addM(newType)
  88. if QgsWkbTypes.hasZ(source.wkbType()):
  89. newType = QgsWkbTypes.addZ(newType)
  90. (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
  91. source.fields(), newType, source.sourceCrs())
  92. if sink is None:
  93. raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))
  94. features = source.getFeatures()
  95. total = 100.0 / source.featureCount() if source.featureCount() else 0
  96. for current, f in enumerate(features):
  97. if feedback.isCanceled():
  98. break
  99. if not f.hasGeometry():
  100. sink.addFeature(f, QgsFeatureSink.FastInsert)
  101. else:
  102. for p in self.convertGeometry(f.geometry(), index):
  103. feat = QgsFeature()
  104. feat.setAttributes(f.attributes())
  105. feat.setGeometry(p)
  106. sink.addFeature(feat, QgsFeatureSink.FastInsert)
  107. feedback.setProgress(int(current * total))
  108. return {self.OUTPUT: dest_id}
  109. def convertGeometry(self, geom, target_type):
  110. # returns an array of output geometries for the input geometry
  111. if target_type == 0:
  112. # centroid
  113. return self.convertToCentroid(geom)
  114. elif target_type == 1:
  115. # nodes
  116. return self.convertToNodes(geom)
  117. elif target_type == 2:
  118. # linestrings
  119. return self.convertToLineStrings(geom)
  120. elif target_type == 3:
  121. # multilinestrings
  122. return self.convertToMultiLineStrings(geom)
  123. elif target_type == 4:
  124. # polygon
  125. return self.convertToPolygon(geom)
  126. def convertToCentroid(self, geom):
  127. return [geom.centroid()]
  128. def convertToNodes(self, geom):
  129. mp = QgsMultiPoint()
  130. # TODO: mega inefficient - needs rework when geometry iterators land
  131. # (but at least it doesn't lose Z/M values)
  132. for g in geom.constGet().coordinateSequence():
  133. for r in g:
  134. for p in r:
  135. mp.addGeometry(p)
  136. return [QgsGeometry(mp)]
  137. def convertToLineStrings(self, geom):
  138. if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry:
  139. raise QgsProcessingException(
  140. self.tr('Cannot convert from {0} to LineStrings').format(QgsWkbTypes.displayString(geom.wkbType())))
  141. elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry:
  142. if QgsWkbTypes.isMultiType(geom.wkbType()):
  143. return geom.asGeometryCollection()
  144. else:
  145. # line to line
  146. return [geom]
  147. else:
  148. # polygons to lines
  149. # we just use the boundary here - that consists of all rings in the (multi)polygon
  150. boundary = QgsGeometry(geom.constGet().boundary())
  151. # boundary will be multipart
  152. return boundary.asGeometryCollection()
  153. def convertToMultiLineStrings(self, geom):
  154. if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry:
  155. raise QgsProcessingException(
  156. self.tr('Cannot convert from {0} to MultiLineStrings').format(QgsWkbTypes.displayString(geom.wkbType())))
  157. elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry:
  158. if QgsWkbTypes.isMultiType(geom.wkbType()):
  159. return [geom]
  160. else:
  161. # line to multiLine
  162. ml = QgsMultiLineString()
  163. ml.addGeometry(geom.constGet().clone())
  164. return [QgsGeometry(ml)]
  165. else:
  166. # polygons to multilinestring
  167. # we just use the boundary here - that consists of all rings in the (multi)polygon
  168. return [QgsGeometry(geom.constGet().boundary())]
  169. def convertToPolygon(self, geom):
  170. if QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry and geom.constGet().nCoordinates() < 3:
  171. raise QgsProcessingException(
  172. self.tr('Cannot convert from Point to Polygon').format(QgsWkbTypes.displayString(geom.wkbType())))
  173. elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.PointGeometry:
  174. # multipoint with at least 3 points
  175. # TODO: mega inefficient - needs rework when geometry iterators land
  176. # (but at least it doesn't lose Z/M values)
  177. points = []
  178. for g in geom.constGet().coordinateSequence():
  179. for r in g:
  180. for p in r:
  181. points.append(p)
  182. linestring = QgsLineString(points)
  183. linestring.close()
  184. p = QgsPolygon()
  185. p.setExteriorRing(linestring)
  186. return [QgsGeometry(p)]
  187. elif QgsWkbTypes.geometryType(geom.wkbType()) == QgsWkbTypes.LineGeometry:
  188. if QgsWkbTypes.isMultiType(geom.wkbType()):
  189. parts = []
  190. for i in range(geom.constGet().numGeometries()):
  191. p = QgsPolygon()
  192. linestring = geom.constGet().geometryN(i).clone()
  193. linestring.close()
  194. p.setExteriorRing(linestring)
  195. parts.append(QgsGeometry(p))
  196. return QgsGeometry.collectGeometry(parts)
  197. else:
  198. # linestring to polygon
  199. p = QgsPolygon()
  200. linestring = geom.constGet().clone()
  201. linestring.close()
  202. p.setExteriorRing(linestring)
  203. return [QgsGeometry(p)]
  204. else:
  205. # polygon
  206. if QgsWkbTypes.isMultiType(geom.wkbType()):
  207. return geom.asGeometryCollection()
  208. else:
  209. return [geom]