Skip to content
Snippets Groups Projects
Commit c9d982f1 authored by xo30xoqa's avatar xo30xoqa
Browse files

Wrote the `distanceto()` function

parent 4b11014b
No related branches found
No related tags found
No related merge requests found
......@@ -84,9 +84,9 @@ function updateevents!(model::AgentBasedModel)
end
"""
createevent(model, pos, name, duration=1)
createevent!(model, pixels, name, duration=1)
Add a farm event to the specified pixels for a given duration.
Add a farm event to the specified pixels (a vector of position tuples) for a given duration.
"""
function createevent!(model::AgentBasedModel, pixels::Vector{Tuple{Int64,Int64}},
name::EventType, duration::Int64=1)
......@@ -97,7 +97,7 @@ function createevent!(model::AgentBasedModel, pixels::Vector{Tuple{Int64,Int64}}
end
"""
landcover(model, position)
landcover(position, model)
Return the land cover class at this position (utility wrapper).
"""
......@@ -106,7 +106,7 @@ function landcover(pos::Tuple{Int64,Int64}, model::AgentBasedModel)
end
"""
farmplot(model, position)
farmplot(position, model)
Return the farm plot at this position, or nothing if there is none (utility wrapper).
"""
......@@ -115,27 +115,60 @@ function farmplot(pos::Tuple{Int64,Int64}, model::AgentBasedModel)
model[model.landscape[pos...].fieldid]
end
"""
distanceto(model, pos, habitattype)
distanceto(pos, model, habitatdescriptor)
Calculate the distance from the given location to the closest location matching the
habitat descriptor function. Caution: can be computationally expensive!
"""
function distanceto(pos::Tuple{Int64,Int64}, model::AgentBasedModel, habitatdescriptor::Function)
(habitatdescriptor(pos, model)) && (return 0)
dist = 1
width, height = size(model.landscape)
while dist <= width || dist <= height
# check the upper and lower bounds of the enclosing rectangle
y1 = pos[2] - dist
y2 = pos[2] + dist
minx = maximum((pos[1]-dist, 1))
maxx = minimum((pos[1]+dist, width))
for x in minx:maxx
(y1 > 0 && habitatdescriptor((x,y1), model)) && (return dist)
(y2 <= height && habitatdescriptor((x,y2), model)) && (return dist)
end
# check the left and right bounds of the enclosing rectangle
x1 = pos[1] - dist
x2 = pos[1] + dist
miny = maximum((pos[2]-dist+1, 1))
maxy = minimum((pos[2]+dist-1, height))
for y in miny:maxy
(x1 > 0 && habitatdescriptor((x1,y), model)) && (return dist)
(x2 <= width && habitatdescriptor((x2,y), model)) && (return dist)
end
dist += 1
end
return Inf
end
"""
distanceto(pos, model, habitattype)
Calculate the distance from the given location to the closest habitat of the specified type.
Caution: can be computationally expensive!
"""
function distanceto(pos::Tuple{Int64,Int64}, model::AgentBasedModel, habitattype::LandCover)
#XXX can I make this check for both land cover type and crop type?
# (Or even take a full habitat descriptor?)
#TODO
# can't use @habitat here because nature.jl is loaded later than this file
distanceto(pos, model, function(p,m) landcover(p,m) == habitattype end)
end
"""
distancetoedge(model, pos)
distancetoedge(pos, model)
Calculate the distance from the given location to the closest neighbouring habitat.
Caution: can be computationally expensive!
"""
function distancetoedge(pos::Tuple{Int64,Int64}, model::AgentBasedModel)
lc = landcover(model, pos)
dist = 1
#TODO
# can't use @habitat here because nature.jl is loaded later than this file
distanceto(pos, model, function(p,m) landcover(p,m) != landcover(pos, model) end)
end
......@@ -13,16 +13,16 @@
Persephone.initfields!(model)
# these tests are specific to the Jena maps
@test size(model.landscape) == (1754, 1602)
@test Persephone.landcover(model, (100,100)) == Persephone.forest
@test Persephone.landcover(model, (300,1)) == Persephone.soil
@test Persephone.landcover(model, (500,1)) == Persephone.nodata
@test Persephone.landcover(model, (400,400)) == Persephone.grass
@test Persephone.landcover(model, (800,800)) == Persephone.agriculture
@test Persephone.landcover(model, (1100,1100)) == Persephone.builtup
@test Persephone.landcover((100,100), model) == Persephone.forest
@test Persephone.landcover((300,1), model) == Persephone.soil
@test Persephone.landcover((500,1), model) == Persephone.nodata
@test Persephone.landcover((400,400), model) == Persephone.grass
@test Persephone.landcover((800,800), model) == Persephone.agriculture
@test Persephone.landcover((1100,1100), model) == Persephone.builtup
@test Persephone.countfields(model) == 2092
@test Persephone.averagefieldsize(model) == 5.37
@test count(f -> ismissing(f.fieldid), model.landscape) == 1685573
@test length(Persephone.farmplot(model, (800,800)).pixels) == 4049
@test length(Persephone.farmplot((800,800), model).pixels) == 4049
end
@testset "Event system" begin
......@@ -49,3 +49,26 @@ end
@test model.landscape[2,1].events == []
@test model.landscape[2,2].events == []
end
@testset "Landscape functions" begin
# initialise a simple 6x6 test landscape
landscape = Matrix{Pixel}(undef, 6, 6)
for x in 1:6
for y in 1:6
(x in (1:2) || y in (1:4)) ? lc = Persephone.forest : lc = Persephone.grass
landscape[x,y] = Pixel(lc, 0, [])
end
end
landscape[6,4] = Pixel(Persephone.water, 0, [])
space = GridSpace(size(landscape), periodic=false)
properties = Dict{Symbol,Any}(:landscape=>landscape)
model = AgentBasedModel(Animal, space, properties=properties, warn=false)
# test the distance functions
@test Persephone.distanceto((2,3), model, Persephone.forest) == 0
@test Persephone.distanceto((2,3), model, Persephone.grass) == 2
@test Persephone.distanceto((2,3), model, Persephone.water) == 4
@test Persephone.distanceto((2,3), model, Persephone.soil) == Inf
@test Persephone.distancetoedge((1,1), model) == 4
@test Persephone.distancetoedge((4,4), model) == 1
@test Persephone.distancetoedge((6,6), model) == 2
end
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment