7

I have 2 polygon layers, 1 showing roads and another showing parking restrictions on those roads (no parking, residents only, etc). I'm trying to style the layer according to the markings that would appear on the actual street (double yellow lines for no parking and white dashed line for residents only).

I've generated styles using a negative buffer in the geometry generator. But I'm not sure how to show only the section of polygon outline that are next to the curb (i.e. the shared outline of the road and restriction layers). This is as far as I have got:

crossing lines

But I'd like this as the end result (created with MS paint):

desired result

I need to achieve this without actually altering the geometry or creating any new layers that are for 'cartographic purposes only'. I found a useful question and answer here, but it's not quite what I'm looking for.

  • Do the polygons share a common edge (or a part of it)? I ask this with reference to the two white facing edges on the right, for example. Then, is it allowed to use a custom function? – mgri Sep 19 '17 at 15:19
  • if you want to supress the lines (like in the example you provided) you may try different "blending modes", but for what I understand, you may try to give an offset to your polygon's line (layer properties>fill>simple line>offset – Elio Diaz Sep 19 '17 at 17:00
  • @mgri Some of the restriction polygons share boundaries, whilst other do not. The key distinction is that I want to show outlines that are shared with the road outlines only. A custom function that is called from within the geometry generator would be fine. – firefly-orange Sep 20 '17 at 10:09
  • @ElioDiaz if I offset the polygon, I will still get the entire perimeter rendered, it'll just be in a different place. Equally blending modes will effect the entire polygon, not just the part I need. – firefly-orange Sep 20 '17 at 10:10
  • @firefly-orange please, could you share a sample dataset? I have one idea but I want to check if it is applicable to your case. – mgri Sep 21 '17 at 14:53
9

I propose an approach that only recurs to a geometry generator and a custom function.


Context

Let's assume to start from this situation (hopefully very close to yours), where the several parking restrictions are stored in the "Condition" field:

enter image description here

I also assume that you know how the QGIS custom functions work, otherwise this post might be helpful: How to create custom functions in QGIS using the function editor.


Preliminary task - Creating the custom function

After having loaded your layers in QGIS, open the Field Calculator and click on the Function Editor tab.

Then, click on New file and type curb_lines as the name of the new function:

enter image description here

You will see that a new function has been created and it is listed on the left side of the dialog. Now, click on the name of the function and replace the default @qgsfunction with the following code:

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

@qgsfunction(args='auto', group='Custom')
def curb_lines(restrictions_layer_name, roads_layer_name, feature, parent):

    def segments(polyline):
        for x in range(0, len(polyline)-1):
            first_point = polyline[x]
            second_point = polyline[x +1]
            seg = QgsGeometry.fromPolyline([first_point, second_point])
            idsList = index.intersects(seg.boundingBox())
            for id in idsList:
                tmp_geom = all_feats[id].geometry()
                if seg.touches(tmp_geom):
                    num.append(seg)
        return num

    layer  = QgsMapLayerRegistry.instance().mapLayersByName(restrictions_layer_name)[0]
    ro_layer  = QgsMapLayerRegistry.instance().mapLayersByName(roads_layer_name)[0]

    all_feats = {}
    index = QgsSpatialIndex()
    for ft in ro_layer.getFeatures():
        index.insertFeature(ft)
        all_feats[ft.id()] = ft

    input_geometry = QgsGeometry(feature.geometry())
    line_geom = QgsGeometry(input_geometry.geometry().boundary())
    polyline = line_geom.asPolyline()
    num = []
    res = segments(polyline)

    first = True
    for h in num:
        if first:
            geom = QgsGeometry(h)
            first = False
        else:
            geom = geom.combine(h)

    return geom

Once you have done this, click on the Load button and you will be able to see the function from the Custom Menu of the Expression dialog.


Solution

Firstly, go to Layer Properties | Style and then choose the Rule-based renderer.

Create two different rules (if you have only two categories, otherwise adapt it to your case): I will show you how to get a working solution only for the first category because the procedure is the same for all the others.

Double-click on the first rule and set the filter expression (step 1, see the image below):

"Condition" = 'Residential only'

enter image description here

From the Symbol selector dialog, choose a Geometry generator as symbol layer type (step 2) and Linestring / MultiLinestring as geometry type (step 3).

Now, type this expression (step 4):

curb_lines('restr', 'roads')

where 'restr' and 'roads' are the layer names for the restrictions and roads layers, respectively (adapt them to your case).

If you want to set a different style for each category, go to step 5 and set Color, Pen width, Offset and so on (the answer won't cover this part).

Click on the OK button and repeat the previous steps for the other categories.

Finally, click on the Apply button for applying the changes.

You will see something like this:

enter image description here

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.