Сдвиг отображения перекрывающихся строк в QGIS?

10

Когда точки перекрываются, есть это свойство, которое позволяет автоматически отображать их множество отдельно, где они находятся, и называется «Смещение точек». Но это не работает для линий, даже если это кажется мне вполне концептуально выполнимым для достижения чего-то подобного:

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

Мне абсолютно необходимо увидеть различия линий, которые на самом деле все находятся в одном месте (я работаю в телекоммуникационных сетях). Единственный способ, которым я сейчас покажу, - это действительно создавать разные линии, как на картинке выше, создавая пространственные ошибки.

Я использую QGIS 2.14.

GuiOm Clair
источник
Я думаю, что-то можно сделать, возвращаясь к стилю. Линия в середине - стартовая линия? Затем я вижу, что вы создали каждую из других линий, используя три разные геометрии, поэтому мой вопрос заключается в том, существуют ли какие-то дополнительные правила для их рендеринга?
Мгри
@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)

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

Вы только что запустили функцию, которая воображаемым образом говорит:

«Для текущего слоя ( @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
МГРИ
источник
Вот Это Да! Это впечатляющий ответ! Большое спасибо, что нашли время, чтобы найти и поделиться им. Однако: 1. У меня возникают проблемы с применением его к моим данным (когда я применяю функцию, линии исчезают), но я предполагаю, что проблема связана с моими данными, так как это работает на временном слое и 2. можно ли создать этот шаблон только между началом и концом каждой полилинии, а не между каждой вершины?
GuiOm Clair
@GuiOmClair строки исчезают, потому что что-то идет не так с функцией. Проблема не в использовании временного слоя, но это может быть связано с использованием геометрии MultiLine вместо геометрии Line. Пожалуйста, загрузите слой в QGIS и затем введите эти две строки в консоли Python: layer=iface.activeLayer()и затем print layer.wkbType(). Нажмите Run: какое значение печатного номера?
Мгри
Число 5 (что это значит?)
GuiOm Clair
@GuiOmClair Это означает, что ваш слой является слоем MultiLineString, хотя я предположил, что это слой LineString (поскольку вы его не указали). Это не будет проблемой, и я правильно отредактирую код, как только смогу (возможно, завтра). Кроме того, я должен иметь возможность рендерить провода только между первой и последней точкой каждой (многолинейной) линейной функции.
Мгри
1
Да, функции являются прямыми (так как ими, как правило, легче управлять и экспортировать), поэтому было бы лучше, учитывая реальную длину проводов.
GuiOm Clair