4

I have the following map in .png-format that I would like to replicate, but I cannot obtain the original data.

The map shows the current water supply vulnerability for local municipalities in South Africa. I took a screenshot of the map and georeferenced with a local municipality shapefile. Here is the original .png-map:

Example_1

And here is the Local Municipality shapefile on top of the georeferenced raster:

Example_2

I would now like to extra each municipality value/colour (there are 10) to the shapefile. I tried using the SAGA-GIS Module 'Add raster values to features' tool, but the result classifies all features as 255:

Example_3

Where am I going wrong, or is it not possible to do this? This question is similar but the answer exceeds my capabilities.

Babel
  • 71,072
  • 14
  • 78
  • 208
JohnGIS
  • 505
  • 4
  • 15
  • 1
    Why not using a native colour picker in QGIS or another tool similar to it since you do not have that many colours? Have you tried r.colors.out or r.colors? – Taras Apr 11 '19 at 08:31
  • 1
    I hope this question gets a decent answer. This is a common real-world task that can be rather tricky in practice because the individual colours in an image file often turn out to not be very uniform at all. If you only have one map, I suspect it might be better to just accept that you will need to take a couple of hours to do it manually. – Alexander Apr 12 '19 at 08:58
  • @Taras would something like that be able to assign a unique value (out of 10 possible option/colours) to each feature in the shapefille attribute table? – JohnGIS Apr 12 '19 at 12:06
  • @Alexander I hope so too. I already had to do this manually and it took forever. I have two more maps left to make so it would be great if I could figure out a solution to streamline this. – JohnGIS Apr 12 '19 at 12:07
  • okay, clear. I thought it only involves one raster layer. – Taras Apr 12 '19 at 12:14

1 Answers1

5

Basic idea

You can use a simple QGIS expression to extract the color value of a raster image and add this as an attribute to your vector layer or use it directy to set the color of your vector features by setting the it's color to data driven override. Use this expression - I use a point layer and get the color of the raster layer named raster_map.

Be aware: raster- and vector layer should be in the same CRS to work!

color_rgb( 
    raster_value( 'raster_map', 1, make_point ($x,$y)),
    raster_value( 'raster_map', 2, make_point ($x,$y)),
    raster_value( 'raster_map', 3, make_point ($x,$y))
)

For polygons (as in your case) you could replace make_point ($x,$y) with centroid ($geometry) to get the color of the raster layer at the center of each polygon.

See screenshot: data driven override for a point layer - getting the color of the raster pixel located at the point's x/y coordinates. Data driven override of set color: QGIS data driven override for vector color: getting color from raster pixel


Variant: color legend

You see it even better when you use Geoemtry Generator to shift the points in another location. Like this, you can create kind of a color map (color legend) of the colors in the raster. Here, I used the function color_rgba( ) to include the 4th, alpha-channel (transparency values) to render the semi-transparent color of the sea correctly.

I shifted the points with this expression (and used an additional Geometry generator symbol layer with make_line() from this expression to $geometry to create the lines that connect the legend dot with the points where the color was sampled):

make_point (
    x_min (@map_extent) + 50000, -- distance from left border of map canvas
    y_max  (@map_extent) - $id * 100000 -- vertical distance
)

Color legend - the lines point to the pixels where the initial points was placed and where the color is sampled: enter image description here

Distribute color legend evenly

Use this more sophisticated expression to automatically place the points of the color legend in a way that only points that match a place inside the current canvas are shown and that they are evenly distributed, regardless of the number of points that are shown:

with_variable (
    'dist',
    50,  -- distance points are away from map canvas border: fraction of canves width: 50=1/50 of canvas width
with_variable (
    'visible_features',  -- variable representing an array of $id's of the features visible in current canvas
    array_agg( 
        $id,
        filter:=within (geometry($currentfeature) ,@map_extent )
    ),
    make_point (
        x_min (@map_extent) +  @map_extent_width / @dist,
        y_max (@map_extent) - 
        array_find (@visible_features, $id) * @map_extent_height / array_length (@visible_features) / 1.2 -  
        @map_extent_height / @dist-- variable vertical distance, see below for details
    )
))

Explanation:

  • Variable vertical distance, line 10: First point at 1/50 of current canvas height from the top. The other points are then evenly distributed with an intervall based on how many features are shown (only features inside the map canvas extent are counted). The value of 1.2 (end of line) determines how much of the vertical height is used to distribute the points (change this value to fit your needs: a value of 1 uses the whole canvas height, a value of 2 just half of it, a value of 4 only the upper quarter etc.)
Babel
  • 71,072
  • 14
  • 78
  • 208