¿Diseñar una capa específica usando una máscara de polígono en QGIS?

10

Tengo una capa de línea y una capa de polígono en QGIS:

Antes de la máscara

Me gustaría diseñar la parte de la capa de línea fuera del polígono usando un estilo y la parte dentro usando un estilo diferente:

Después de la máscara

No quiero crear un conjunto de datos derivados, ej. recorta la capa de línea y dale estilo a las dos partes.

Este es un caso simple, pero en mi proyecto QGIS tengo +30 capas, por lo que creo que cualquier combinación de capas perturbaría las capas subyacentes.

¿Es posible hacer algo como esto?

No quiero mostrar la capa de polígono, solo está aquí para visualizar lo que me gustaría hacer.

Chau
fuente
1
Buen método! Creo que debería publicarse como una respuesta en lugar de una edición de la pregunta :)
Joseph
@Joseph, ¡está hecho!
Chau

Respuestas:

11

No es una solución perfecta, pero podría utilizar el Geometry Generator que agrega una línea visualizada para representar la intersección. Luego puede configurar esto para superponer la función de línea original.

Agregue una nueva capa de símbolo haciendo clic en el signo más y seleccione el Geometry generatortipo de capa como símbolo. Establezca el tipo de geoemtry en LineString / MultiLineStringy use la siguiente expresión:

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

Debería agregar detalles sobre su polígono específico donde:

  • polygonLayer es el nombre de tu capa de polígono
  • fieldName es el nombre del campo
  • value es el valor de la característica de su polígono específico

Propiedades de estilo

Tenga en cuenta que para colorear la línea visual, es posible que deba hacerlo desde la propiedad Dibujar efectos :

Dibujar propiedades de efectos

Este fue el resultado (tenga en cuenta que la línea visual no se superponía completamente con la línea original, por lo que modifiqué ligeramente el desplazamiento):

Resultado

Y sin el polígono:

Resultado sin polígono



Editar:

Si desea que esto se aplique para cada entidad de línea que se cruza con una entidad poligonal, vaya al Editor de funciones y use la siguiente función (cambie el nombre de polygon example_2para que coincida con el nombre de su capa de polígono):

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

Editor de funciones

Haga clic en Cargar , vaya a la pestaña Expresión y escriba func(). Esperemos que el resultado sea similar al siguiente (usando las mismas propiedades de estilo mencionadas anteriormente):

Resultado final

Joseph
fuente
De hecho, miré eso, pero me detuve cuando descubrí que eso get_featurerequiere un nombre y un valor de campo. Solo tengo una capa de polígono y me gustaría usar todas las características en esa capa para enmascarar. ¿Es eso posible?
Chau
@Chau - Publicación editada para incluir un posible método :)
Joseph
1
Otra opción sería disolver la capa de polígono.
csk
1
@Joseph: cuando se usa a, ¿ Geometry Generatorse funcllama al método para cada entidad en la capa donde se usa para diseñar? Entonces, si mi capa de línea tiene 3 características, ¿ funcse llama 3 veces y dibuja el mismo resultado 3 veces?
Chau
1
@Chau: creo que tenías razón, el código recorrió cada función varias veces. Edité la publicación de modo que funcahora solo debería llamarse por cada entidad de línea y solo dibujará el resultado una vez (que parece ser el caso, como lo muestran los marcadores de vértice dentro de los polígonos, antes de que esto se ocultara debajo de lo que me perdí). Gracias por señalar esto :)
Joseph
3

Ampliando la respuesta de Joseph , se me ocurrió esta función. Da cuenta de diferentes sistemas de coordenadas y necesitaba buscar en dos capas de enmascaramiento, por lo que también lo maneja. Además, quería poder enmascarar las líneas dentro de los polígonos o las líneas fuera de los polígonos.

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

Este ejercicio me ha demostrado que QGIS no es muy aficionado a trabajar con grandes conjuntos de datos y este algoritmo con QGIS se bloquea en mi camino con demasiada frecuencia. Sospecho que al renderizador QGIS no le gusta renderizar generadores de geometría que requieren mucho tiempo.

Chau
fuente