### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe. ### ### This file visualises model output using Makie. ### """ visualisemap(model, date, landcover) Draw the model's land cover map and plot all individuals as points on it at the specified date. If no date is passed, use the last date for which data are available. Optionally, you can pass a landcover map image (this is needed to reduce the frequency of disk I/O for Persefone Desktop). Returns a Makie figure object. """ function visualisemap(model::SimulationModel,date=nothing,landcover=nothing) # load and plot the map # Note: if the landcover map is supplied, it needs to be rotr90'ed if isnothing(landcover) lcm = joinpath(@param(world.mapdirectory), @param(world.landcovermap)) landcover = rotr90(load(lcm)) end f = Figure() ax = Axis(f[1,1]) hidedecorations!(ax) image!(f[1,1], landcover) ax.aspect = DataAspect() # check if there are individuals and plot them inds = model.datatables["individuals"] if iszero(size(inds)[1]) @debug "No individual data to map" return f end isnothing(date) && (date = inds.Date[end]) #XXX other colour schemes: :tab10, :Accent_8, :Dark2_8, :Paired_12, :Set1_9 # https://juliagraphics.github.io/ColorSchemes.jl/stable/catalogue/ ncolors = max(2, length(@param(nature.targetspecies))) update_theme!(palette=(color=cgrad(:seaborn_bright, ncolors),), cycle=[:color]) for s in @param(nature.targetspecies) points = @select!(@subset(inds, :Species .== s, :Date .== date), :X, :Y) iszero(size(points)[1]) && continue # The origin in Makie is in the bottom-left rather than in the top-left as # on the model map, so we have to invert the Y coordinates @transform!(points, :Y = size(landcover)[2] .- :Y) scatter!(f[1,1], Matrix{Float32}(points), markersize=8) end f end """ populationtrends(model) Plot a line graph of population sizes of each species over time. Returns a Makie figure object. """ function populationtrends(model::SimulationModel) pops = model.datatables["populations"] ncolors = max(2, length(@param(nature.targetspecies))) update_theme!(palette=(color=cgrad(:seaborn_bright, ncolors),), cycle=[:color]) f = Figure() dates = @param(core.startdate):@param(core.enddate) axlimits = (1, length(dates), 0, maximum(pops[!,:Abundance])) ax = Axis(f[1,1], xlabel="Date", ylabel="Population size", limits=axlimits, xticks=gettickmarks(dates)) for s in @param(nature.targetspecies) points = @select!(@subset(pops, :Species .== s), :Abundance) iszero(size(points)[1]) && continue lines!(f[1,1], Vector{Float32}(points.Abundance), linewidth=3, label=s) end size(pops)[1] > 0 && axislegend("Species"; position=:lt) f end """ gettickmarks(dates) Given a vector of dates, construct a selection to use as tick mark locations. Helper function for `[populationtrends](@ref)` """ function gettickmarks(dates) ticks = (Int[], String[]) for i in 1:length(dates) if Day(dates[i]) == Day(1) push!(ticks[1], i) push!(ticks[2], Dates.format(dates[i], "u yy")) end end while length(ticks[1]) > 12 deleteat!(ticks[1], 2:2:length(ticks[1])) deleteat!(ticks[2], 2:2:length(ticks[2])) end ticks end #XXX add animation with record()? https://docs.makie.org/stable/explanations/animation/