From 498dc494f8f08d04ea6eaea0f73f20936766146f Mon Sep 17 00:00:00 2001
From: Daniel Vedder <daniel.vedder@idiv.de>
Date: Thu, 26 Oct 2023 12:04:17 +0200
Subject: [PATCH] Fixed the memory leak!! :D

closes #12
---
 src/GUI.jl       | 66 +++++++++++++++++++++++-------------------------
 src/main.qml     |  2 +-
 src/variables.jl | 10 ++++----
 3 files changed, 38 insertions(+), 40 deletions(-)

diff --git a/src/GUI.jl b/src/GUI.jl
index 9f03a22..4cab0ca 100644
--- a/src/GUI.jl
+++ b/src/GUI.jl
@@ -50,44 +50,42 @@ function saveoutput()
 end
  
 function render_map(screen)
-    global model, mapimage, landcovermap
+    global model, mapimage
     launching[] && return display(screen, Figure().scene) # blank screen at launch
     println("Updating map")
-    #FIXME repeatedly calling `visualisemap` causes the memory leak
-    # (regardless of whether individuals are plotted or just the map)
-    #mapimage[] = visualisemap(model, date[]-Day(1), landcovermap)
-    empty!(mapimage[])
-    mapimage[] = vismap(model, date[]-Day(1), landcovermap)
-    display(screen, mapimage[])
-end
-
-function vismap(model::AgentBasedModel,date=nothing,landcover=nothing)
-    # load and plot the map
-    # Note: if the landcover map is supplied, it needs to be rotr90'ed
-    isnothing(landcover) && (landcover = rotr90(load(@param(world.landcovermap))))
-    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.scene
-    end
-    isnothing(date) && (date = inds.Date[end])
-    for s in unique(inds.Species)
-        points = @select!(@subset(inds, :Species .== s, :Date .== date),
-                          :X, :Y)
-        # 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(model.landscape)[2] .- :Y)
-        scatter!(f[1,1], Matrix{Float32}(points), markersize=8)
+    if isnothing(mapimage)
+        # I'm reimplementing `visualisemap()` here, because I need to use Observables
+        # to keep the figure updated (if I use multiple plots, I get a memory leak,
+        # see here: https://github.com/MakieOrg/Makie.jl/issues/795)
+        landcover = rotr90(load(@param(world.landcovermap)))
+        mapimage = Figure()
+        ax = Axis(mapimage[1,1])
+        hidedecorations!(ax)
+        image!(mapimage[1,1], landcover)
+        ax.aspect = DataAspect()
+        inds = @lift(@transform!(@subset(model.datatables["individuals"],
+                                         :Date .== $date-Day(1)),
+                                 :Y = size(landcover)[2] .- :Y))
+        on(inds) do i
+            println("Updated `inds`, length $(size(inds[])[1])")
+        end
+        if size(inds[])[1] > 0
+            update_theme!(palette=(color=cgrad(:seaborn_bright,
+                                               length(@param(nature.targetspecies))),),
+                          cycle=[:color])
+            for s in @param(nature.targetspecies)
+                points = @lift(Matrix{Float32}(
+                    @select!(@subset($inds, :Species .== s), :X, :Y)))
+                on(points) do i
+                    println("Updated `points` of species $s")
+                end
+                scatter!(mapimage[1,1], points, markersize=8)
+            end
+        end
     end
-    f.scene
+    display(screen, mapimage.scene)
 end
-
+   
 function render_plot(screen)
     #FIXME Y axis is too small -> lines not shown?
     global model
diff --git a/src/main.qml b/src/main.qml
index 06f179e..6ce7279 100644
--- a/src/main.qml
+++ b/src/main.qml
@@ -204,7 +204,7 @@ Distributed under the MIT license."
 		onUpdateMakie: {
 			dateText.text = Julia.datestring();
 			mapviewport.update();
-			//plotviewport.update();
+			plotviewport.update();
 		}
 		signal showSplash()
 		onShowSplash: {
diff --git a/src/variables.jl b/src/variables.jl
index 01a87ef..baa4e42 100644
--- a/src/variables.jl
+++ b/src/variables.jl
@@ -1,16 +1,17 @@
 ### PersefoneDesktop - a GUI to the Persefone model of agriculture and ecosystems
 ###
-### This file contains all globals and Observables.
+### This file contains all global variables and Observables.
 ###
 
 
-## GUI parameters
+## GUI variables
 
 global model = nothing
-global landcovermap = nothing
 global runsimulation = () -> nothing
-#global newsimulation = () -> nothing
+global newsimulation = () -> nothing
 global timer = nothing
+global mapimage = nothing
+global plotimage = nothing
 
 const configfile = Observable("guiparams.toml")
 const launching = Observable(false)
@@ -21,7 +22,6 @@ const progress = Observable(0.0)
 const delay = Observable(0.5)
 const runbuttontext = Observable(">>")
 const runbuttontip = Observable("Run")
-const mapimage = Observable(Figure().scene)
 
 
 ## configuration parameters
-- 
GitLab