4

I got two files, a multi-polygon file with polygons and points (File 1) and a file with points only (File 2).

Our task is to fill the color of the points from File 2 with the respectively nearest polygon or point from File 1.

I use QGIS. I'm almost sure there is an expression or method to do just that!

We also got another line layer that shows the distance between the points and the respectively nearest point/polygon (File 1), so I could technically color it all manually, but I want to learn how to automate my workflow.


EDIT 1: @Babel’s answer does not work for me so far. Here are the wrong symbols: https://ibb.co/ZXSkk2y My two layers do not have the column "Id" but @osmId in the attribute table instead, but changing the "Id" with "@osmID" doesnt work either :/ https://ibb.co/020zD61 This is my command, which should be correct...: https://ibb.co/Fwy10Xk I also tried to activate the current expression here, didnt work either: https://ibb.co/Y3bz13p And lastly, I should add that that file 1 is a multi-polygon file with polygons as well as points...maybe theres the error?


EDIT 2: Those are my values from Layer/File 1 (points): https://ibb.co/r2wh0MX and those are my vaules from Layer/File 1 (polygons): https://ibb.co/Df3dfgJ

Both definited by @osmId because there is no column "Id"

Now I want that the points from Layer / File 2 (Kindergarten_Karlsruhe) have the nearest colour either from the point from Layer/File 1 (Krankenhaeuser_Karlsruhe) (points) or from Layer/File 1 (Krankenhaueser_Karlsruhe) (polygons).

Layer 2 (Kindergarten_Karlsruhe) should receive the colour from either of those two geometries (polygons/points) from Layer 1 (Krankenhaeuser_Karlsruhe) which the respecitve kindergarten point is closest to.

Here you can see both Layers again: https://ibb.co/n3T03m2

Now, if type in the formula instead of "Id" "@osmId", there is an error messing appearing... see also: https://ibb.co/12jMZV2


EDIT 3:
This is correct, I have two (grouped) layers, both named Krankenhaeuser_Karlsruhe, once as polygon and once as points. I renamed both of them and set the colour settings from both again and tried to type in the command - still with an error!
See: https://ibb.co/4ZS08qQ
Error Message: https://ibb.co/vYSGNxn

3
  • Marked some parts of your updates as deleted as links were dead etc. Please update and revise the question, delete obsolete parts, so that it becomes clear where you're still stuck and what kind of help youstill expect and what has been solved.
    – Babel
    Commented Jan 11, 2021 at 20:35
  • You have $@osmId where you should be using $id. $id is a special variable that does not reference a normal column in your attribute table but the internal ID. Commented Jan 11, 2021 at 20:45
  • @bugmenot123: see gis.stackexchange.com/a/383812/88814 - $id is problematic for another reason.
    – Babel
    Commented Jan 12, 2021 at 7:06

2 Answers 2

11

You can use the new overlay expressions, available since QGIS version 3.16, see Visual changelog. By the way: I'm not sure what you mean that you have "a multi-polygon file with polygons and points", that does not make sense to me, so maybe be more precise. Likewise, you did not include any information how the colors in layer 1 are defined. But anyway, once you understood how the get the color from one layer to another, you can play around with the settings and adapt it to your needs.

So here I show the principles. For demonstration purpose, let's suppose the features from layer 1 have a categorized color, defined by an attribute value, from a color ramp. I used the id as value for the definition of the color and the Viridis color ramp as you can see on this screenshot:

enter image description here

Now, on layer 2 with the points, I set the color to data driven override and select assistant:

enter image description here

In the dialog panel, paste this expression in the source field:

array_first (overlay_nearest('polygon',$id))

So for every point, the expression creates an array of features, ordered by distance (overlay_nearest) from the layer 'polygon' and returns the id of these polygons (id of nearest, id of second nearest, id of third nearest etc.). With array_first you get the first, thus the nearest, polygon id. Every point now has the id of the nearest polygon. We use that and set the same Viridis color ramp to get the same color (the small dotted black line is for demonstration purposes only):

enter image description here

Update: See on my first screenshot that I have 15 classes for colors (1 to 15) plus another one for "other values" (yellow) - that makes 16 together. So be sure to load the valus when defining the colors of layer 2 (below the field where you paste the expression): see last screenshot, double arrow symbol. You could also set manually values from 1 to 15 - or 16, in case the colors don't match to conform with the number from above.

