### Persefone.jl - a model of agricultural landscapes and ecosystems in Europe. ### ### This file contains code for the fields that farmers manage. ### """ FarmPlot A struct representing a single field, on which a crop can be grown. """ mutable struct FarmPlot <: ModelAgent const id::Int64 pixels::Vector{Tuple{Int64, Int64}} farmer::Int64 soiltype::SoilType cropstate::AbstractCropState end croptype(f::FarmPlot) = croptype(f.cropstate) cropname(f::FarmPlot) = cropname(croptype(f)) cropheight(f::FarmPlot) = cropheight(f.cropstate) cropcover(f::FarmPlot) = cropcover(f.cropstate) cropyield(f::FarmPlot) = cropyield(f.cropstate) isharvestable(f::FarmPlot) = isharvestable(f.cropstate) function setsoiltype!(f::FarmPlot, soiltype::SoilType) f.soiltype = soiltype setsoiltype!(f.cropstate, soiltype) return nothing end """ stepagent!(farmplot, model) Update a farm plot by one day. """ function stepagent!(farmplot::FarmPlot, model::SimulationModel) stepagent!(farmplot.cropstate, model) end """ sow!(farmplot, model, cropname) Sow the specified crop on the farmplot. """ function sow!(farmplot::FarmPlot, model::SimulationModel, cropname::String) new_croptype = model.crops[cropname] createevent!(model, farmplot.pixels, sowing) if typeof(new_croptype) === typeof(croptype(farmplot)) sow!(farmplot.cropstate, model, cropname) else farmplot.cropstate = makecropstate(new_croptype, model, farmplot.soiltype) end @debug "Farmer $(farmplot.farmer) sowed $(cropname) on farmplot $(farmplot.id)." end """ harvest!(farmplot, model) Harvest the crop of this farmplot. """ function harvest!(farmplot::FarmPlot, model::SimulationModel) createevent!(model, farmplot.pixels, harvesting) harvest!(farmplot.cropstate, model) # TODO: multiply with area to return units of `g` @debug "Farmer $(farmplot.farmer) harvested $(cropname(farmplot)) from farmplot $(farmplot.id)." end """ @sow(cropname) Sow the named crop on the current field. Requires the variables `field` and `model`. """ macro sow(cropname) :(sow!($(esc(:field)), $(esc(:model)), $(esc(cropname)))) end """ @harvest() Harvest the current field. Requires the variables `field` and `model`. """ macro harvest() :(harvest!($(esc(:field)), $(esc(:model)))) end ## UTILITY FUNCTIONS """ isgrassland(farmplot, model) Classify a farmplot as grassland or not (i.e., is the landcover of >80% of its pixels grass?) """ function isgrassland(farmplot::FarmPlot, model::SimulationModel) proportiongrass = count(pos -> landcover(pos, model) == grass, farmplot.pixels) / length(farmplot.pixels) if proportiongrass > 0.8 return true elseif proportiongrass > 0.2 @debug "Unclear classification: farm plot $(farmplot.id) has $(proportiongrass*100)% grass." proportiongrass >= 0.5 && return true end return false end """ averagefieldsize(model) Calculate the average field size in hectares for the model landscape. """ function averagefieldsize(model::SimulationModel) area_sum = sum(@areaof(length(fp.pixels)) for fp in model.farmplots; init=0.0u"m^2") area_avg = area_sum / length(model.farmplots) return area_avg |> ha end """ croptype(model, position) Return the crop at this position, or nothing if there is no crop here (utility wrapper). """ function croptype(pos::Tuple{Int64,Int64}, model::SimulationModel) ismissing(model.landscape[pos...].fieldid) ? nothing : croptype(model.farmplots[model.landscape[pos...].fieldid]) end """ cropname(model, position) Return the name of the crop at this position, or an empty string if there is no crop here (utility wrapper). """ function cropname(pos::Tuple{Int64,Int64}, model::SimulationModel) field = model.landscape[pos...].fieldid ismissing(field) ? "NA" : cropname(model.farmplots[field]) end """ cropheight(model, position) Return the height of the crop at this position, or nothing if there is no crop here (utility wrapper). """ function cropheight(pos::Tuple{Int64,Int64}, model::SimulationModel) ismissing(model.landscape[pos...].fieldid) ? 0.0cm : # TODO: 0.0cm correct here? cropheight(model.farmplots[model.landscape[pos...].fieldid]) end """ cropcover(model, position) Return the crop cover of the crop at this position, or nothing if there is no crop here (utility wrapper). """ function cropcover(pos::Tuple{Int64,Int64}, model::SimulationModel) ismissing(model.landscape[pos...].fieldid) ? 0.0 : # TODO: 0.0 correct here? cropcover(model.farmplots[model.landscape[pos...].fieldid]) end