From ce1ce84a3613726083bc37b22743c1462f3fd142 Mon Sep 17 00:00:00 2001 From: Gabriele Bozzola Date: Thu, 25 Jul 2024 07:06:24 -0700 Subject: [PATCH] Add ClimaExplorer This commit adds an interactive viewer for ClimaAnalysis --- Project.toml | 2 +- climaexplorer/Project.toml | 14 +++++ climaexplorer/assets/interactive.css | 12 ++++ climaexplorer/explorer.jl | 12 ++++ climaexplorer/libexplorer.jl | 88 ++++++++++++++++++++++++++++ 5 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 climaexplorer/Project.toml create mode 100644 climaexplorer/assets/interactive.css create mode 100644 climaexplorer/explorer.jl create mode 100644 climaexplorer/libexplorer.jl diff --git a/Project.toml b/Project.toml index 503d6fa8..45ba6a56 100644 --- a/Project.toml +++ b/Project.toml @@ -26,9 +26,9 @@ GeoMakie = "0.6.5, 0.7" JuliaFormatter = "1" NCDatasets = "0.13.1, 0.14" OrderedCollections = "1.3" +Reexport = "1" SafeTestsets = "0.1" Statistics = "1" -Reexport = "1" Test = "1" julia = "1.9" diff --git a/climaexplorer/Project.toml b/climaexplorer/Project.toml new file mode 100644 index 00000000..d43134e6 --- /dev/null +++ b/climaexplorer/Project.toml @@ -0,0 +1,14 @@ +name = "ClimaExplorer" +authors = ["Gabriele Bozzola "] +version = "0.0.1" + +[deps] +Bonito = "824d6782-a2ef-11e9-3a09-e5662e0c26f8" +CairoMakie = "13f3f980-e62b-5c42-98c6-ff1f3baf88f0" +ClimaAnalysis = "29b5916a-a76c-4e73-9657-3c8fd22e65e6" +GeoMakie = "db073c08-6b98-4ee5-b6a4-5efafb3259c6" + +[compat] +Bonito = "3" +ClimaAnalysis = "0.5.5" +GeoMakie = "0.6.5, 0.7" diff --git a/climaexplorer/assets/interactive.css b/climaexplorer/assets/interactive.css new file mode 100644 index 00000000..3bbcc096 --- /dev/null +++ b/climaexplorer/assets/interactive.css @@ -0,0 +1,12 @@ +.container { + display: flex; +} +.column { + padding: 10px; +} +.left { + flex: 0 1 20%; /* Flex-grow: 0, Flex-shrink: 1, Flex-basis: 20% */ +} +.right { + flex: 1; +} diff --git a/climaexplorer/explorer.jl b/climaexplorer/explorer.jl new file mode 100644 index 00000000..0e9f1ae0 --- /dev/null +++ b/climaexplorer/explorer.jl @@ -0,0 +1,12 @@ +length(ARGS) == 1 || error("Usage: julia --project=climaexplorer explorer.jl ") + +include(joinpath(@__DIR__, "libexplorer.jl")) + +path = first(ARGS) + +app = BonitoApp(path) + +server = Server(app, "0.0.0.0", 8080) +println(server) +route!(server, "/" => app) +wait(server) diff --git a/climaexplorer/libexplorer.jl b/climaexplorer/libexplorer.jl new file mode 100644 index 00000000..a105db7a --- /dev/null +++ b/climaexplorer/libexplorer.jl @@ -0,0 +1,88 @@ +import ClimaAnalysis +import Bonito: Observable, App, Slider, Dropdown, DOM, Server, route!, wait, Asset +import CairoMakie +import GeoMakie + +function BonitoApp(path) + app = App() do + climastyle = Asset(joinpath(@__DIR__, "assets", "interactive.css")) + simdir = ClimaAnalysis.SimDir(path) + variables = ClimaAnalysis.available_vars(simdir) |> collect + vars_menu = Dropdown(variables) + short_name = vars_menu.value.val + # TODO: Add Dropdown menus for reduction and period + + # reductions = ClimaAnalysis.available_reductions(simdir; short_name) |> collect + # reductions_menu = Dropdown(reductions) + # reduction = reductions_menu.value.val + # periods = ClimaAnalysis.available_periods(simdir; short_name, reduction) |> collect + # periods_menu = Dropdown(periods) + + # Set up initial var + reduction = Observable(first(ClimaAnalysis.available_reductions(simdir; short_name))) + period = Observable(first(ClimaAnalysis.available_periods(simdir; short_name, reduction = reduction.val))) + var = get(simdir; short_name, reduction = reduction.val, period = period.val) + time_slider = Slider(ClimaAnalysis.times(var)) + level_slider = Slider(ClimaAnalysis.altitudes(var)) + + var = map(vars_menu.value) do short_name + reduction[] = first(ClimaAnalysis.available_reductions(simdir; short_name)) + period[] = first(ClimaAnalysis.available_periods(simdir; short_name, reduction = reduction.val)) + var = get(simdir; short_name, reduction = reduction.val, period = period.val) + time_slider.values[] = ClimaAnalysis.times(var) + level_slider.values[] = ClimaAnalysis.altitudes(var) + setindex!(time_slider, first(ClimaAnalysis.times(var))) + setindex!(level_slider, first(ClimaAnalysis.altitudes(var))) + var + end + + var = map(time_slider) do time + short_name = vars_menu.value.val + # Trigger level_slidel + setindex!(level_slider, level_slider.value.val) + ClimaAnalysis.slice(get(simdir; short_name, reduction = reduction.val, period = period.val); time, z = level_slider.value.val) + end + + var = map(level_slider) do level + short_name = vars_menu.value.val + ClimaAnalysis.slice(get(simdir; short_name, reduction = reduction.val, period = period.val); time = time_slider.value.val, z = level) + end + + fig = map(var) do sliced_var + fig = CairoMakie.Figure(size=(1200, 760), fontsize=30) + ClimaAnalysis.Visualize.contour2D_on_globe!(fig, sliced_var) + # ClimaAnalysis.Visualize.plot!(fig, sliced_var) + fig + end + + return DOM.div( + DOM.h3("Output: ", path), + DOM.div( + DOM.div( + climastyle, + DOM.h4("Short name"), + vars_menu, + DOM.h4("Reduction: ", reduction), + DOM.h4("Period: ", period), + DOM.h4("Time: ", time_slider.value), + time_slider, + DOM.h4("Altitude: ", level_slider.value), + level_slider; + class = "column left", + ), + DOM.div(fig, class = "column right"), + class = "container" + )) + end + return app +end + +# path = first(ARGS) +path = "../ClimaAtmos.jl/output/diagnostic_edmfx_aquaplanet/output_active/" + +app = BonitoApp(path) + +# server = Server(app, "0.0.0.0", 8080) +println(server) +route!(server, "/" => app) +# wait(server)