This Processing Script is made available under the CC-0 license.
Generate Rectangles along Lines
"""
*****************************************************************************
* *
* 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. *
* *
*****************************************************************************
* Created: 19-06-2025 by Christoph Candido (christoph.candido@gmx.at) *
* *
* This algorithm creates a polygon layer with rectangles along the features *
* of a line layer. It demonstrates the @alg decorator and shows how all *
* input parameters can be stored as new default values. *
* *
* Due to the use of the @alg decorator, this algorithm is only supported *
* by QGIS >= 3.6. *
* *
*****************************************************************************
"""
from qgis.PyQt.QtCore import QVariant
from qgis import processing
from qgis.processing import alg
from qgis.core import (QgsProcessing,
QgsGeometry,
QgsFeature,
QgsWkbTypes,
QgsFeatureSink,
QgsFields,
QgsField,
QgsPointXY,
QgsSettings,
QgsApplication,
QgsProcessingAlgorithm)
@alg(name="algRectAlongLines", label="Generate rectangles along lines", group="", group_label="")
@alg.input(type=alg.SOURCE, name="INPUT", label="Select Line layer", types= [QgsProcessing.TypeVectorLine])
@alg.input(type=alg.NUMBER, name="WIDTH", label="Enter Rectangle Width", default=QgsSettings().value("algRectAlongLines/WIDTH"))
@alg.input(type=alg.NUMBER, name="HEIGHT", label="Enter Rectangle Height", default=QgsSettings().value("algRectAlongLines/HEIGHT"))
@alg.input(type=alg.NUMBER, name="OVERLAP", label="Enter Rectangle Overlap [%]", default=QgsSettings().value("algRectAlongLines/OVERLAP"))
@alg.input(type=alg.NUMBER, name="TOLERANCE", label="Enter Tolerance for Line Simplification", default=QgsSettings().value("algRectAlongLines/TOLERANCE",0))
@alg.input(type=alg.BOOL, name="ALIGN", label="Align Rectangles with Line Features", default=QgsSettings().value("algRectAlongLines/ALIGN",True))
@alg.input(type=alg.SINK, name="OUTPUT", label="Output layer")
def algRectAlongLines(self, parameters, context, feedback, inputs):
"""
This algorithm creates a polygon layer with rectangles along the features of a line layer.
We can adjust the width and height of the rectangles, define an overlap factor and set a tolerance value if we want to simplify the line geometries.
With the "Align Rectangles with Line Features" check box, we can align all rectangles to the line geometries.
Each rectangle created contains the ID of the source line feature, the rotation angle and a sort value.
In addition, the algorithm saves the input parameters in global QGIS variables for repeated use.
"""
def setDefaultValue(dict, param, val):
s = QgsSettings()
s.setValue("algRectAlongLines/"+param, val)
dict[param].setGuiDefaultValueOverride(val)
return dict[param]
width = self.parameterAsDouble(parameters, 'WIDTH', context)
height = self.parameterAsDouble(parameters, 'HEIGHT', context)
overlap = self.parameterAsDouble(parameters, 'OVERLAP', context)
if overlap > 0:
overlap = overlap / 100.0
tolerance = self.parameterAsDouble(parameters, 'TOLERANCE', context)
align = self.parameterAsBool(parameters, 'ALIGN', context)
sortOrder = None
i = 1
# refresh the inputs property of the AlgWrapper instance to update the default parameters
inp = self.inputs
newInputs = { 'WIDTH': setDefaultValue(inp, 'WIDTH', width),
'HEIGHT': setDefaultValue(inp, 'HEIGHT', height),
'OVERLAP': setDefaultValue(inp, 'OVERLAP', overlap * 100.0),
'TOLERANCE': setDefaultValue(inp, 'TOLERANCE', tolerance),
'ALIGN': setDefaultValue(inp, 'ALIGN', align) }
inp.update(newInputs)
source = self.parameterAsSource(parameters, 'INPUT', context)
fields = QgsFields()
fields.append(QgsField('src_fid', QVariant.Int))
fields.append(QgsField('angle', QVariant.Double))
fields.append(QgsField('sort_order', QVariant.Int))
(sink, dest_id) = self.parameterAsSink(parameters, 'OUTPUT',
context, fields, QgsWkbTypes.Polygon , source.sourceCrs())
features = source.getFeatures()
for feature in features:
if feature.hasGeometry():
geom = feature.geometry().extendLine(width*0.1,width*0.1)
if tolerance != 0:
geom = geom.simplify(tolerance)
curs = 0
numpages = geom.length()/width
if numpages > 1:
step = 1.0/numpages
stepnudge = (1.0-overlap) * step
else:
step = 1
stepnudge = 1
currangle = 0
while curs < 1:
startpoint = geom.interpolate(curs*geom.length())
endpoint = geom.interpolate((curs+step)*geom.length())
if not endpoint:
endpoint = geom.interpolate(geom.length())
x_start = startpoint.asPoint().x()
y_start = startpoint.asPoint().y()
x_end = endpoint.asPoint().x()
y_end = endpoint.asPoint().y()
x_mid = (x_start + x_end)/2
y_mid = (y_start + y_end)/2
w2 = width/2.0
h2 = height/2.0
minx = -w2
miny = -h2
maxx = w2
maxy = h2
poly = QgsGeometry().fromWkt(f'POLYGON(({minx} {miny}, {minx} {maxy},{maxx} {maxy}, {maxx} {miny}, {minx} {miny}))')
feat = QgsFeature()
feat.setFields(fields)
if align:
azimuth = startpoint.asPoint().azimuth(endpoint.asPoint())
currangle = (startpoint.asPoint().azimuth(endpoint.asPoint())+270)%360
poly.rotate(currangle, QgsPointXY(0,0))
feat['angle'] = currangle
else:
feat['angle'] = 0
#poly.translate(-width*overlap,0)
poly.translate(x_mid, y_mid)
poly.asPolygon()
curs = curs + stepnudge
feat['src_fid'] = feature.id()
feat['sort_order'] = i
feat.setGeometry(poly)
sink.addFeature(feat, QgsFeatureSink.FastInsert)
i += 1
results = {}
results['OUTPUT'] = dest_id
return results
This algorithm creates a polygon layer with rectangles along the features of a line layer. It demonstrates the @alg decorator and shows how all input parameters can be stored as new default values.
Due to the use of the @alg decorator, this algorithm is only supported by QGIS >= 3.6.
Great! Thank you!
Reviewed by gabrieldeluca 2 weeks, 5 days ago
This Processing Script is made available under the CC-0 license.