# Buffering with obstacles

I want to analyse parks for their reachability by foot, and I thought of doing that with a buffer around the parks (more than 300 in total). Now the problem is, that in cities usually there are boundaries that cant be crossed or only at very specific spots. So I would like to create a buffer that is limited by these boundaries, but where they are open, it should still expand according to the maximum distance given to the buffer. I think it can be easily understood from the picture below. I'd need the polygons for further analyses like the amount of people living in the parks vicinity.

Any tips on how to approach that?

• You're looking for travel distances instead of usual buffers. Those require a network to literally run on.
– Erik
Mar 24, 2021 at 15:57

EDIT : Faster processing direcly with geometries

From :

To :

With (description in the code comments) :

``````def buffer_dist(lyr_input, distance: int):
"""Create a buffer from the lyr_input at distance"""
result = processing.run(
"native:buffer",
{
"INPUT": lyr_input,
"DISTANCE": distance,
"SEGMENTS": 30,
"END_CAP_STYLE": 0,
"JOIN_STYLE": 0,
"MITER_LIMIT": 2,
"DISSOLVE": False,
"OUTPUT": 'TEMPORARY_OUTPUT'
}
)
return result["OUTPUT"]

def diff(lyr_input, lyr_overlay):
"""Make a diffence between lyr_input and lyr_overlay"""
result = processing.run(
"native:difference",
{
"INPUT": lyr_input,
"OVERLAY": lyr_overlay,
"OUTPUT": "TEMPORARY_OUTPUT"
}
)
return result["OUTPUT"]

# init the start layer, can be points, lines, polygons
# layer named in my QGIS project: "start"
lyr_start = QgsProject.instance().mapLayersByName("start")[0]
# init layer with obstacles, in my case a line layer
# layer named in my QGIS project: "obstacle"
lyr_obstacle = QgsProject.instance().mapLayersByName("obstacle")[0]
# if layer with obstacles is a line layer, create a small buffer around
lyr_obstacle_buffered = buffer_dist(lyr_obstacle, 1)
# set the walking distance
walking_distance = 100

result = lyr_start
# to each loop, add 1 unit until walking_distance
for i in range(walking_distance):
# add a buffer of 1 unit
result = buffer_dist(result, 1)
# make a buffer at 0 units to make the geometry valid
result = buffer_dist(result, 0)
# make a difference between the buffer and the obstacles
result = diff(result, lyr_obstacle_buffered)

# finally, add result in the project
``````

Fast version :

``````# init the start layer, can be points, lines, polygons
# layer named in my QGIS project: "start"
lyr_start = QgsProject.instance().mapLayersByName("start")[0]
# init layer with obstacles, in my case a line layer
# layer named in my QGIS project: "obstacle"
lyr_obstacle = QgsProject.instance().mapLayersByName("obstacle")[0]
# set the walking distance
walking_distance = 100
# create a red transparent rubberband to display result
rubber = QgsRubberBand(iface.mapCanvas(), QgsWkbTypes.PolygonGeometry)
rubber.setFillColor(QColor(255, 0, 0, 200))

# create the obstacle buffer
for i, obstacle_feat in enumerate(lyr_obstacle.getFeatures()):
# first loop, create the buffer from first line
if i == 0:
obstacles = obstacle_feat.geometry().buffer(1.1, 30)
# for the next loops, combine the buffer with the previous obstacles geometry
else:
obstacles = obstacles.combine(obstacle_feat.geometry().buffer(1.1, 30))

# iterate over start layer features
for start_feat in lyr_start.getFeatures():
# init walking_buff
walking_buff = start_feat.geometry()
# to each loop, add 1 unit until walking_distance
for j in range(walking_distance):
# add a buffer of 1 unit
walking_buff = walking_buff.buffer(1, 30)
# make the geometry valid
walking_buff = walking_buff.makeValid()
# make a difference between the buffer and the obstacles
walking_buff = walking_buff.difference(obstacles)
# add walking buffer to the rubberband
``````
• This is big brain. Simply rename your layers in QGIS to "start" and "obstacle", then hit run on the code. If you have very large geometry, best take a small subset first to make sure it works. Oct 10, 2023 at 22:42

There are two possibilities that come to mind.

The first is to use isochrone instead of buffer (there are numerous online services that will calculate them for you, some of them are discussed here). As isochrones are calculated using a street network, the result will account for uncrossable boundaries. The problems with this solution are:

• Pedestrians can wander outside of the street network (think of crossing a plaza diagonally or walking on grass where there really isn't a path).
• Isochrones are generally calculated from a point while your parks may not have an obvious entrance point if they are not fenced (if they are relatively small, a point in the center could work)

The second solution is to use a cost distance analysis where your cost raster is set to 1 for walkable area and to no data for obstacles effectively preventing crossing. The advantage of this approach is that you are not constrained by the street network and get a model that's closer to real foot walking, the difficulty is the creation of the cost raster by identifying all non accessible areas for pedestrian.

• Nice icon (probably a new one) =) Mar 24, 2021 at 16:34
• Finally, the map cost distance worked very nicely for my purpose. And, as i had access to a complete surface coverage layer, it was even possible to assign different costs to different surfaces, making my model much more precise than i wanted it to be! So thanks! You showed me a tool which will come in handy more often i think ;). Jun 2, 2021 at 14:16

An alternative technique using standard spatial analyst tools in ArcGIS Pro or ArcMap is to use the Euclidean distance tool and set the lines as barriers and a maximum distance. This would create such a dataset

Then using the CON tool convert all distances below your threshold distance to 1 and everything else 0 or nodata. In the example below all cells less than 4Km away are coded up as 1 (light brown).

No code or network analysis required and could easily be bundled up in a model.