Я пропоную підхід, який стосується лише генератора геометрії та спеціальної функції.
Перш ніж почати, я хочу підкреслити, що зосередитиму увагу на поясненні мінімальних речей для відтворення бажаного результату: це означає, що деякі інші незначні параметри (наприклад, розміри, ширина тощо) повинні легко регулюватися вами для кращого задоволення ваших потреб.
Тому це рішення працює як для географічних, так і для проектованих довідкових систем: у наступному я припускав використовувати проектований CRS (тобто одиниці вимірювань є лічильниками), але ви можете змінити їх відповідно до вашої CRS.
Контекст
Припустимо, почнемо з цього шару векторного рядкового рядка, що представляє дроти (мітки представляють кількість проводів, що перекриваються (збігаються)):
Рішення
По-перше, перейдіть, Layer Properties | Style
а потім виберіть Single symbol
рендерінга.
У Symbol selector
діалоговому вікні виберіть Geometry generator
тип типу символу та Linestring / MultiLinestring
тип геометрії. Потім натисніть на Function Editor
вкладку:
Потім натисніть New file
і введіть draw_wires
як назву нової функції:
Ви побачите, що створена нова функція, і вона вказана в лівій частині діалогового вікна. Тепер натисніть на назву функції та замініть за замовчуванням @qgsfunction
наступний код (не забудьте додати тут усі бібліотеки, що додаються):
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
Після цього натисніть Load
кнопку, і ви зможете побачити функцію з Custom
меню Expression
діалогового вікна.
Тепер введіть цей вираз (див. Зображення нижче як посилання):
draw_wires(40, 0.3, $currentfeature, @layer_name)
Ви просто запустили функцію, яка говорить уявним чином:
"Для поточного шару ( @ ім'я_користувача ) та поточної функції ( $ currentfeature ) відображайте дроти разом, використовуючи початкове максимальне відкриття в 40 градусів і зі зміною напрямку на відстань в 0,3 рази більше довжини поточного сегмента."
Єдине, що вам потрібно змінити - це значення перших двох параметрів, як ви хочете, але, очевидно, розумним способом (залиште інші параметри функції, як передбачено).
Нарешті, натисніть на Apply
кнопку для застосування змін.
Ви побачите щось подібне:
як і очікувалося.
EDIT
Відповідно до конкретного запиту, поданого ОП у коментарі:
"Чи можна було б створити цей шаблон лише між початком і кінцем кожної полілінії, а не між кожною вершиною?"
Я трохи відредагував код. Наступна функція повинна повернути очікуваний результат:
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