Propongo un approccio che ricorre solo a un generatore di geometria e una funzione personalizzata.
Prima di iniziare, voglio sottolineare che focalizzerò l'attenzione sulla spiegazione delle cose minime da fare per riprodurre il risultato desiderato: questo significa che alcuni altri parametri minori (come dimensioni, larghezze e così via) dovrebbero essere facilmente regolati da te per adattarsi meglio alle tue esigenze.
Pertanto, questa soluzione funziona sia per i sistemi geografici sia per i sistemi di riferimento proiettati: di seguito, ho assunto di utilizzare un CRS proiettato (ovvero le unità di misura sono metri), ma è possibile modificarle in base al proprio CRS.
Contesto
Supponiamo di iniziare da questo livello di vettore lineare che rappresenta i fili (le etichette rappresentano il numero di fili sovrapposti (coincidenti)):
Soluzione
Innanzitutto, vai a Layer Properties | Style
e quindi scegli il Single symbol
renderer.
Dalla Symbol selector
finestra di dialogo, scegliere un Geometry generator
tipo di livello come simbolo e Linestring / MultiLinestring
come tipo di geometria. Quindi, fai clic sulla Function Editor
scheda:
Quindi, fai clic su New file
e digita draw_wires
come nome della nuova funzione:
Vedrai che una nuova funzione è stata creata ed è elencata sul lato sinistro della finestra di dialogo. Ora, fai clic sul nome della funzione e sostituisci il valore predefinito @qgsfunction
con il seguente codice (non dimenticare di aggiungere tutte le librerie allegate qui):
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
Una volta fatto questo, fai clic sul Load
pulsante e sarai in grado di vedere la funzione dal Custom
menu della Expression
finestra di dialogo.
Ora, digita questa espressione (vedi l'immagine sotto come riferimento):
draw_wires(40, 0.3, $currentfeature, @layer_name)
Hai appena eseguito una funzione che sta dicendo, in modo immaginario:
"Per il layer corrente ( @layer_name ) e la funzione corrente ( $ currentfeature ), mostra i fili insieme usando un'apertura massima iniziale di 40 gradi e con un cambio di direzione a una distanza di 0,3 volte la lunghezza del segmento corrente."
L'unica cosa che devi cambiare è il valore dei primi due parametri come vuoi, ma ovviamente in modo ragionevole (lascia gli altri parametri di funzione come previsto).
Infine, fai clic sul Apply
pulsante per applicare le modifiche.
Vedrai qualcosa del genere:
come previsto.
MODIFICARE
Secondo una specifica richiesta sollevata dal PO in un commento:
"Sarebbe possibile creare questo modello solo tra l'inizio e la fine di ogni polilinea anziché tra ciascun vertice?"
Ho leggermente modificato il codice. La seguente funzione dovrebbe restituire il risultato atteso:
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