Зсув відображення ліній, що перекриваються в QGIS?


10

Коли точки перекриваються, є ця властивість, яка дозволяє автоматично відображати їх лот окремо там, де вони знаходяться, і називається "Зсув точки". Але це не працює для ліній, навіть тому мені здається цілком концептуально можливим, щоб досягти чогось такого:

введіть тут опис зображення

Мені абсолютно потрібно бачити різницькі лінії, які насправді знаходяться в одному місці (я працюю в телекомунікаційних мережах). Єдиний спосіб, який я зараз бачу, - це дійсно створювати різні лінії, як на малюнку вище, таким чином створюючи просторові помилки.

Я використовую QGIS 2.14.


Я думаю, що щось могло б зробити, що повторюється у стилях. Чи є лінія посередині початковою лінією? Потім я бачу, що ви створили один з інших рядків, використовуючи три різні геометрії, тож моє запитання, чи є якісь додаткові правила їх надання?
мгрі

@mgri Я не впевнений, що розумію ваше запитання. Надана картина - це приклад, в якому я намалював п’ять різних ліній заради демонстрації. Насправді було б більше, що ці 5 ліній насправді знаходяться на місці середнього (вони є дротами, тому всі застрягли в одній оболонці).
GuiOm Clair

1
Ви також можете відображати лінії зі зміщенням ("зміщенням"), але вони не зустрічаються у початковій та кінцевій точках.
AndreJ

@AndreJ Так, і ще одна проблема полягає в тому, що це була б цілком ручна робота, коли мені знадобиться щось більш автоматичне, оскільки це використовувалося б багатьма користувачами.
GuiOm Clair

1
@GuiOmClair Після доданого зображення я припускаю, що ви починаєте з одного рядка, який перекриває (наприклад) чотири інші лінії, і що вам потрібно знайти спосіб їх відображення окремо, навіть якщо вони перетинаються. Я щойно сказав, що можна відтворити те, що відображається на зображенні, що додається, без необхідності створення нових геометрій (але лише повторюваних властивостей стилю початкового шару). Ще один спосіб буде запропонований AndreJ, але, здається, він не відповідає вашим потребам.
мгрі

Відповіді:


12

Я пропоную підхід, який стосується лише генератора геометрії та спеціальної функції.

Перш ніж почати, я хочу підкреслити, що зосередитиму увагу на поясненні мінімальних речей для відтворення бажаного результату: це означає, що деякі інші незначні параметри (наприклад, розміри, ширина тощо) повинні легко регулюватися вами для кращого задоволення ваших потреб.

Тому це рішення працює як для географічних, так і для проектованих довідкових систем: у наступному я припускав використовувати проектований 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

Оце Так! Це вражаюча відповідь! Дуже дякую, що знайшли цей час і поділилися цим часом. Однак: 1. У мене виникають проблеми із застосуванням його до моїх даних (коли я застосовую функцію, рядки зникають), але я думаю, що проблема виникає з моїх даних, оскільки вона працює на тимчасовому шарі, і 2. чи можна було б створити ця закономірність лише між початком і кінцем кожної полілінії замість між кожною вершиною?
GuiOm Clair

@GuiOmClair рядки зникають, оскільки щось не вдається з функцією. Проблема не пов’язана з використанням тимчасового шару, але вона може бути пов’язана з використанням геометрії MultiLine замість геометрії лінії. Будь ласка, завантажте шар у QGIS і введіть ці два рядки на консолі Python: layer=iface.activeLayer()а потім print layer.wkbType(). Клацніть Run: яке значення друкованого номера?
мгрі

Число 5 (що це означає?)
GuiOm Clair

@GuiOmClair Це означає, що ваш шар є шаром MultiLineString, тоді як я припускав, що це шар LineString (оскільки ви його не вказали). Це не буде проблемою, і я правильно редагую код, як тільки можу (можливо, завтра). Крім того, я повинен мати змогу відводити дроти лише між першою та останньою точкою кожної (багатолінійної) лінії.
мгрі

1
Так, особливості - це прямі лінії (оскільки їх, як правило, легше керувати та експортувати), тому краще було б врахувати реальну довжину проводів.
GuiOm Clair
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.