Я предлагаю подход, который повторяется только к генератору геометрии и пользовательской функции.
Прежде чем начать, я хочу подчеркнуть, что я сосредоточу внимание на объяснении минимальных действий, которые необходимо выполнить для воспроизведения желаемого результата: это означает, что некоторые другие второстепенные параметры (такие как размеры, ширина и т. Д.) Должны быть легко настроены вами для лучшего соответствия вашим потребностям.
Следовательно, это решение работает как для географических, так и для проектируемых эталонных систем: в дальнейшем я предполагал использовать прогнозируемый 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)
Вы только что запустили функцию, которая воображаемым образом говорит:
«Для текущего слоя ( @layer_name ) и текущего объекта ( $ currentfeature ) отобразите провода вместе, используя начальное максимальное отверстие 40 градусов и с изменением направления на расстоянии, в 0,3 раза превышающем длину текущего сегмента».
Единственное, что вам нужно изменить - это значение первых двух параметров, как вы хотите, но, очевидно, разумным образом (оставьте остальные параметры функции как есть).
Наконец, нажмите на Apply
кнопку для применения изменений.
Вы увидите что-то вроде этого:
как и ожидалось.
РЕДАКТИРОВАТЬ
Согласно конкретной просьбе, поднятой ФП в комментарии:
«Будет ли возможно создать этот шаблон только между началом и концом каждой полилинии, а не между каждой вершиной?»
Я слегка отредактировал код. Следующая функция должна вернуть ожидаемый результат:
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