Styliser une couche spécifique en utilisant un masque polygonal dans QGIS?

10

J'ai une couche de lignes et une couche de polygones dans QGIS:

Avant le masque

Je voudrais styliser la partie du calque de ligne à l'extérieur du polygone en utilisant un style et la partie à l'intérieur en utilisant un style différent:

Après le masque

Je ne veux pas créer un ensemble de données dérivées, ex. découpez le calque de ligne et stylisez les deux parties.

C'est un cas simple mais dans mon projet QGIS j'ai +30 couches donc je pense que tout mélange de couches perturberait les couches sous-jacentes.

Est-il possible de faire quelque chose comme ça?

Je ne veux pas montrer la couche polygonale, c'est juste ici pour visualiser ce que j'aimerais faire.

Chau
la source
1
Belle méthode! Je pense que cela devrait être affiché comme une réponse au lieu d'une modification de la question :)
Joseph
@Joseph, c'est fait!
Chau

Réponses:

11

Pas une solution parfaite, mais vous pouvez utiliser le générateur de géométrie qui ajoute une ligne visualisée pour représenter l'intersection. Vous pouvez ensuite définir cela pour chevaucher l'entité linéaire d'origine.

Ajoutez une nouvelle couche de symboles en cliquant sur le signe plus et sélectionnez le Geometry generatortype de couche de symboles. Définissez le type geoemtry sur LineString / MultiLineStringet utilisez l'expression suivante:

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

Vous devrez ajouter des détails sur votre polygone spécifique où:

  • polygonLayer est le nom de votre couche polygonale
  • fieldName est le nom du champ
  • value est la valeur d'entité de votre polygone spécifique

Propriétés de style

Notez que pour colorer la ligne visuelle, vous devrez peut-être le faire à partir de la propriété d' effets Draw :

Propriétés des effets de dessin

C'était le résultat (notez que la ligne visuelle ne chevauchait pas complètement la ligne d'origine, j'ai donc légèrement modifié le décalage):

Résultat

Et sans le polygone:

Résultat sans polygone



Éditer:

Si vous souhaitez que cela soit appliqué à chaque entité linéaire intersectant une entité polygonale, accédez à l' éditeur de fonctions et utilisez la fonction suivante (changez le nom de polygon example_2pour qu'il corresponde au nom de votre couche polygonale):

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

Éditeur de fonction

Cliquez sur Charger, puis accédez à l' onglet Expression et saisissez func(). Espérons que le résultat devrait ressembler à ceci (en utilisant les mêmes propriétés de style mentionnées ci-dessus):

Résultat final

Joseph
la source
En fait, j'ai regardé cela, mais je me suis arrêté quand j'ai découvert que cela get_featurenécessite un nom et une valeur de champ. J'ai juste une couche polygonale et je voudrais utiliser toutes les entités de cette couche pour masquer. Est-ce possible?
Chau
@Chau - Message édité pour inclure une méthode possible :)
Joseph
1
Une autre option serait de dissoudre la couche polygonale.
csk
1
@Joseph - Lorsque vous utilisez a, Geometry Generatorla méthode est-elle funcappelée pour chaque entité du calque où elle est utilisée pour le style? Donc, si ma couche de ligne a 3 entités, elle funcest appelée 3 fois et dessine le même résultat 3 fois?
Chau
1
@Chau - Je pense que vous aviez raison, le code a été répété plusieurs fois dans chaque fonctionnalité. Modifié le message afin qu'il funcne soit désormais appelé que pour chaque entité linéaire et ne dessine le résultat qu'une seule fois (ce qui semble être le cas, comme le montrent les marqueurs de sommet à l'intérieur des polygones, avant qu'il ne soit caché sous lequel j'ai manqué). Merci de l'avoir signalé :)
Joseph
3

Dans le prolongement de la réponse de Joseph , j'ai créé cette fonction. Il représente différents systèmes de coordonnées et j'avais besoin de rechercher dans deux couches de masquage, donc il gère cela aussi. De plus, je voulais pouvoir masquer les lignes à l'intérieur des polygones ou les lignes à l'extérieur des polygones.

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

Cet exercice m'a montré que QGIS n'aime pas trop travailler avec de grands ensembles de données et que cet algorithme avec QGIS plante trop souvent. Je soupçonne que le rendu QGIS n'aime pas rendre les générateurs de géométrie qui prennent du temps.

Chau
la source