6
  • Thanks for your answer! Unfortunately, it does not work...
    – Nokturius
    Commented Jan 6, 2021 at 18:19
  • I added new information, thanks again for your help! I hope ill soon work it out together with you.
    – Nokturius
    Commented Jan 7, 2021 at 12:37
  • It says the same as my error message mentioning in my main post
    – Nokturius
    Commented Jan 7, 2021 at 13:01
  • I searched the internet so far but didnt find an recent method to easily chance the columns name, how do I do it?
    – Nokturius
    Commented Jan 7, 2021 at 13:15
  • 1
    @Nokturius there is a new Rename Field processing algortihm if you are using 3.16
    – Dror Bogin
    Commented Jan 7, 2021 at 20:45
7

Using @babel's answer I wrote a function to get a feature's color by layer name\id and feature ID.
You are welcome to add it to you QGIS with the function editor.

from qgis.core import *
from qgis.gui import *
from PyQt5.QtCore import QVariant

@qgsfunction(args='auto', group='Custom')
def get_color(layer, uid, feature, parent):
    """
    Gets the main color for a feature, works with 'single symbol', 'graduated symbol' and 'categorized symbol' types.
    Returns a RGBA string.
    <h2>Example usage:</h2>
    <ul>
      <li>get_color('Buildings',47) -> '255,100,46,255'</li>
    </ul>
    """
    layers_names = []
    for ll in QgsProject.instance().mapLayers().values():
        if ll.name() == layer or ll.id() == layer:
            layer1 = ll
    
    iterator = layer1.getFeatures(QgsFeatureRequest().setFilterFid(uid))
    feature = next(iterator)
    color = [0,0,0,0]
    if layer1.renderer().type() =='singleSymbol':
        rgb = layer1.renderer().symbol().color().getRgb()
        color = '{},{},{},{}'.format(rgb[0],rgb[1],rgb[2],rgb[3])
        
    if layer1.renderer().type() == 'graduatedSymbol':
        for range in layer1.renderer().ranges:
            attribute = layer1.renderer().classAttribute()
            value = feature.attribute(attribute)
            return value
            if range.upperValue() > range and range.lowerValue() < range:
                rgb = range.symbol().color().getRgb()
                color = '{},{},{},{}'.format(rgb[0],rgb[1],rgb[2],rgb[3])

                
    if layer1.renderer().type() == 'categorizedSymbol':
        attribute = layer1.renderer().classAttribute()
        value = feature.attribute(attribute)
        catValues = []
        for cat in layer1.renderer().categories():
            if type(cat.value()) != QVariant:
                if int(cat.value()) == value:
                    rgb = cat.symbol().color().getRgb()
                    color = '{},{},{},{}'.format(rgb[0],rgb[1],rgb[2],rgb[3])

    return color

After adding it to your QGIS expression engine (just go to the function editor, create a new file, copy and paste the code and save) you could use an expression like this:

get_color('Buildings',array_first (overlay_nearest('Buildings',$id)))

Note that the $id part is the automatic feature ID in the layer, and not a specific attribute.

Also Note that the function currently only supports single, graduated and categorized symbol types, and not rule based.

You can even keep using a single symbol style and just edit the expression for the fill color:

enter image description here

7
  • That's really great! That is the additional benefit of answering a question, to see it turn into a feature. This should be implemented as a standard expression in one of the next QGIS versions - did you propose it to be included in the official QGIS code?
    – Babel
    Commented Jan 7, 2021 at 21:21
  • I'll check it out in the morning, didn't really do anything but post here, not sure this is currently worth inclusion as a feature. I think this is eventually only a bypass since a @symbol_color variable exists in the expression engine but can't be seen between layers, i.e you can only get the symbol color in a layer style expression of the layer you are styling.
    – Dror Bogin
    Commented Jan 8, 2021 at 1:54
  • @babel did you try evaluating just $id on the Buildings layer? Because $id is a built-in attribute every feature should, you can just search for $ to see all available attributes https://imgur.com/ueHhjcC
    – Dror Bogin
    Commented Jan 8, 2021 at 6:22
  • And you are getting a feature when you use array_first(overlay_nearest('Buildings')) and a number when array_first(overlay_nearest('Buildings',$id))?
    – Dror Bogin
    Commented Jan 8, 2021 at 6:57
  • Your data worked just fine for me, Imgur. You could try what I used, but it's just natural earth data gpkg
    – Dror Bogin
    Commented Jan 8, 2021 at 7:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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