Tôi đề xuất một cách tiếp cận chỉ lặp lại với một trình tạo hình học và một hàm tùy chỉnh.
Trước khi bắt đầu, tôi muốn nhấn mạnh rằng tôi sẽ tập trung chú ý vào phần giải thích những điều tối thiểu cần làm để tái tạo kết quả mong muốn: điều này có nghĩa là một số thông số nhỏ khác (như kích thước, chiều rộng, v.v.) nên được điều chỉnh dễ dàng bởi bạn để phù hợp hơn với nhu cầu của bạn.
Do đó, giải pháp này hoạt động cho cả Hệ thống tham chiếu địa lý và dự kiến: sau đây, tôi giả sử sử dụng CRS dự kiến (nghĩa là đơn vị đo là mét), nhưng bạn có thể thay đổi chúng theo CRS của mình.
Bối cảnh
Giả sử bắt đầu từ lớp vectơ linestring này đại diện cho các dây (nhãn đại diện cho số lượng dây chồng chéo (trùng khớp):
Giải pháp
Đầu tiên, đi đến Layer Properties | Style
và sau đó chọn Single symbol
trình kết xuất.
Từ Symbol selector
hộp thoại, chọn Geometry generator
kiểu lớp biểu tượng và Linestring / MultiLinestring
kiểu hình học. Sau đó, nhấp vào Function Editor
tab:
Sau đó, bấm vào New file
và gõ draw_wires
như tên của chức năng mới:
Bạn sẽ thấy rằng một chức năng mới đã được tạo và nó được liệt kê ở phía bên trái của hộp thoại. Bây giờ, nhấp vào tên của hàm và thay thế mặc định @qgsfunction
bằng mã sau đây (đừng quên thêm tất cả các thư viện được đính kèm tại đây):
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
Một khi bạn đã làm điều này, bấm vào Load
nút và bạn sẽ có thể thấy chức năng từ Custom
Menu của Expression
hộp thoại.
Bây giờ, nhập biểu thức này (xem hình ảnh dưới đây để tham khảo):
draw_wires(40, 0.3, $currentfeature, @layer_name)
Bạn vừa chạy một chức năng đang nói, theo một cách tưởng tượng:
"Đối với lớp hiện tại ( @layer_name ) và tính năng hiện tại ( $ currentfeature ), hiển thị các dây với nhau bằng cách mở tối đa ban đầu 40 độ và thay đổi hướng ở khoảng cách 0,3 lần chiều dài của đoạn hiện tại."
Điều duy nhất bạn cần thay đổi là giá trị của hai tham số đầu tiên mà bạn muốn, nhưng rõ ràng là theo cách hợp lý (để lại các tham số chức năng khác như được cung cấp).
Cuối cùng, bấm vào Apply
nút để áp dụng các thay đổi.
Bạn sẽ thấy một cái gì đó như thế này:
như mong đợi.
BIÊN TẬP
Theo một yêu cầu cụ thể được OP đưa ra trong một bình luận:
"Có thể chỉ tạo mẫu này giữa đầu và cuối của mỗi đa tuyến thay vì giữa mỗi đỉnh không?"
Tôi hơi chỉnh sửa mã. Hàm sau sẽ trả về kết quả mong đợi:
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