PointsFromLines.py 7.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205
  1. """
  2. ***************************************************************************
  3. PointsFromLines.py
  4. ---------------------
  5. Date : August 2013
  6. Copyright : (C) 2013 by Alexander Bruy
  7. Email : alexander dot bruy at gmail 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__ = 'Alexander Bruy'
  18. __date__ = 'August 2013'
  19. __copyright__ = '(C) 2013, Alexander Bruy'
  20. from osgeo import gdal
  21. from qgis.PyQt.QtCore import QVariant
  22. from qgis.core import (QgsFeature,
  23. QgsFeatureSink,
  24. QgsFields,
  25. QgsField,
  26. QgsGeometry,
  27. QgsPointXY,
  28. QgsWkbTypes,
  29. QgsProcessing,
  30. QgsProcessingException,
  31. QgsFeatureRequest,
  32. QgsProcessingParameterRasterLayer,
  33. QgsProcessingParameterFeatureSource,
  34. QgsProcessingParameterFeatureSink)
  35. from processing.tools import raster
  36. from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm
  37. class PointsFromLines(QgisAlgorithm):
  38. INPUT_RASTER = 'INPUT_RASTER'
  39. RASTER_BAND = 'RASTER_BAND'
  40. INPUT_VECTOR = 'INPUT_VECTOR'
  41. OUTPUT = 'OUTPUT'
  42. def group(self):
  43. return self.tr('Vector creation')
  44. def groupId(self):
  45. return 'vectorcreation'
  46. def __init__(self):
  47. super().__init__()
  48. def initAlgorithm(self, config=None):
  49. self.addParameter(QgsProcessingParameterRasterLayer(self.INPUT_RASTER,
  50. self.tr('Raster layer')))
  51. self.addParameter(QgsProcessingParameterFeatureSource(self.INPUT_VECTOR,
  52. self.tr('Vector layer'), [QgsProcessing.TypeVectorLine]))
  53. self.addParameter(QgsProcessingParameterFeatureSink(self.OUTPUT, self.tr('Points along lines'), QgsProcessing.TypeVectorPoint))
  54. def name(self):
  55. return 'generatepointspixelcentroidsalongline'
  56. def displayName(self):
  57. return self.tr('Generate points (pixel centroids) along line')
  58. def processAlgorithm(self, parameters, context, feedback):
  59. source = self.parameterAsSource(parameters, self.INPUT_VECTOR, context)
  60. if source is None:
  61. raise QgsProcessingException(self.invalidSourceError(parameters, self.INPUT_VECTOR))
  62. raster_layer = self.parameterAsRasterLayer(parameters, self.INPUT_RASTER, context)
  63. rasterPath = raster_layer.source()
  64. rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly)
  65. geoTransform = rasterDS.GetGeoTransform()
  66. rasterDS = None
  67. fields = QgsFields()
  68. fields.append(QgsField('id', QVariant.Int, '', 10, 0))
  69. fields.append(QgsField('line_id', QVariant.Int, '', 10, 0))
  70. fields.append(QgsField('point_id', QVariant.Int, '', 10, 0))
  71. (sink, dest_id) = self.parameterAsSink(parameters, self.OUTPUT, context,
  72. fields, QgsWkbTypes.Point, raster_layer.crs())
  73. if sink is None:
  74. raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT))
  75. outFeature = QgsFeature()
  76. outFeature.setFields(fields)
  77. self.fid = 0
  78. self.lineId = 0
  79. self.pointId = 0
  80. features = source.getFeatures(QgsFeatureRequest().setDestinationCrs(raster_layer.crs(), context.transformContext()))
  81. total = 100.0 / source.featureCount() if source.featureCount() else 0
  82. for current, f in enumerate(features):
  83. if feedback.isCanceled():
  84. break
  85. if not f.hasGeometry():
  86. continue
  87. geom = f.geometry()
  88. if geom.isMultipart():
  89. lines = geom.asMultiPolyline()
  90. for line in lines:
  91. for i in range(len(line) - 1):
  92. p1 = line[i]
  93. p2 = line[i + 1]
  94. (x1, y1) = raster.mapToPixel(p1.x(), p1.y(),
  95. geoTransform)
  96. (x2, y2) = raster.mapToPixel(p2.x(), p2.y(),
  97. geoTransform)
  98. self.buildLine(x1, y1, x2, y2, geoTransform,
  99. sink, outFeature)
  100. else:
  101. points = geom.asPolyline()
  102. for i in range(len(points) - 1):
  103. p1 = points[i]
  104. p2 = points[i + 1]
  105. (x1, y1) = raster.mapToPixel(p1.x(), p1.y(), geoTransform)
  106. (x2, y2) = raster.mapToPixel(p2.x(), p2.y(), geoTransform)
  107. self.buildLine(x1, y1, x2, y2, geoTransform, sink,
  108. outFeature)
  109. self.pointId = 0
  110. self.lineId += 1
  111. feedback.setProgress(int(current * total))
  112. return {self.OUTPUT: dest_id}
  113. def buildLine(self, startX, startY, endX, endY, geoTransform, writer, feature):
  114. if startX == endX:
  115. if startY > endY:
  116. (startY, endY) = (endY, startY)
  117. row = startX
  118. for col in range(startY, endY + 1):
  119. self.createPoint(row, col, geoTransform, writer, feature)
  120. elif startY == endY:
  121. if startX > endX:
  122. (startX, endX) = (endX, startX)
  123. col = startY
  124. for row in range(startX, endX + 1):
  125. self.createPoint(row, col, geoTransform, writer, feature)
  126. else:
  127. width = endX - startX
  128. height = endY - startY
  129. if width < 0:
  130. dx1 = -1
  131. dx2 = -1
  132. else:
  133. dx1 = 1
  134. dx2 = 1
  135. if height < 0:
  136. dy1 = -1
  137. else:
  138. dy1 = 1
  139. dy2 = 0
  140. longest = abs(width)
  141. shortest = abs(height)
  142. if not longest > shortest:
  143. (longest, shortest) = (shortest, longest)
  144. if height < 0:
  145. dy2 = -1
  146. else:
  147. dy2 = 1
  148. dx2 = 0
  149. err = longest / 2
  150. for i in range(longest + 1):
  151. self.createPoint(startX, startY, geoTransform, writer, feature)
  152. err += shortest
  153. if not err < longest:
  154. err = err - longest
  155. startX += dx1
  156. startY += dy1
  157. else:
  158. startX += dx2
  159. startY += dy2
  160. def createPoint(self, pX, pY, geoTransform, writer, feature):
  161. (x, y) = raster.pixelToMap(pX, pY, geoTransform)
  162. feature.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(x, y)))
  163. feature['id'] = self.fid
  164. feature['line_id'] = self.lineId
  165. feature['point_id'] = self.pointId
  166. self.fid += 1
  167. self.pointId += 1
  168. writer.addFeature(feature, QgsFeatureSink.FastInsert)