Ich schlage einen Ansatz vor, der nur auf einen Geometriegenerator und eine benutzerdefinierte Funktion zurückgreift.
Bevor ich anfange, möchte ich unterstreichen, dass ich die Aufmerksamkeit auf die Erklärung der minimalen Dinge richten werde, die zur Wiedergabe des gewünschten Ergebnisses zu tun sind: Dies bedeutet, dass einige andere kleinere Parameter (wie Größen, Breiten usw.) von Ihnen leicht angepasst werden sollten für eine bessere Anpassung an Ihre Bedürfnisse.
Daher funktioniert diese Lösung sowohl für geografische als auch für projizierte Referenzsysteme: Im Folgenden habe ich angenommen, dass ein projiziertes CRS verwendet wird (dh Maßeinheiten sind Meter), aber Sie können sie entsprechend Ihrem CRS ändern.
Kontext
Nehmen wir an, wir beginnen mit dieser linearen Vektorebene, die die Drähte darstellt (die Beschriftungen geben die Anzahl der überlappenden (zusammenfallenden) Drähte an):
Lösung
Gehen Sie zuerst zu Layer Properties | Style
und wählen Sie dann den Single symbol
Renderer aus.
Symbol selector
Wählen Sie im Dialogfeld einen Geometry generator
Typ Linestring / MultiLinestring
als Symbolebene und einen Geometrietyp aus. Klicken Sie dann auf die Function Editor
Registerkarte:
Klicken Sie dann auf New file
und geben Sie draw_wires
den Namen der neuen Funktion ein:
Sie werden sehen, dass eine neue Funktion erstellt wurde und diese auf der linken Seite des Dialogfelds aufgeführt ist. Klicken Sie nun auf den Namen der Funktion und ersetzen Sie die Standardeinstellung @qgsfunction
durch den folgenden Code (vergessen Sie nicht, alle hier angehängten Bibliotheken hinzuzufügen):
from qgis.core import *
from qgis.gui import *
from math import sin, cos, radians
@qgsfunction(args='auto', group='Custom')
def draw_wires(angle, percentage, curr_feat, layer_name, feature, parent):
def wires(polyline, new_angle, percentage):
for x in range(0, len(polyline)-1):
vertices = []
first_point = polyline[x]
second_point = polyline[x +1]
seg = QgsGeometry.fromPolyline([first_point, second_point])
len_feat = seg.length()
frac_len = percentage * len_feat
limb = frac_len/cos(radians(new_angle))
tmp_azim = first_point.azimuth(second_point)
angle_1 = radians(90 - (tmp_azim+new_angle))
dist_x, dist_y = (limb * cos(angle_1), limb * sin(angle_1))
point_1 = QgsPoint(first_point[0] + dist_x, first_point[1] + dist_y)
angle_2 = radians(90 - (tmp_azim-new_angle))
dist_x, dist_y = (limb * cos(angle_2), limb * sin(angle_2))
point_2 = QgsPoint(second_point[0] - dist_x, second_point[1] - dist_y)
tmp_azim = second_point.azimuth(first_point)
angle_3 = radians(90 - (tmp_azim+new_angle))
dist_x, dist_y = (limb * cos(angle_3), limb * sin(angle_3))
point_3 = QgsPoint(second_point[0] + dist_x, second_point[1] + dist_y)
angle_4 = radians(90 - (tmp_azim-new_angle))
dist_x, dist_y = (limb * cos(angle_4), limb * sin(angle_4))
point_4 = QgsPoint(first_point[0] - dist_x, first_point[1] - dist_y)
vertices.extend([first_point, point_1, point_2, second_point, point_3, point_4, first_point])
tempGeom = QgsGeometry.fromPolyline(vertices)
num.append(tempGeom)
return num
layer = QgsMapLayerRegistry.instance().mapLayersByName(layer_name)[0]
all_feats = {}
index = QgsSpatialIndex()
for ft in layer.getFeatures():
index.insertFeature(ft)
all_feats[ft.id()] = ft
first = True
tmp_geom = curr_feat.geometry()
polyline = tmp_geom.asPolyline()
idsList = index.intersects(tmp_geom.boundingBox())
occurrences = 0
for id in idsList:
test_feat = all_feats[id]
test_geom = test_feat.geometry()
if tmp_geom.equals(test_geom):
occurrences += 1
if occurrences & 0x1:
num = [tmp_geom]
else:
num = []
rapp = occurrences/2
i=2
new_angle = angle
while i <= occurrences:
draw=wires(polyline, new_angle, percentage)
i += 2
new_angle -= new_angle/rapp
first = True
for h in num:
if first:
geom = QgsGeometry(h)
first = False
else:
geom = geom.combine(h)
return geom
Wenn Sie dies getan haben, klicken Sie auf die Load
Schaltfläche und Sie können die Funktion im Custom
Menü des Expression
Dialogfelds sehen.
Geben Sie nun diesen Ausdruck ein (siehe Abbildung unten als Referenz):
draw_wires(40, 0.3, $currentfeature, @layer_name)
Sie haben gerade eine Funktion ausgeführt, die auf imaginäre Weise sagt:
" Zeigen Sie für die aktuelle Ebene ( @layer_name ) und das aktuelle Feature ( $ currentfeature ) die Drähte zusammen mit einer anfänglichen maximalen Öffnung von 40 Grad und einer Richtungsänderung in einem Abstand von 0,3 mal der Länge des aktuellen Segments an."
Das einzige, was Sie ändern müssen, ist der Wert der ersten beiden Parameter, wie Sie möchten, aber offensichtlich in angemessener Weise (lassen Sie die anderen Funktionsparameter wie angegeben).
Klicken Sie abschließend auf die Apply
Schaltfläche, um die Änderungen zu übernehmen.
Sie werden so etwas sehen:
wie erwartet.
BEARBEITEN
Auf eine spezielle Anfrage des OP in einem Kommentar hin:
"Wäre es möglich, dieses Muster nur zwischen dem Anfang und dem Ende jeder Polylinie anstatt zwischen jedem Scheitelpunkt zu erstellen?"
Ich habe den Code leicht bearbeitet. Die folgende Funktion sollte das erwartete Ergebnis zurückgeben:
from qgis.core import *
from qgis.gui import *
from math import sin, cos, radians
@qgsfunction(args='auto', group='Custom')
def draw_wires(angle, percentage, curr_feat, layer_name, feature, parent):
def wires(polyline, new_angle, percentage):
vertices = []
len_feat = polyline.length()
frac_len = percentage * len_feat
limb = frac_len/cos(radians(new_angle))
tmp_azim = first_point.azimuth(second_point)
angle_1 = radians(90 - (tmp_azim+new_angle))
dist_x, dist_y = (limb * cos(angle_1), limb * sin(angle_1))
point_1 = QgsPoint(first_point[0] + dist_x, first_point[1] + dist_y)
angle_2 = radians(90 - (tmp_azim-new_angle))
dist_x, dist_y = (limb * cos(angle_2), limb * sin(angle_2))
point_2 = QgsPoint(second_point[0] - dist_x, second_point[1] - dist_y)
tmp_azim = second_point.azimuth(first_point)
angle_3 = radians(90 - (tmp_azim+new_angle))
dist_x, dist_y = (limb * cos(angle_3), limb * sin(angle_3))
point_3 = QgsPoint(second_point[0] + dist_x, second_point[1] + dist_y)
angle_4 = radians(90 - (tmp_azim-new_angle))
dist_x, dist_y = (limb * cos(angle_4), limb * sin(angle_4))
point_4 = QgsPoint(first_point[0] - dist_x, first_point[1] - dist_y)
vertices.extend([first_point, point_1, point_2, second_point, point_3, point_4, first_point])
tempGeom = QgsGeometry.fromPolyline(vertices)
num.append(tempGeom)
layer = QgsMapLayerRegistry.instance().mapLayersByName(layer_name)[0]
all_feats = {}
index = QgsSpatialIndex()
for ft in layer.getFeatures():
index.insertFeature(ft)
all_feats[ft.id()] = ft
first = True
tmp_geom = curr_feat.geometry()
coords = tmp_geom.asMultiPolyline()
if coords:
new_coords = [QgsPoint(x, y) for x, y in z for z in coords]
else:
coords = tmp_geom.asPolyline()
new_coords = [QgsPoint(x, y) for x, y in coords]
first_point = new_coords[0]
second_point = new_coords[-1]
polyline=QgsGeometry.fromPolyline([first_point, second_point])
idsList = index.intersects(tmp_geom.boundingBox())
occurrences = 0
for id in idsList:
test_feat = all_feats[id]
test_geom = test_feat.geometry()
if tmp_geom.equals(test_geom):
occurrences += 1
if occurrences & 0x1:
num = [polyline]
else:
num = []
rapp = occurrences/2
i=2
new_angle = angle
while i <= occurrences:
draw=wires(polyline, new_angle, percentage)
i += 2
new_angle -= new_angle/rapp
first = True
for h in num:
if first:
geom = QgsGeometry(h)
first = False
else:
geom = geom.combine(h)
return geom