""" *************************************************************************** PointsFromLines.py --------------------- Date : August 2013 Copyright : (C) 2013 by Alexander Bruy Email : alexander dot bruy at gmail dot com *************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * *************************************************************************** """ __author__ = "Alexander Bruy" __date__ = "August 2013" __copyright__ = "(C) 2013, Alexander Bruy" from osgeo import gdal from qgis.PyQt.QtCore import QMetaType from qgis.core import ( QgsFeature, QgsFeatureSink, QgsFields, QgsField, QgsGeometry, QgsPointXY, QgsWkbTypes, QgsProcessing, QgsProcessingException, QgsFeatureRequest, QgsProcessingParameterRasterLayer, QgsProcessingParameterFeatureSource, QgsProcessingParameterFeatureSink, ) from processing.tools import raster from processing.algs.qgis.QgisAlgorithm import QgisAlgorithm gdal.UseExceptions() class PointsFromLines(QgisAlgorithm): INPUT_RASTER = "INPUT_RASTER" RASTER_BAND = "RASTER_BAND" INPUT_VECTOR = "INPUT_VECTOR" OUTPUT = "OUTPUT" def group(self): return self.tr("Vector creation") def groupId(self): return "vectorcreation" def __init__(self): super().__init__() def initAlgorithm(self, config=None): self.addParameter( QgsProcessingParameterRasterLayer( self.INPUT_RASTER, self.tr("Raster layer") ) ) self.addParameter( QgsProcessingParameterFeatureSource( self.INPUT_VECTOR, self.tr("Vector layer"), [QgsProcessing.SourceType.TypeVectorLine], ) ) self.addParameter( QgsProcessingParameterFeatureSink( self.OUTPUT, self.tr("Points along lines"), QgsProcessing.SourceType.TypeVectorPoint, ) ) def name(self): return "generatepointspixelcentroidsalongline" def displayName(self): return self.tr("Generate points (pixel centroids) along line") def processAlgorithm(self, parameters, context, feedback): source = self.parameterAsSource(parameters, self.INPUT_VECTOR, context) if source is None: raise QgsProcessingException( self.invalidSourceError(parameters, self.INPUT_VECTOR) ) raster_layer = self.parameterAsRasterLayer( parameters, self.INPUT_RASTER, context ) rasterPath = raster_layer.source() rasterDS = gdal.Open(rasterPath, gdal.GA_ReadOnly) geoTransform = rasterDS.GetGeoTransform() rasterDS = None fields = QgsFields() fields.append(QgsField("id", QMetaType.Type.Int, "", 10, 0)) fields.append(QgsField("line_id", QMetaType.Type.Int, "", 10, 0)) fields.append(QgsField("point_id", QMetaType.Type.Int, "", 10, 0)) (sink, dest_id) = self.parameterAsSink( parameters, self.OUTPUT, context, fields, QgsWkbTypes.Type.Point, raster_layer.crs(), ) if sink is None: raise QgsProcessingException(self.invalidSinkError(parameters, self.OUTPUT)) outFeature = QgsFeature() outFeature.setFields(fields) self.fid = 0 self.lineId = 0 self.pointId = 0 features = source.getFeatures( QgsFeatureRequest().setDestinationCrs( raster_layer.crs(), context.transformContext() ) ) total = 100.0 / source.featureCount() if source.featureCount() else 0 for current, f in enumerate(features): if feedback.isCanceled(): break if not f.hasGeometry(): continue geom = f.geometry() if geom.isMultipart(): lines = geom.asMultiPolyline() for line in lines: for i in range(len(line) - 1): p1 = line[i] p2 = line[i + 1] (x1, y1) = raster.mapToPixel(p1.x(), p1.y(), geoTransform) (x2, y2) = raster.mapToPixel(p2.x(), p2.y(), geoTransform) self.buildLine(x1, y1, x2, y2, geoTransform, sink, outFeature) else: points = geom.asPolyline() for i in range(len(points) - 1): p1 = points[i] p2 = points[i + 1] (x1, y1) = raster.mapToPixel(p1.x(), p1.y(), geoTransform) (x2, y2) = raster.mapToPixel(p2.x(), p2.y(), geoTransform) self.buildLine(x1, y1, x2, y2, geoTransform, sink, outFeature) self.pointId = 0 self.lineId += 1 feedback.setProgress(int(current * total)) sink.finalize() return {self.OUTPUT: dest_id} def buildLine(self, startX, startY, endX, endY, geoTransform, writer, feature): if startX == endX: if startY > endY: (startY, endY) = (endY, startY) row = startX for col in range(startY, endY + 1): self.createPoint(row, col, geoTransform, writer, feature) elif startY == endY: if startX > endX: (startX, endX) = (endX, startX) col = startY for row in range(startX, endX + 1): self.createPoint(row, col, geoTransform, writer, feature) else: width = endX - startX height = endY - startY if width < 0: dx1 = -1 dx2 = -1 else: dx1 = 1 dx2 = 1 if height < 0: dy1 = -1 else: dy1 = 1 dy2 = 0 longest = abs(width) shortest = abs(height) if not longest > shortest: (longest, shortest) = (shortest, longest) if height < 0: dy2 = -1 else: dy2 = 1 dx2 = 0 err = longest / 2 for i in range(longest + 1): self.createPoint(startX, startY, geoTransform, writer, feature) err += shortest if not err < longest: err = err - longest startX += dx1 startY += dy1 else: startX += dx2 startY += dy2 def createPoint(self, pX, pY, geoTransform, writer, feature): (x, y) = raster.pixelToMap(pX, pY, geoTransform) feature.setGeometry(QgsGeometry.fromPointXY(QgsPointXY(x, y))) feature["id"] = self.fid feature["line_id"] = self.lineId feature["point_id"] = self.pointId self.fid += 1 self.pointId += 1 writer.addFeature(feature, QgsFeatureSink.Flag.FastInsert)