Proponuję podejście, które powtarza się tylko w przypadku generatora geometrii i funkcji niestandardowej.
Przed rozpoczęciem chcę podkreślić, że skupię uwagę na wyjaśnieniu minimalnych czynności, które należy wykonać, aby odtworzyć pożądany rezultat: oznacza to, że niektóre inne drobne parametry (takie jak rozmiary, szerokości itp.) Powinny być łatwo dostosowane przez ciebie dla lepszego dopasowania do twoich potrzeb.
Dlatego to rozwiązanie działa zarówno dla Geograficznego, jak i Projekcyjnego Systemu Referencyjnego: poniżej założyłem, że użyję rzutowanego CRS (tj. Jednostki miary to metry), ale możesz je zmienić zgodnie ze swoim CRS.
Kontekst
Załóżmy, że zaczniemy od tej wektorowej warstwy linii przedstawiającej przewody (etykiety reprezentują liczbę nakładających się (zbieżnych) drutów):
Rozwiązanie
Najpierw przejdź do, Layer Properties | Style
a następnie wybierz Single symbol
renderer.
W Symbol selector
oknie dialogowym wybierz Geometry generator
typ warstwy symbolu i Linestring / MultiLinestring
typ geometrii. Następnie kliknij Function Editor
kartę:
Następnie kliknij New file
i wpisz draw_wires
jako nazwę nowej funkcji:
Zobaczysz, że nowa funkcja została utworzona i jest wymieniona po lewej stronie okna dialogowego. Teraz kliknij nazwę funkcji i zastąp domyślną @qgsfunction
następującym kodem (nie zapomnij dodać wszystkich dołączonych tutaj bibliotek):
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
Gdy to zrobisz, kliknij Load
przycisk, a będziesz mógł zobaczyć funkcję z Custom
menu Expression
okna dialogowego.
Teraz wpisz to wyrażenie (patrz obrazek poniżej jako odniesienie):
draw_wires(40, 0.3, $currentfeature, @layer_name)
Właśnie uruchomiłeś funkcję, która w wyobrażony sposób mówi:
„W przypadku bieżącej warstwy ( @nazwa_layera ) i bieżącej funkcji ( $ currentfeature ) wyświetl przewody razem, używając początkowego maksymalnego otwarcia 40 stopni i ze zmianą kierunku w odległości 0,3-krotnej długości bieżącego segmentu.”
Jedyną rzeczą, którą musisz zmienić, jest wartość pierwszych dwóch parametrów, jak chcesz, ale oczywiście w rozsądny sposób (pozostaw pozostałe parametry funkcji, jak podano).
Na koniec kliknij Apply
przycisk, aby zastosować zmiany.
Zobaczysz coś takiego:
zgodnie z oczekiwaniami.
EDYTOWAĆ
Zgodnie ze szczególnym żądaniem zgłoszonym przez PO w komentarzu:
„Czy byłoby możliwe utworzenie tego wzoru tylko między początkiem i końcem każdej polilinii zamiast między każdym wierzchołkiem?”
Lekko zredagowałem kod. Następująca funkcja powinna zwrócić oczekiwany wynik:
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