Стилизация определенного слоя с помощью маски полигона в QGIS?

10

У меня есть слой линий и слой многоугольников в QGIS:

Перед маской

Я хотел бы стилизовать часть слоя линии вне многоугольника, используя один стиль, а часть внутри, используя другой стиль:

После маски

Я не хочу создавать производный набор данных, напр. обрезать слой линии и стиль две части.

Это простой случай, но в моем проекте QGIS у меня есть +30 слоев, поэтому я думаю, что любое смешение слоев нарушит нижележащие слои.

Можно ли сделать что-то подобное?

Я не хочу показывать слой многоугольника, это просто здесь, чтобы визуализировать то, что я хотел бы сделать.

Чау
источник
1
Хороший метод! Я думаю, что это должно быть опубликовано в качестве ответа вместо редактирования вопроса :)
Джозеф
@ Джозеф, все готово!
Чау

Ответы:

11

Не идеальное решение, но вы можете использовать Генератор геометрии, который добавляет визуализированную линию для представления пересечения. Затем вы можете установить это, чтобы перекрывать исходный линейный объект.

Добавьте новый слой символов, щелкнув знак плюс, и выберите Geometry generatorтип слоя символов. Задайте для типа geoemtry значение LineString / MultiLineStringи используйте следующее выражение:

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

Вам нужно будет добавить информацию о вашем конкретном полигоне, где:

  • polygonLayer имя вашего полигонального слоя
  • fieldName это имя поля
  • value значение функции вашего конкретного многоугольника

Свойства стиля

Обратите внимание, что для окрашивания визуальной линии вам может потребоваться сделать это из свойства « Рисовать эффекты» :

Свойства эффектов рисования

Это был результат (обратите внимание, что визуальная линия не полностью перекрывала исходную линию, поэтому я слегка изменил смещение):

Результат

И без многоугольника

Результат без полигона



Редактировать:

Если вы хотите, чтобы это применялось к каждому линейному объекту, пересекающему полигональный объект, перейдите в Редактор функций и используйте следующую функцию (измените имя, polygon example_2чтобы оно соответствовало имени вашего полигонального слоя):

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

Редактор функций

Нажмите « Загрузить», затем перейдите на вкладку « Выражение » и введите func(). Надеемся, что результат должен выглядеть следующим образом (используя те же свойства стиля, которые указаны выше):

Конечный результат

Джозеф
источник
Я на самом деле смотрел на это, но остановился, когда обнаружил, что get_featureтребует имя поля и значение. У меня просто есть слой многоугольника, и я хотел бы использовать все элементы этого слоя для маскировки. Это возможно?
Чау
@Chau - Отредактированный пост, чтобы включить возможный метод :)
Джозеф
1
Другим вариантом будет растворение полигонального слоя.
csk
1
@Joseph - При использовании a Geometry Generatorвызывается ли метод funcдля каждого элемента на слое, где он используется для стилизации? Так что, если мой линейный слой имеет 3 объекта, то он funcвызывается 3 раза, а результат получается 3 раза?
Чау
1
@ Чау - я думаю, что вы были правы, код повторял каждую функцию несколько раз. Отредактировал сообщение так, что funcтеперь его следует вызывать только для каждого линейного объекта и выводить результат только один раз (что, как показано на рисунке маркерами вершин внутри многоугольников, было скрыто под тем, что я пропустил). Спасибо за указание на это :)
Джозеф
3

Продолжая ответ Джозефа , я придумал эту функцию. Он учитывает разные системы координат, и мне нужно было перейти к двум маскирующим слоям, поэтому он тоже справляется с этим. Более того, я хотел иметь возможность маскировать линии внутри полигонов или линии вне полигонов.

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

Это упражнение показало мне, что QGIS не слишком любит работать с большими наборами данных, и этот алгоритм с QGIS часто ломается у меня на пути. Я подозреваю, что рендерер QGIS не любит рендерить Geometry Generators, которые отнимают много времени.

Чау
источник