Przesunąć wyświetlanie nakładających się linii w QGIS?

10

Kiedy punkty się pokrywają, istnieje ta właściwość, która pozwala automatycznie wyświetlić wiele z nich osobno wokół ich miejsca, zwana „przesunięciem punktu”. Ale to nie działa dla linii, mimo to wydaje mi się całkiem wykonalne koncepcyjnie, aby osiągnąć coś takiego:

wprowadź opis zdjęcia tutaj

Absolutnie muszę zobaczyć różne linie, które w rzeczywistości wszystkie są w tym samym miejscu (pracuję w sieciach telekomunikacyjnych). Jedyny sposób, jaki widzę na razie, to naprawdę tworzenie różnych linii, jak na powyższym obrazku, co powoduje błędy przestrzenne.

Używam QGIS 2.14.

GuiOm Clair
źródło
Myślę, że można coś zrobić, powtarzając stylizację. Czy linia pośrodku jest linią początkową? Następnie widzę, że utworzyłeś każdą z pozostałych linii za pomocą trzech różnych geometrii, więc moje pytanie brzmi, czy istnieją jakieś dodatkowe reguły dotyczące ich renderowania?
mgri
@mgri Nie jestem pewien, czy rozumiem twoje pytanie. Przedstawiony obraz jest przykładem, w którym narysowałem pięć różnych linii dla celów demonstracyjnych. W rzeczywistości byłoby bardziej, że te 5 linii faktycznie znajduje się w miejscu środkowej linii (są to druty, więc wszystkie utknęły w tej samej osłonie).
GuiOm Clair
1
Można również renderować linie z przesunięciem („przesunięcie”), ale nie spotkałyby się one w punktach początkowym i końcowym.
AndreJ
@AndreJ Tak, a innym problemem byłoby to, że byłaby to dość ręczna obsługa, w której potrzebowałbym czegoś bardziej automatycznego, ponieważ byłby używany przez wielu użytkowników.
GuiOm Clair
1
@GuiOmClair Po dołączeniu obrazu założyłem, że zaczynasz od jednej linii, która nakłada się (na przykład) na cztery inne linie i że musisz znaleźć sposób na ich osobne wyświetlanie, nawet jeśli się pokrywają. Powiedziałem tylko, że można odtworzyć to, co jest wyświetlane na dołączonym obrazie, bez potrzeby tworzenia nowych geometrii (ale tylko powtarzając właściwości stylu warstwy początkowej). Innym sposobem byłby ten zaproponowany przez AndreJ, ale wydaje się, że nie pasuje do twoich potrzeb.
mgri

Odpowiedzi:

12

Proponuję podejście, które powtarza się tylko w przypadku generatora geometrii i funkcji niestandardowej.

Przed rozpoczęciem chcę podkreślić, że skupię uwagę na wyjaśnieniu minimalnych czynności, które należy wykonać, aby odtworzyć pożądany rezultat: oznacza to, że niektóre inne drobne parametry (takie jak rozmiary, szerokości itp.) Powinny być łatwo dostosowane przez ciebie dla lepszego dopasowania do twoich potrzeb.

Dlatego to rozwiązanie działa zarówno dla Geograficznego, jak i Projekcyjnego Systemu Referencyjnego: poniżej założyłem, że użyję rzutowanego CRS (tj. Jednostki miary to metry), ale możesz je zmienić zgodnie ze swoim CRS.


Kontekst

Załóżmy, że zaczniemy od tej wektorowej warstwy linii przedstawiającej przewody (etykiety reprezentują liczbę nakładających się (zbieżnych) drutów):

wprowadź opis zdjęcia tutaj


Rozwiązanie

Najpierw przejdź do, Layer Properties | Stylea następnie wybierz Single symbolrenderer.

W Symbol selectoroknie dialogowym wybierz Geometry generatortyp warstwy symbolu i Linestring / MultiLinestringtyp geometrii. Następnie kliknij Function Editorkartę:

wprowadź opis zdjęcia tutaj

Następnie kliknij New filei wpisz draw_wiresjako nazwę nowej funkcji:

wprowadź opis zdjęcia tutaj

Zobaczysz, że nowa funkcja została utworzona i jest wymieniona po lewej stronie okna dialogowego. Teraz kliknij nazwę funkcji i zastąp domyślną @qgsfunctionnastępującym kodem (nie zapomnij dodać wszystkich dołączonych tutaj bibliotek):

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

Gdy to zrobisz, kliknij Loadprzycisk, a będziesz mógł zobaczyć funkcję z Custommenu Expressionokna dialogowego.

Teraz wpisz to wyrażenie (patrz obrazek poniżej jako odniesienie):

draw_wires(40, 0.3, $currentfeature, @layer_name)

wprowadź opis zdjęcia tutaj

Właśnie uruchomiłeś funkcję, która w wyobrażony sposób mówi:

„W przypadku bieżącej warstwy ( @nazwa_layera ) i bieżącej funkcji ( $ currentfeature ) wyświetl przewody razem, używając początkowego maksymalnego otwarcia 40 stopni i ze zmianą kierunku w odległości 0,3-krotnej długości bieżącego segmentu.”

Jedyną rzeczą, którą musisz zmienić, jest wartość pierwszych dwóch parametrów, jak chcesz, ale oczywiście w rozsądny sposób (pozostaw pozostałe parametry funkcji, jak podano).

Na koniec kliknij Applyprzycisk, aby zastosować zmiany.

Zobaczysz coś takiego:

wprowadź opis zdjęcia tutaj

zgodnie z oczekiwaniami.


EDYTOWAĆ

Zgodnie ze szczególnym żądaniem zgłoszonym przez PO w komentarzu:

„Czy byłoby możliwe utworzenie tego wzoru tylko między początkiem i końcem każdej polilinii zamiast między każdym wierzchołkiem?”

Lekko zredagowałem kod. Następująca funkcja powinna zwrócić oczekiwany wynik:

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
mgri
źródło
Łał! To imponująca odpowiedź! Dziękujemy bardzo za poświęcenie tego czasu na jego znalezienie i udostępnienie. Jednak: 1. Mam problem ze stosowaniem go do moich danych (kiedy stosuję funkcję, linie znikają), ale wydaje mi się, że problem pochodzi z moich danych, ponieważ działa na warstwie tymczasowej i 2. czy można byłoby utworzyć ten wzór tylko między początkiem a końcem każdej polilinii zamiast między każdym wierzchołkiem?
GuiOm Clair,
@GuiOmClair linie znikają, ponieważ coś działa nie tak z funkcją. Problem nie wynika z zastosowania warstwy tymczasowej, ale może być związany z użyciem geometrii MultiLine zamiast geometrii Line. Załaduj warstwę w QGIS, a następnie wpisz te dwie linie w Python Console: layer=iface.activeLayer()a następnie print layer.wkbType(). Kliknij Run: jaka jest wartość wydrukowanego numeru?
mgri
Liczba to 5 (co to znaczy?)
GuiOm Clair
@GuiOmClair Oznacza to, że twoja warstwa jest warstwą MultiLineString, podczas gdy założyłem, że była to warstwa LineString (ponieważ jej nie określiłeś). To nie będzie problem i odpowiednio zmodyfikuję kod jak najszybciej (może jutro). Ponadto powinienem być w stanie renderować przewody tylko między pierwszym a ostatnim punktem każdej (wielu) linii.
mgri
1
Tak, funkcje są liniami prostymi (ponieważ ogólnie łatwiej nimi zarządzać i eksportować), więc lepiej byłoby wziąć pod uwagę rzeczywistą długość drutów.
GuiOm Clair