Czy chcesz stylizować określoną warstwę za pomocą maski wielokąta w QGIS?

10

Mam warstwę liniową i warstwę wielokąta w QGIS:

Przed maską

Chciałbym stylizować część warstwy linii poza wielokątem za pomocą jednego stylu, a część wewnątrz za pomocą innego stylu:

Po masce

Nie chcę tworzyć pochodnego zestawu danych, np. przytnij warstwę i stylizuj dwie części.

Jest to prosty przypadek, ale w moim projekcie QGIS mam +30 warstw, więc myślę, że jakiekolwiek mieszanie warstw zakłóciłoby warstwy leżące poniżej.

Czy można zrobić coś takiego?

Nie chcę pokazywać warstwy wielokąta, tutaj jest tylko wizualizacja tego, co chciałbym zrobić.

Chau
źródło
1
Dobra metoda! Myślę, że powinno to zostać opublikowane jako odpowiedź zamiast edycji pytania :)
Joseph
@Joseph, gotowe!
Chau

Odpowiedzi:

11

Nie jest to idealne rozwiązanie, ale możesz skorzystać z Generatora Geometrii, który dodaje wizualizowaną linię do reprezentacji skrzyżowania. Następnie można ustawić tę opcję, aby nakładała się na pierwotny element linii.

Dodaj nową warstwę symbolu, klikając znak plus i wybierz Geometry generatortyp warstwy symbolu jako. Ustaw typ geoemtry LineString / MultiLineStringi użyj następującego wyrażenia:

intersection($geometry, geometry(get_feature( 'polygonLayer','fieldName','value'))) 

Musisz dodać szczegółowe informacje na temat określonego wielokąta, w którym:

  • polygonLayer to nazwa twojej warstwy wielokątów
  • fieldName to nazwa pola
  • value jest wartością funkcji określonego wielokąta

Właściwości stylu

Pamiętaj, że aby pokolorować linię wizualną, może być konieczne wykonanie tej czynności we właściwościach Efekty rysowania :

Narysuj właściwości efektów

To był wynik (zauważ, że linia wizualna nie pokrywała się całkowicie z linią oryginalną, więc nieznacznie zmodyfikowałem przesunięcie):

Wynik

I bez wielokąta:

Wynik bez wielokąta



Edytować:

Jeśli chcesz to zastosować dla każdego elementu linii przecinającego element wielokąta, przejdź do Edytora funkcji i użyj następującej funkcji (zmień nazwę, polygon example_2aby pasowała do nazwy warstwy wielokąta):

from qgis.core import *
from qgis.gui import *

@qgsfunction(args='auto', group='Custom')
def func(feature, parent):
    polygon_layer = QgsMapLayerRegistry.instance().mapLayersByName( "polygon example_2" )[0]
    feat_list = []
    geoms = QgsGeometry.fromWkt('GEOMETRYCOLLECTION()')
    for polygon_feat in polygon_layer.getFeatures():
        if feature.geometry().intersects(polygon_feat.geometry()):
            intersection = feature.geometry().intersection(polygon_feat.geometry())
            feat_list.append(intersection)
    for x in feat_list:
        geoms = geoms.combine(x)
    return geoms

Edytor funkcji

Kliknij Załaduj, a następnie przejdź do karty Wyrażenie i wpisz func(). Mam nadzieję, że wynik powinien wyglądać następująco (przy użyciu tych samych właściwości stylu wspomnianych powyżej):

Ostateczny wynik

Joseph
źródło
Właściwie to na to spojrzałem, ale przestałem, gdy odkryłem, że get_featurewymaga to nazwy i wartości pola. Mam tylko warstwę wielokąta i chciałbym użyć wszystkich funkcji na tej warstwie, aby się maskować. Czy to jest możliwe?
Chau,
@Chau - Edytowany post, aby uwzględnić możliwą metodę :)
Joseph
1
Inną opcją byłoby rozpuszczenie warstwy wielokąta.
csk
1
@Joseph - Czy przy użyciu a wywoływana Geometry Generatorjest metoda funcdla każdej funkcji na warstwie, w której jest używana do stylizacji? Więc jeśli moja warstwa linii ma 3 funkcje, functo nazywa się 3 razy i rysuje ten sam wynik 3 razy?
Chau,
1
@Chau - Myślę, że miałeś rację, kod wielokrotnie powtarzał się w każdej funkcji. Zredagowałem post, funcaby teraz mógł być wywoływany tylko dla każdego elementu linii i narysuje wynik tylko raz (co wydaje się, że tak jest, jak pokazują znaczniki wierzchołków wewnątrz wielokątów, zanim ukryto je pod tym, co przegapiłem). Dzięki za zwrócenie na to uwagi :)
Joseph
3

Rozszerzając odpowiedź Josepha , wymyśliłem tę funkcję. Uwzględnia różne układy współrzędnych i musiałem wyszukać dwie warstwy maskujące, więc to też obsługuje. Co więcej, chciałem móc maskować linie wewnątrz wielokątów lub linie na zewnątrz wielokątów.

from qgis.core import *
from qgis.gui import *
from qgis.utils import iface

@qgsfunction(args='auto', group='Custom')
def mask_line_with_polygon(mask_type, line_layer_name, polygon_layer_name_1, polygon_layer_name_2, feature, parent):
    line_layer = QgsMapLayerRegistry.instance().mapLayersByName( line_layer_name )[0]

    # This is the geometry outside the polygon mask.
    outside = QgsGeometry(feature.geometry())

    polygon_layer_names = [polygon_layer_name_1, polygon_layer_name_2]
    line_feature_extent = outside.boundingBox()

    geoms = QgsGeometry.fromWkt('MultiLineString()')

    for polygon_layer_name in polygon_layer_names:
        if polygon_layer_name is None or len(polygon_layer_name) == 0:
            continue

        # If the line and the polygon layers have different projections, handle them here.
        polygon_layer = QgsMapLayerRegistry.instance().mapLayersByName(polygon_layer_name)[0]
        trs = QgsCoordinateTransform(line_layer.crs(), polygon_layer.crs())
        polygon_extent = trs.transform(line_feature_extent)
        trs = QgsCoordinateTransform(polygon_layer.crs(), line_layer.crs())

        # Go through the features in the polygon layer, but only those within the line feature bounding box.
        for feature in polygon_layer.getFeatures(QgsFeatureRequest().setFilterRect(polygon_extent)):
            polygon_geometry = QgsGeometry(feature.geometry())

            # Transform the polygon to line space.
            polygon_geometry.transform(trs)

            if outside.intersects(polygon_geometry):
                if mask_type.lower() == 'outside':
                    inside = outside.intersection(polygon_geometry)

                    if inside.isMultipart():
                        for x in inside.asMultiPolyline():
                            geoms.addPart(x)
                    else:
                        geoms.addPart(inside.asPolyline())

                outside = outside.difference(polygon_geometry)

    if mask_type.lower() == 'inside':
        if outside.isMultipart():
            for x in outside.asMultiPolyline():
                geoms.addPart(x)
        else:
            geoms.addPart(outside.asPolyline())

    return geoms

To ćwiczenie pokazało mi, że QGIS nie przepada za pracą z dużymi zestawami danych, a ten algorytm z QGIS zbyt często ulega awarii. Podejrzewam, że moduł renderujący QGIS nie lubi renderować generatorów geometrii, które są czasochłonne.

Chau
źródło