Weeknotes: 27th October 2025

Last week

Edge Effects

It's been a couple of months since I mentioned I needed to look into how to apply fractional edge-effects to Area of Habitat maps, and this week I finally got around to implementing it. Before I go into the details, a quick recap. An Area Of Habitat (AOH) is a map that shows the likely area a species will live, based on a refinement of a species range estimate, that is a polygon area of the planet where we think it might be found, in combination with high resolution raster maps of elevation (where each pixel's value is the height of the ground in metres) and habitat (where each pixel is assigned a habitat type like desert, forest, etc.). Because we know the species' habitat and elevation preferences, we can combine all three layers of data to get just the pixels where there is suitable living conditions for the species.

An AOH is a simple model, and lacks refinement in many ways, but does have the benefits of being relatively cheap to compute, and easy to reason about vs more statistically derived methods, or even machine learning based methods. When people come to me with a highly aggregated result like a LIFE or STAR map, both of which are biodiversity metrics that combine data from thousands of species, and ask my "why is this pixel this value", thanks to the simple model of AOH I can get a root cause of why things are the way they are relatively quickly (albeit somewhat laboriously).

But that isn't to say we don't want to do better, and the edge effects work is an example of that. We know from existing work that whilst a species might like a particular habitat type, e.g., forest, some species do not like to go to the edges of that habitat. Indeed, this starts to tie into another general problem with habitat maps, which is in reality habitats do not have nicely defined edges where it is desert on one side on forest on the other, rather they usually are more blended over a distance. But I digress, as in this work we know that certain species will not go say within 50m of the edge of a forest, living only withn in the core. This then presents a challenge when people report forest preservation based on area alone: if a species won't use the edges of a forest, then if I have two forests with the same area and one is a contiguous block of forest, and the other is lots of small 100x100m parcels, then the usable area for the species is very different. Indeed, if the species can't go within 50m of an edge the 100x100m parcels are of no use, so the area is effectively zero.

But AOH, being a simple model doesn't capture this, so a group of us are looking at adding it to how we calculate LIFE and seeing what impact that has. A while back I did an initial look at this, doing a simple binary erosion of AOHs based on removing the outer pixel of the habitat areas before generating the AOH. This showed that for a bunch of bird species in Brazil, where we know the land is highly fragmented in areas like the Atlantic Forest, even removing just 100m from the edge of habitats can reduce the AOH by up to 50%. This both shows how important it is to consider edge effects, but also how important it is to not over-estimate the edge distance a species will tolerate.

To try get more understanding of this, this last week I did two more variations of my edge-effect code. The first one was to add fractional pixel edges. We know that for some species the literature says they like edges of say 30m, yet all our input data is at 100m per pixel, and so to deal with that I needed to implement fractional erosion of the habitat area, as you can see in this animation that is slowly going from 0m to 100m of erosion on a 100m per pixel data source:

Animated GIF of a QGIS screenshot showing progressive erosion of white pixels in an Area of Habitat map against a black background, demonstrating the application of an edge effect that gradually removes pixels from the boundaries inward, with the layers panel visible on the left showing the data sources.

For this I did a somewhat simplistic geometric method that assumes pixels are square (they are not) and roughly all the same size (they are not, particularly in the map projection I was testing out on here). I'd hoped I'd find some neat math trick to have a simple algorithm, but in the end the code just splits the pixel into nine sub-pixels and works out how to impact each one. On the plus side it did make it easier to explain to my ecologist colleages when they asked me how I was calculating the fractional impact.

The code is, obviously as its me, using Yirgacheffe, and I'm very pleased with how easy to read the code is thanks to the declarative nature of Yirgacheffe and that all the book keeping is hidden. Whilst my implementation algorithm isn't the most interesting, I like to think anyone with a passing understanding of python could see what I was up to, allowing for the use of a convolution matrix via conv2d perhaps needing a little explanation.

We also got some values for other species that went way above 100m, as far as 1500m, and so I also expanded my code to allow for multi-pixel edge effects, as shown here going from 0 to 1500m:

Animated GIF of a QGIS screenshot showing progressive erosion of white pixels in an Area of Habitat map of the Atlantic Forest region in Brazil, demonstrating an edge effect that gradually removes habitat pixels from the boundaries inward, with the map displayed against a black background and a layers panel visible on the left.

If you look at the summed area values for these, you can see an obviously non-linear impact of the edge as it increases, which again shows why it is important to try consider this, but also important that the edge values are something you can trust.

Our next stage is to both look into getting more data on species edge-data and to find a way to validate the results so that we can have some confidence that what we're generating isn't nonsense.

Life, the universe, and validation

I had some great discussions with Shane on how all these biodiversity pipelines fit together, and how they relate to other work going on around us both in the Energy and Environment Group (e.g., TESSERA), and in the broader Cambridge Conservation Initiative. Shane is trying to map out interesting projects for his first year as a PhD student, and so all of this context is hopefully highlighting some interesting challenges.

The main recurring theme, and one that Alison Eyres and I have been keen to look at more, is validation of results. Whilst in this area there is the Dahal et al validation method, that's got a well defined use case, but might not be ideal for say evaluating how we change habitat maps or other AOH related things. In the EEG and CCI I can see lots of ways we could do better biodiversity metrics, but what concerns me is how we measure whether that intuition is correct or not, and so I've spent a bunch of time discussing that with Shane in particular, as evaluating any improvements will be key.

IUCN Redlist API work

A little while ago Daniele Baisero released a Python library for accessing the IUCN Redlist API. I'd known this was in the works, so for the longest time I'd been putting off making it easier for people other than me to run my implementations of LIFE and STAR by having it fetch species data using the Redlist API (I have a local archive I use to run them).

So to that end I updated my STAR pipeline to use the new API library. There were a few hiccups along the way, as this was the first time I'd use the Redlist API and I accidentally blew past the rate limits, and because Daniele's library hasn't been used in anger like I was using it before, but it's been great having to not write the low level API code myself.

Once this is properly passing tests and marged to STAR I'll do the same for LIFE.

Yirgacheffe

Just a small addition to Yirgacheffe this week, which is to add a way to let you generate a layer directly from a numpy array, which will make it easier for me to write unit tests for my AOH library, something I need to do before I try integrating the edge-effect work into the main AOH library I use, rather than having it as a stand alone thing that is off to one side like it is currently.

I still need to find time to do the last push to get Yirgacheffe to 2.0, but that's probably a month or so away given other commitments.

This week

  • Finish up the Redlist API work for STAR
  • Check in with Shane again before I have to dash back up to Liverpool way later this week
  • We have a LIFE planning meeting, so prep a little for that

Tags: yirgacheffe, life, star, validation, aoh