3

In QGIS 3.30 I have a dataset of points associated with several values, call them integers A and B.

I want to create a raster with maybe 5km x 5km pixels where the value associated with each pixel indicates a function of A, B and the distance to all points within a specific maximum distance.

Something of pseudo-code:

For each pixel in the raster.

  • For each point within about 300km of the centre of the pixel calculate a value based on distance, A and B
  • Sum all these calculations and assign them to the value of the raster

The final goal will be to combine a few of these rasters in numerous ways in the usual ways, such as clipping to a polygon and applying a function to combine these function raster values.

2 Answers 2

4

First create a vector polygon grid with the size/resolution of the raster pixels you want to create. Then use the following expression to create a new attribute field to create the value you need, based on fields a and b from layer points (in line 4, change the kind of calcluation to adapt to your use case):

array_sum (
    overlay_nearest (
        'name_of_point_layer',  -- change layer name
        a / b,  -- the calculation you want to make, based on fields "a" and "b" from layer points
        max_distance:=300000, -- max. distance of point to take into consideration
        limit:=-1
    )
)

When finished, run Menu Raster > Conversion > Rasterize (Vector to Raster) and set the new field creted before as input for Field to use for a burn-in value.


Edit

To answer your follow up question from the comments: to include the the distance between the feature being evaluated and the item from the array returned by overlay_nearest so that the calculation changes based on that distance, use the following expression. It calculates the division of attributes a and b and multiplies this with the distance a point is away from the current feature (thus higher values for points far away):

array_foreach(
    overlay_nearest (
        'points', -- replace with the name of your point layer
        @feature, -- available since QGIS 3.28
        max_distance:= 300000,  -- define the distance
        limit:= -1
    ),
    attribute (@element, 'a') /  -- get the value for attribute field a 
    attribute (@element, 'b') *  -- get the value for attribute field a 
    length (make_line ($geometry, geometry(@element))  -- get the distance between the feature being evaluated and the item from the array returned by overlay_nearest
    )
)
4
  • Thanks, this is exactly what I was looking for! A follow up question though. Is there any way to incorporate the distance between the cell being evaluated and the items from the array returned by overlay_nearest? So that my calculation can change based on that distance. I know that there are distance functions, but I am not sure in this context which feature "geometry" would refer to, and what I could use for the other point. May 23, 2023 at 5:47
  • 1
    See updated answer
    – Babel
    May 23, 2023 at 7:24
  • Just wondering why you used "length(make_line($geometry, geometry(@element)))" rather than "distance($geometry, geometry(@element))"? Is one faster to execute than the other? May 23, 2023 at 15:53
  • 1
    Simply seems I overlooked the distance() function... ;-)
    – Babel
    May 23, 2023 at 15:59
1

So I thought that I would post the solution that I came up with based on the excellent guidance of @Babel.

My source dataset was a spreadsheet of geocoded EV chargers across the country that came in as strings. So my first step was to create binned fields as follows:

-- handle definition
try(coalesce(to_int(attribute( @feature, 'Number of')), 0), 0)

-- handle_bin definition
case 
    when attribute( @feature, 'handles') > 10 then 5
    when attribute( @feature, 'handles') >= 8 then 4
    when attribute( @feature, 'handles') >= 4 then 3
    when attribute( @feature, 'handles') >= 2 then 2
    when attribute( @feature, 'handles') >= 1 then 1
    else 0
end

-- power definition
try(coalesce(to_int(attribute( @feature, 'Power Ra_1')), 0), 0)

-- power_bin definition
case 
    when attribute( @feature, 'power') >= 250 then 5
    when attribute( @feature, 'power') >= 150 then 4
    when attribute( @feature, 'power') >= 100 then 3
    when attribute( @feature, 'power') >= 50 then 2
    when attribute( @feature, 'power') >= 1 then 1
    else 0
end

After this, it was a pretty simple matter to follow Babel's instructions to create a grid (I used 10km for speed since my country is very large, might increase resolution later but it does not change anything in the code) and then add a calculated field as folllows

-- DCTravelScore definition
coalesce(
    array_sum(
        array_foreach(
            overlay_nearest (
                'Australia_chargers_xlsx_2c8dfa1c_99e3_4eeb_ae67_988ade0ad84e',
                @feature, -- available since QGIS 3.28
                max_distance:= 100000,  -- define the distance
                limit:= -1
            ),
            with_variable(
                'distance', 
                distance($geometry, geometry(@element)),
                coalesce(attribute (@element, 'handle_bin'), 0) *
                coalesce(attribute (@element, 'power_bin'), 0) *
                case 
                    when @distance <= 5000.0 then 5 
                    when @distance <= 10000.0 then 4
                    when @distance <= 20000.0 then 3
                    when @distance <= 50000.0 then 2
                    when @distance <= 100000.0 then 1
                    else 0
                end
            )
        )
    ), 0
)

Not sure whether there is a more elegant way to do binning like this? At least the with_variable function exists to create a variable in local scope without jumping into full script mode!

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.