I'm looking to calculate focal statistics for each cell of a raster, within a neighborhood that changes shape.
Background - I have three binary rasters, each representing a single vegetation type of interest. I'd like to calculate the percent coverage of each vegetation type within (e.g.) 20 km^2 of any cell in my study area (sum/total cells in neighborhood). The problem is that I can't use a simple circle or square neighborhood around each cell because, if I did, the search area used to calculate the sum would incorporate areas outside my study area. This exception is important because the statistics will be used as inputs for a habitat model, and the areas outside of my study area cannot be considered possible habitat - they're urbanized. Including them would give me erroneous statistics. So, what I'm looking to do is come up with a way to choose a neighborhood representing the n nearest cells (n determined by number of cells required to cover an area equal to my desired neighborhood size) that meet my criteria: that they do not fall within an urbanized area. I'm thinking that some code that mimics cellular automata neighborhood decisions should be used. I've never worked with CA though.
Example - Let's say I'm calculating this statistic for a cell on the boundary of my study site. If I assign all areas outside of my study area to zero (or ignore NoData), then I will get a statistic that represents roughly half of the areal coverage I'm interested in. So, percent coverage in a ~10 km^2 area, instead of 20 km^2 area. Since I'm studying home range sizes this is important. The neighborhood has to change shape, since that is how the animal views/uses the landscape. If they need 20 km^2, they'll change the shape or their home territory so that in encompasses 20 km^2. If I do not check ignore NoData, cell output will be NoData - and NoData is no help.
I'm not a biologist, but wouldn't you be altering the range of the critter, i.e. isn't its range dependent on distance? In that case adding more distant areas to its range may not be valid. It's also key to think about land cover on the other side of the "fence" for example, the critter may not be able to cross the fence but still may benefit from the habitat on the other side as a noise buffer, etc. It's a really interesting corner of spatial analysis!
A nice trick I've found for calculating percentages may help you: create a 0-100-NoData raster and run Focal Statistics with MEAN. The averages of the zeros and 100s is the percentage. You may want to consider treating edge areas differently in your analysis, or just leaving them out.
Here's some untested map algebra to help you experiment, I'm using Python map algebra (say, at the Python prompt in ArcMap) but you could use the Raster Calculator tool if you want. Though I recommend using the ArcMap Python prompt so you can easily try things and see what happens (I recommend setting the extent to a small area (or set a large cell size) so the tools will run fast while you test it out, then set the extent to do your final analysis.
from arcpy import env
# set up arcpy map algebra
from arcpy.sa import *
env.extent = "MAXOF" # or set this from ArcMap Geoprocessing > Environments
env.snapRaster = "landcov"
env.cellSize = "landcov"
# create raster objects for arcpy map algebra
landcov = Raster("landcov")
study = Raster("study")
# calculate neighborhood
# 100-cell buffer (500 meters for a 100-meter landcov raster)
circbuf = Nbr("CIRCLE", 5)
# Calculate inside-area pct raster, output raster will range from 0 to 100
inside = FocalStatistics(Con(IsNull(studyarea), 0, 100), circbuf, "MEAN")
# land cover #1 percentages
lc1pct = FocalStatistics(Con(landcov == 1, 100, 0), circbuf, "MEAN")
# mask out areas < 100 % inside, you could pick 75 include
# areas near the edge that are less susceptible to edge effects.
lc1pct_clip = ExtractByMask(lc1pct, inside > 100)
i'm trying alter the shape (boundary) of the species' possible home territory, while keeping its home-range size the same. this specie has an average home-range size, but obviously the shape should be able to change given the availability of desirable landscape attributes (all individuals of this species won't have home-range sizes of exactly the same shape in nature, so there is some inherent flexibility). using extreme shapes would give me distances that aren't valid, but my study area isn't shaped in such a way as to give a home range that is, say, one kilometer wide, and twenty kilometers long.
The only approach (using arcpy map algebra and tools) for "region growing till you get enough area" that I can think of right now would be to to use cost allocation for these cells along the edge (one by one), though the processing would probably take a very long time and I'm not sure how much it would buy you over a variable circular buffer approach. (If the study area has a pretty smooth boundary, the cost allocations result would not be much different.)
The map algebra above would give you the ingredients you need to move forward. Good luck. Sounds like fun!
> but obviously the shape should be able to change given the availability of desirable landscape attributes
Do you have some sort of algorithm in mind to calculate "desirability"? That's starting to sound more like a cost allocation problem than a neighborhood analysis problem. So definitely read up on the weighted distance tools...
i have no algorithm to calculate desirable neighborhood in the sense you use it above, though i plan to work on one in the future. right now it's more of a (very simple) available neighborhood, with developed areas designated as not available. i should replace the above "desirable" with "available". poor choice of words!
last night a friend and i came up with this workflow:
1.) create binary mask raster where 1 = available pixels, 0 = non-available pixels (developed areas)
2.) for any pixel in binary mask generate circular buffer that gives desired neighborhood size n (where n = number of pixels needed, given pixel dimensions, to reach desired area) -> calculate sum x within buffer (sum = number of "available" cells) -> if x < n increase buffer size by y until n = x.
3.) for each corresponding cell in binary veg raster, use above buffer to calculate sum (will give sum of target veg type cells within appropriate buffer)
4.) assign that count to corresponding cell in new raster.
basically a lot like what you suggested above. thanks for the code BTW!
i'll post my code when i think i have something that works.
A few years ago I worked with a friend of mine to work with different wedge azimuths for each cell by creating a collection of rasters for each angle (you could create a collection of rasters calculated for each buffer size, chosen by percent inside study area) and then used the Pick tool to select the stats result she wanted for each individual cell.
Just another option I thought I'd throw at you, if you weren't overwhelmed by your options already!