diff --git a/NEWS.md b/NEWS.md index 2739258..b51c6fb 100644 --- a/NEWS.md +++ b/NEWS.md @@ -1,11 +1,12 @@ # Release notes -## Unversioned updates +## Version 0.5.18 (2025-11-24) ### Enhancements * Added the option `pre_plot_sub_components` to the `GUI`-constructor to skip preplotting hidden sub components of an area (the option is by default `true`). The components of an `Area` are then plotted on demand (on the `open` functionality). This greatly enhances performance for large cases. * Improved general performance. +* Added two new options to the `GUI` constructor: `simplified_connection_plotting` (default `false`) for controlling connection visualization detail, and `simplify_all_levels` (default `false`) for applying simplification across hierarchical levels. ### Adjustments @@ -13,6 +14,8 @@ * Added the field `visible` to `EnergySystemDesign` and `Connection` on which plots can directly rely on for visibility * Adjust behaviour of `Base.show` on the types `EnergySystemDesign`, `Connection` and `AbstractSystem` to correspond to `Base.show` of their corresponding `AbstractElement`. * Change tests of toggling of colors to be based on a new case having more transmission modes (the case in the new `test/EMI_geography_2.jl` file). +* Make the distance between two way connection linearly dependent on `Δh` instead of a fixed width based on `gui.vars[:two_way_sep_px]`. +* Make the markersize (arrow heads for connections) linearly dependent on `Δh` instead of a fixed size based on `gui.vars[:markersize]` ## Version 0.5.17 (2025-11-19) diff --git a/docs/generate_images.jl b/docs/generate_images.jl index 59dc4fa..693041c 100644 --- a/docs/generate_images.jl +++ b/docs/generate_images.jl @@ -9,6 +9,8 @@ import EnergyModelsGUI: get_component, get_selected_systems, get_name, + get_toggle, + pick_component!, update!, toggle_selection_color!, select_data! @@ -77,7 +79,7 @@ function create_EMI_geography_images() solution_summary(m) # Set folder where visualization info is saved and retrieved - design_path = joinpath(@__DIR__, "design", "EMI", "geography") + design_path = joinpath(docsdir, "design", "EMI", "geography") # Run the GUI gui = GUI( @@ -90,7 +92,7 @@ function create_EMI_geography_images() ) # Create examples.png image - path_to_results = joinpath(@__DIR__, "src", "figures") + path_to_results = joinpath(docsdir, "src", "figures") get_vars(gui)[:path_to_results] = path_to_results get_menu(gui, :axes).selection[] = "All" get_menu(gui, :export_type).selection[] = "png" @@ -105,9 +107,8 @@ function create_EMI_geography_images() # Create EMI_geography.png image oslo_area = get_component(get_root_design(gui), 1) - push!(get_selected_systems(gui), oslo_area) # Manually add to :selected_systems + pick_component!(gui, oslo_area, :topo) update!(gui) - toggle_selection_color!(gui, oslo_area, true) select_data!(gui, "area_exchange") notify(export_button.clicks) mv( @@ -119,11 +120,8 @@ function create_EMI_geography_images() # Create EMI_geography_Oslo.png image notify(open_button.clicks) sub_component = get_component(oslo_area, 5) # fetch node id 5 in Oslo area - selected_systems = get_selected_systems(gui) - empty!(selected_systems) - push!(selected_systems, sub_component) # Manually add to :selected_systems + pick_component!(gui, sub_component, :topo) update!(gui) - toggle_selection_color!(gui, sub_component, true) select_data!(gui, "cap_add") notify(export_button.clicks) mv( diff --git a/docs/make.jl b/docs/make.jl index 45deb3f..a8bb672 100644 --- a/docs/make.jl +++ b/docs/make.jl @@ -7,14 +7,16 @@ const EMB = EnergyModelsBase const EMGUI = EnergyModelsGUI # Copy the NEWS.md file -news = "docs/src/manual/NEWS.md" +pkg_dir = pkgdir(EnergyModelsGUI) +docsdir = joinpath(pkg_dir, "docs") +news = joinpath(docsdir, "src", "manual", "NEWS.md") if isfile(news) rm(news) end -cp("NEWS.md", news) +cp(joinpath(pkg_dir, "NEWS.md"), news) ENV["EMX_TEST"] = true # Set flag for example scripts to check if they are run as part CI -include("generate_images.jl") +include(joinpath(docsdir, "generate_images.jl")) DocMeta.setdocmeta!( EnergyModelsGUI, :DocTestSetup, :(using EnergyModelsGUI); recursive = true, diff --git a/docs/src/figures/EMI_geography.png b/docs/src/figures/EMI_geography.png index 2837ba5..7b43885 100644 Binary files a/docs/src/figures/EMI_geography.png and b/docs/src/figures/EMI_geography.png differ diff --git a/docs/src/figures/EMI_geography_Oslo.png b/docs/src/figures/EMI_geography_Oslo.png index a652c2c..2d7f0a8 100644 Binary files a/docs/src/figures/EMI_geography_Oslo.png and b/docs/src/figures/EMI_geography_Oslo.png differ diff --git a/ext/EMGExt/EMGExt.jl b/ext/EMGExt/EMGExt.jl index ea8485e..12115e1 100644 --- a/ext/EMGExt/EMGExt.jl +++ b/ext/EMGExt/EMGExt.jl @@ -166,7 +166,7 @@ Get the line style for an Transmission `transmission` based on its properties. """ function EMGUI.get_linestyle(gui::GUI, transmission::Transmission) return [ - EMI.has_investment(m) ? EMGUI.get_var(gui, :investment_lineStyle) : :solid for + EMI.has_investment(m) ? EMGUI.get_var(gui, :investment_linestyle) : :solid for m ∈ modes(transmission) ] end diff --git a/src/datastructures.jl b/src/datastructures.jl index 5a53811..7302a9b 100644 --- a/src/datastructures.jl +++ b/src/datastructures.jl @@ -1,7 +1,7 @@ """ AbstractGUIObj -Supertype for EnergyModelsGUI objects representing `Node`s/`Link`s/`Area`s/`Transmission`s. +Supertype for `EnergyModelsGUI` objects representing `Node`s/`Link`s/`Area`s/`Transmission`s. """ abstract type AbstractGUIObj end @@ -15,14 +15,14 @@ struct NothingDesign <: AbstractGUIObj end """ AbstractSystem -Supertype for EnergyModelsGUI objects representing a sub system of a Case. +Supertype for `EnergyModelsGUI` objects representing a sub system of a `Case`. """ abstract type AbstractSystem <: AbstractCase end """ NothingElement <: AbstractElement -Type for representing an empty Element (the "nothing" element). +Type for representing an empty `AbstractElement` (the "nothing" element). """ struct NothingElement <: AbstractElement end @@ -139,6 +139,8 @@ energy system designs in Julia. - **`inv_data::ProcInvData`** stores processed investment data. - **`plots::Vector{Makie.AbstractPlot}`** is a vector with all Makie object associated with this object. +- **`simplified::Observable{Bool}`** indicates whether the system uses a simplified + representation of its plotted connections, observed for changes. """ mutable struct EnergySystemDesign <: AbstractGUIObj system::AbstractSystem @@ -155,6 +157,7 @@ mutable struct EnergySystemDesign <: AbstractGUIObj visible::Observable{Bool} inv_data::ProcInvData plots::Vector{Makie.AbstractPlot} + simplified::Observable{Bool} end function EnergySystemDesign( system::AbstractSystem, @@ -184,7 +187,8 @@ function EnergySystemDesign( file, visible, ProcInvData(), - Any[], + Makie.AbstractPlot[], + Observable(false), ) end @@ -201,30 +205,42 @@ Mutable type for providing a flexible data structure for connections between - **`to::EnergySystemDesign`** is the `EnergySystemDesign` to which the connection is linked to. - **`connection::AbstractElement`** is the EMX connection structure. +- **`parent::EnergySystemDesign`** is the parent EnergySystemDesign of the connection. - **`colors::Vector{RGBA{Float32}}`** is the associated colors of the connection. -- **`visible::Observable{Bool}`** indicates whether the system is visible, observed for changes. - **`inv_data::ProcInvData`** stores processed investment data. -- **`plots::Vector{Makie.AbstractPlot}`** is a vector with all Makie object associated with - this object. +- **`regular_plots::Vector{Makie.AbstractPlot}`** is a vector with + all regular plots associated with the connection. +- **`simplified_plots::Vector{Makie.AbstractPlot}`** is a vector with + all simplified plots associated with the connection. """ mutable struct Connection <: AbstractGUIObj from::EnergySystemDesign to::EnergySystemDesign connection::AbstractElement + parent::EnergySystemDesign colors::Vector{RGBA{Float32}} - visible::Observable{Bool} inv_data::ProcInvData - plots::Vector{Makie.AbstractPlot} + regular_plots::Vector{Makie.AbstractPlot} + simplified_plots::Vector{Makie.AbstractPlot} end function Connection( from::EnergySystemDesign, to::EnergySystemDesign, connection::AbstractElement, + parent::EnergySystemDesign, id_to_color_map::Dict{Any,Any}, - visible::Observable{Bool}, ) colors::Vector{RGBA{Float32}} = get_resource_colors(connection, id_to_color_map) - return Connection(from, to, connection, colors, visible, ProcInvData(), Any[]) + return Connection( + from, + to, + connection, + parent, + colors, + ProcInvData(), + Makie.AbstractPlot[], + Makie.AbstractPlot[], + ) end """ @@ -446,13 +462,6 @@ Returns the `system` field of a `EnergySystemDesign` `design`. """ get_system(design::EnergySystemDesign) = design.system -""" - get_parent(design::EnergySystemDesign) - -Returns the `parent` field of a `EnergySystemDesign` `design`. -""" -get_parent(design::EnergySystemDesign) = design.parent - """ get_element(design::EnergySystemDesign) @@ -535,10 +544,24 @@ EMB.get_time_struct(design::EnergySystemDesign) = EMB.get_time_struct(get_system """ get_ref_element(design::EnergySystemDesign) -Returns the `ref_element` field of a `EnergySystemDesign` `design`. +Returns the `ref_element` field in the `system` field of a `EnergySystemDesign` `design`. """ get_ref_element(design::EnergySystemDesign) = get_ref_element(get_system(design)) +""" + get_plots(design::EnergySystemDesign) + +Returns the `plots` field of a `EnergySystemDesign` `design`. +""" +get_plots(design::EnergySystemDesign) = design.plots + +""" + get_simplified(design::EnergySystemDesign) + +Returns the `simplified` field of a `EnergySystemDesign` `design`. +""" +get_simplified(design::EnergySystemDesign) = design.simplified + """ get_from(conn::Connection) @@ -567,6 +590,43 @@ Returns the `colors` field of a `Connection` `conn`. """ get_colors(conn::Connection) = conn.colors +""" + get_regular_plots(conn::Connection) + +Returns the `regular_plots` field of a `Connection` `conn`. +""" +get_regular_plots(conn::Connection) = conn.regular_plots + +""" + get_simplified_plots(conn::Connection) + +Returns the `simplified_plots` field of a `Connection` `conn`. +""" +get_simplified_plots(conn::Connection) = conn.simplified_plots + +""" + get_simplified(conn::Connection) + +Returns the `simplified` field of the parent `EnergySystemDesign` of a `Connection` `conn`. +""" +get_simplified(conn::Connection) = get_simplified(get_parent(conn)) + +""" + get_plots(conn::Connection) + +Returns the active `plots` field of a `Connection` `conn`. +""" +get_plots(conn::Connection) = + get_simplified(conn)[] ? get_simplified_plots(conn) : get_regular_plots(conn) + +""" + get_plots(conn::Connection, simplified::Bool) + +Returns the `plots` field of a `Connection` `conn` based on the `simplified` flag. +""" +get_plots(conn::Connection, simplified::Bool) = + simplified ? get_simplified_plots(conn) : get_regular_plots(conn) + """ get_inv_times(data::ProcInvData) get_inv_times(design::AbstractGUIObj) @@ -602,18 +662,18 @@ Returns the `inv_data` field of a `AbstractGUIObj` `obj`. get_inv_data(obj::AbstractGUIObj) = obj.inv_data """ - get_plots(obj::AbstractGUIObj) + get_visible(obj::AbstractGUIObj) -Returns the `plots` field of a `AbstractGUIObj` `obj`. +Returns the `visible` field of a `AbstractGUIObj` `obj`. """ -get_plots(obj::AbstractGUIObj) = obj.plots +get_visible(obj::AbstractGUIObj) = obj.visible """ - get_visible(obj::AbstractGUIObj) + get_parent(obj::AbstractGUIObj) -Returns the `visible` field of a `AbstractGUIObj` `obj`. +Returns the `parent` field of a `AbstractGUIObj` `obj`. """ -get_visible(obj::AbstractGUIObj) = obj.visible +get_parent(obj::AbstractGUIObj) = obj.parent """ get_fig(gui::GUI) diff --git a/src/setup_GUI.jl b/src/setup_GUI.jl index 26fca5c..6fd8851 100644 --- a/src/setup_GUI.jl +++ b/src/setup_GUI.jl @@ -46,6 +46,11 @@ to the old EnergyModelsX `case` dictionary. sub-components of areas in the topology design. Setting this to `false` greatly enhances performance for large cases, as the components of an `Area` are then plotted on demand (on the `open` functionality). +- **`simplified_connection_plotting::Bool=false`** toggles whether or not to use a + simplified representation of the connections with a single line for each connection + (instead of multiple lines for multiple transmission modes) in the topology design. +- **`simplify_all_levels::Bool=false`** toggles whether or not to use simplified connection + plotting for all hierarchical levels. !!! warning "Reading model results from CSV-files" Reading model results from a directory (*i.e.*, `model::String` implying that the results @@ -77,6 +82,8 @@ function GUI( enable_data_inspector::Bool = true, use_geomakie::Bool = true, pre_plot_sub_components::Bool = true, + simplified_connection_plotting::Bool = false, + simplify_all_levels::Bool = false, ) # Generate the system topology: @info raw"Setting up the topology design structure" @@ -98,7 +105,6 @@ function GUI( :Δh => Observable(0.05f0), # Sidelength of main box :coarse_coast_lines => coarse_coast_lines, :Δh_px => 50, # Pixel size of a box for nodes - :markersize => 15, # Marker size for arrows in connections :boundary_add => 0.2f0, # Relative to the xlim/ylim-dimensions, expand the axis :line_sep_px => 2, # Separation (in px) between lines for connections :connection_linewidth => 2, # line width of connection lines @@ -107,9 +113,8 @@ function GUI( :linewidth => 1.2, # Width of the line around boxes :parent_scaling => 1.1, # Scale for enlargement of boxes around main boxes for nodes for parent systems :icon_scale => 0.9f0, # scale icons w.r.t. the surrounding box in fraction of Δh - :two_way_sep_px => 10, # No pixels between set of lines for nodes having connections both ways :selection_color => GREEN2, # Colors for box boundaries when selection objects - :investment_lineStyle => Linestyle([1.0, 1.5, 2.0, 2.5] .* 5), # linestyle for investment connections and box boundaries for nodes + :investment_linestyle => Linestyle([1.0, 1.5, 2.0, 2.5] .* 5), # linestyle for investment connections and box boundaries for nodes :path_to_results => path_to_results, # Path to the location where axes[:results] can be exported :plotted_data => [], :periods_labels => periods_labels, @@ -122,6 +127,9 @@ function GUI( :tol => tol, :use_geomakie => use_geomakie, :pre_plot_sub_components => pre_plot_sub_components, + :simplified_connection_plotting => simplified_connection_plotting, + :simplify_all_levels => simplify_all_levels, + :marker_to_box_ratio => 0.4, # Ratio between marker size and `Node` box size :autolimits => Dict( :results_op => true, :results_sc => true, @@ -501,9 +509,20 @@ function create_makie_objects(vars::Dict, design::EnergySystemDesign) justification = :right, ) expand_all_toggle = Makie.Toggle(gridlayout_taskbar[1, 8]; active = vars[:expand_all]) + Makie.Label( + gridlayout_taskbar[1, 9], + "Simplified:"; + halign = :right, + fontsize = vars[:fontsize], + justification = :right, + ) + simplified_toggle = Makie.Toggle( + gridlayout_taskbar[1, 10]; + active = vars[:simplified_connection_plotting], + ) # Add the following to add flexibility - Makie.Label(gridlayout_taskbar[1, 9], " "; tellwidth = false) + Makie.Label(gridlayout_taskbar[1, 11], " "; tellwidth = false) # Add buttons related to the ax_results object (where the optimization results are plotted) Makie.Label( @@ -664,7 +683,8 @@ function create_makie_objects(vars::Dict, design::EnergySystemDesign) ) # Collect all toggles into a dictionary - toggles::Dict{Symbol,Makie.Toggle} = Dict(:expand_all => expand_all_toggle) + toggles::Dict{Symbol,Makie.Toggle} = + Dict(:expand_all => expand_all_toggle, :simplified => simplified_toggle) # Collect all axes into a dictionary axes::Dict{Symbol,Makie.Block} = Dict( diff --git a/src/setup_topology.jl b/src/setup_topology.jl index 0874630..1778b43 100644 --- a/src/setup_topology.jl +++ b/src/setup_topology.jl @@ -55,8 +55,6 @@ function EnergySystemDesign( # Create an iterator for the current system elements = get_children(system) - visible::Observable{Bool} = Observable(level <= 1) - design = EnergySystemDesign( system, id_to_color_map, @@ -69,7 +67,7 @@ function EnergySystemDesign( Observable(BLACK), Observable(:E), file, - visible, + Observable(level == 0), ) # If system contains any components (i.e. !isnothing(elements)) add all components @@ -139,7 +137,7 @@ function EnergySystemDesign( if !isnothing(from) && !isnothing(to) push!( design.connections, - Connection(from, to, element, id_to_color_map, Observable(level == 0)), + Connection(from, to, element, design, id_to_color_map), ) end end diff --git a/src/utils_GUI/GUI_utils.jl b/src/utils_GUI/GUI_utils.jl index 575a931..4f77b7d 100644 --- a/src/utils_GUI/GUI_utils.jl +++ b/src/utils_GUI/GUI_utils.jl @@ -23,13 +23,22 @@ function toggle_selection_color!(gui::GUI, selection::Connection, selected::Bool else colors = get_colors(selection) no_colors = length(colors) + simplified = get_simplified(selection)[] i::Int64 = 1 for plot ∈ plots - if isa(plot.color[], Vector) - plot.color = colors + if simplified + if isa(plot.color[], Vector) + plot.color = [BLACK] + else + plot.color = BLACK + end else - plot.color = colors[((i-1)%no_colors)+1] - i += 1 + if isa(plot.color[], Vector) + plot.color = colors + else + plot.color = colors[((i-1)%no_colors)+1] + i += 1 + end end end end diff --git a/src/utils_GUI/event_functions.jl b/src/utils_GUI/event_functions.jl index b576e69..1532c54 100644 --- a/src/utils_GUI/event_functions.jl +++ b/src/utils_GUI/event_functions.jl @@ -11,6 +11,7 @@ function define_event_functions(gui::GUI) expand_all_toggle = get_toggle(gui, :expand_all) expand_all = get_var(gui, :expand_all) + simplified_toggle = get_toggle(gui, :simplified) # On zooming, make sure all graphics are adjusted acordingly on(ax_topo.finallimits; priority = 10) do finallimits @@ -220,19 +221,19 @@ function define_event_functions(gui::GUI) end # Align horizontally button: Handle click on the align horizontal button - on(get_button(gui, :align_horizontal).clicks; priority = 10) do clicks + on(get_button(gui, :align_horizontal).clicks; priority = 10) do _ align(gui, :horizontal) return Consume(false) end # Align vertically button: Handle click on the align vertical button - on(get_button(gui, :align_vertical).clicks; priority = 10) do clicks + on(get_button(gui, :align_vertical).clicks; priority = 10) do _ align(gui, :vertical) return Consume(false) end # Open button: Handle click on the open button (open a sub system) - on(get_button(gui, :open).clicks; priority = 10) do clicks + on(get_button(gui, :open).clicks; priority = 10) do _ if !isempty(get_selected_systems(gui)) get_vars(gui)[:expand_all] = false component = get_selected_systems(gui)[end] # Choose the last selected node @@ -243,10 +244,11 @@ function define_event_functions(gui::GUI) visible = false, expand_all, ) - if isa(component, EnergySystemDesign) && sub_plots_empty(component) + if sub_plots_empty(component) initialize_plot!(gui, component) end gui.design = component + simplified_toggle.active[] = get_simplified(component)[] plot_design!( gui, get_design(gui); @@ -262,11 +264,12 @@ function define_event_functions(gui::GUI) end # Navigate up button: Handle click on the navigate up button (go back to the root_design) - on(get_button(gui, :up).clicks; priority = 10) do clicks + on(get_button(gui, :up).clicks; priority = 10) do _ if !isa(get_parent(get_system(gui)), NothingElement) get_vars(gui)[:expand_all] = expand_all_toggle.active[] plot_design!(gui, get_design(gui); visible = false, expand_all) gui.design = get_root_design(gui) + simplified_toggle.active[] = get_simplified(get_design(gui))[] plot_design!(gui, get_design(gui); visible = true, expand_all) update_title!(gui) adjust_limits!(gui) @@ -351,14 +354,21 @@ function define_event_functions(gui::GUI) return Consume(false) end + # Toggle simplified connection plotting + on(simplified_toggle.active; priority = 10) do val + design = get_design(gui) + toggle_simplified!(gui, design, val) + return Consume(false) + end + # Save button: Handle click on the save button (save the altered coordinates) - on(get_button(gui, :save).clicks; priority = 10) do clicks + on(get_button(gui, :save).clicks; priority = 10) do _ save_design(get_design(gui)) return Consume(false) end # Reset button: Reset view to the original view - on(get_button(gui, :reset_view).clicks; priority = 10) do clicks + on(get_button(gui, :reset_view).clicks; priority = 10) do _ adjust_limits!(gui) notify(ax_topo.finallimits) return Consume(false) diff --git a/src/utils_GUI/topo_axis_utils.jl b/src/utils_GUI/topo_axis_utils.jl index a698493..989ca93 100644 --- a/src/utils_GUI/topo_axis_utils.jl +++ b/src/utils_GUI/topo_axis_utils.jl @@ -20,6 +20,15 @@ function pixel_to_data(gui::GUI, pixel_size::Real) return Point2f(pixel_size * x_factor, pixel_size * y_factor) end +""" + data_to_pixel(gui::GUI, data_size::Real) + +Convert `data_size` (in x- and y-direction) to pixel sizes in design object `gui`. +""" +function data_to_pixel(gui::GUI, data_size::Real) + return data_size / l2_norm(pixel_to_data(gui, 1.0)) +end + """ update_distances!(gui::GUI) @@ -124,6 +133,9 @@ Initialize the plot of the topology of design object `gui` given an EnergySystem `design`. """ function initialize_plot!(gui::GUI, design::EnergySystemDesign) + simplified = get_var(gui, :simplified_connection_plotting) + simplify = get_var(gui, :simplify_all_levels) || (design == get_root_design(gui)) + get_simplified(design)[] = simplified && simplify for component ∈ get_components(design) if get_var(gui, :pre_plot_sub_components) initialize_plot!(gui, component) @@ -151,9 +163,26 @@ function plot_design!( if get_design(gui) == design update_distances!(gui) end - components_and_connections = vcat(get_components(design), get_connections(design)) - for component ∈ components_and_connections - component.visible[] = visible + get_visible(design)[] = visible +end + +""" + toggle_simplified!(gui::GUI, design::EnergySystemDesign, val::Bool) + +Toggle simplified connection plotting for EnergySystemDesign `design` in GUI `gui`. +""" +function toggle_simplified!(gui::GUI, design::EnergySystemDesign, val::Bool) + get_vars(gui)[:simplified_connection_plotting] = val + clear_selection!(gui, :topo) + get_simplified(design)[] = val + connections = get_connections(design) + if !isempty(connections) && isempty(get_plots(connections[1], val)) # create alternative plots + connect!(gui, design) + end + if get_vars(gui)[:expand_all] + for component ∈ get_components(design) + toggle_simplified!(gui, component, val) + end end end @@ -187,8 +216,22 @@ end When a boolean argument `two_way` is specified, draw the lines in both directions. """ function connect!(gui::GUI, connection::Connection, two_way::Bool) - colors = get_colors(connection) - no_colors = length(colors) + linestyles = get_linestyle(gui, connection) + simplified = get_simplified(connection) + parent = get_parent(connection) + if simplified[] + colors = [BLACK] + no_colors = 1 + + # If any of the modes contains investments, use investment linestyle + inv_linestyle = get_var(gui, :investment_linestyle) + linestyle = + any(style == inv_linestyle for style ∈ linestyles) ? [inv_linestyle] : [:solid] + else + colors = get_colors(connection) + no_colors = length(colors) + linestyle = linestyles + end # Create an arrow to highlight the direction of the energy flow l::Float32 = 1.0f0 # length of the arrow @@ -204,20 +247,18 @@ function connect!(gui::GUI, connection::Connection, two_way::Bool) end # Allocate and store objects - linestyle = get_linestyle(gui, connection) linewidth = get_var(gui, :connection_linewidth) - markersize = get_var(gui, :markersize) - two_way_sep_px = get_var(gui, :two_way_sep_px) line_sep_px = get_var(gui, :line_sep_px) parent_scaling = get_var(gui, :parent_scaling) + marker_to_box_ratio = get_var(gui, :marker_to_box_ratio) - from_xy = connection.from.xy - to_xy = connection.to.xy + from_xy = get_xy(get_from(connection)) + to_xy = get_xy(get_to(connection)) Δh = get_var(gui, :Δh) triple = @lift begin pts_lines = Vector{Vector{Point2f}}(fill(fill(Point2f(0.0f0, 0.0f0), 2), no_colors)) - markersize_length::Float32 = l2_norm(pixel_to_data(gui, markersize)) + markersize_length::Float32 = marker_to_box_ratio * $Δh linewidth_length::Float32 = l2_norm(pixel_to_data(gui, linewidth)) xy_1::Point2f = $from_xy @@ -234,13 +275,13 @@ function connect!(gui::GUI, connection::Connection, two_way::Bool) dirθ::Point2f = Point2f(cosθ, sinθ) Δ::Float32 = $Δh / 2 # half width of a box - if !isempty(get_components(connection.from)) + if !isempty(get_components(get_from(connection))) Δ *= parent_scaling end Δ += linewidth_length / 2 lines_shift::Point2f = (linewidth_length + l2_norm(pixel_to_data(gui, line_sep_px))) * dirϕ - two_way_sep::Point2f = l2_norm(pixel_to_data(gui, two_way_sep_px)) * dirϕ + two_way_sep::Point2f = Δ / 2 * dirϕ xy_midpoint::Point2f = xy_2 + (no_colors - 1) / 2 * lines_shift if two_way # separate the opposite directed lines by two_way_sep xy_midpoint += two_way_sep / 2 @@ -274,6 +315,10 @@ function connect!(gui::GUI, connection::Connection, two_way::Bool) θs = @lift fill($triple[2], no_colors) ax = get_ax(gui, :topo) + visible = get_visible(parent) + simplified_initial::Bool = simplified[] + visible_plot = @lift $visible && ($simplified == simplified_initial) + markersize = @lift data_to_pixel(gui, marker_to_box_ratio * $Δh) sctr = scatter!( ax, xy_midpoints; @@ -283,7 +328,7 @@ function connect!(gui::GUI, connection::Connection, two_way::Bool) color = colors, inspectable = false, depth_shift = get_var(gui, :depth_shift_lines), - visible = get_visible(connection), + visible = visible_plot, ) sctr.kw[:EMGUI_obj] = connection push!(get_plots(connection), sctr) @@ -299,7 +344,7 @@ function connect!(gui::GUI, connection::Connection, two_way::Bool) inspector_label = (self, i, p) -> get_hover_string(connection), inspectable = true, depth_shift = get_var(gui, :depth_shift_lines), - visible = get_visible(connection), + visible = visible_plot, ) lns.kw[:EMGUI_obj] = connection push!(get_plots(connection), lns) @@ -329,7 +374,7 @@ get_linestyle(gui::GUI, design::EnergySystemDesign) = get_linestyle(gui, design. function get_linestyle(gui::GUI, system::AbstractSystem) node = get_parent(system) if isa(node, EMB.Node) && EMI.has_investment(node) - return get_var(gui, :investment_lineStyle) + return get_var(gui, :investment_linestyle) end return :solid end @@ -349,11 +394,11 @@ function get_linestyle(gui::GUI, connection::Connection) # For Links, simply use dashed style if from or to node has investments no_lines = length(get_colors(connection)) linestyle::Union{Symbol,Makie.Linestyle} = get_linestyle(gui, connection.from) - if linestyle == get_var(gui, :investment_lineStyle) + if linestyle == get_var(gui, :investment_linestyle) return fill(linestyle, no_lines) end linestyle = get_linestyle(gui, connection.to) - if linestyle == get_var(gui, :investment_lineStyle) + if linestyle == get_var(gui, :investment_linestyle) return fill(linestyle, no_lines) end return fill(:solid, no_lines) @@ -397,7 +442,7 @@ function draw_box!(gui::GUI, design::EnergySystemDesign) linestyle = linestyle, depth_shift = get_var(gui, :depth_shift_components), stroke_depth_shift = get_var(gui, :depth_shift_components) - 1.0f-5, - visible = get_visible(design), + visible = get_visible(get_parent(design)), ) # Create a white background rectangle to hide lines from connections add_inspector_to_poly!(white_rect2, (self, i, p) -> get_hover_string(design)) @@ -419,7 +464,7 @@ function draw_box!(gui::GUI, design::EnergySystemDesign) linestyle = linestyle, depth_shift = get_var(gui, :depth_shift_components), stroke_depth_shift = get_var(gui, :depth_shift_components) - 1.0f-5, - visible = get_visible(design), + visible = get_visible(get_parent(design)), ) # Create a white background rectangle to hide lines from connections add_inspector_to_poly!(white_rect, (self, i, p) -> get_hover_string(design)) @@ -502,7 +547,7 @@ function draw_icon!(gui::GUI, design::EnergySystemDesign) inspectable = true, depth_shift = get_var(gui, :depth_shift_components), stroke_depth_shift = get_var(gui, :depth_shift_components) - 1.0f-5, - visible = get_visible(design), + visible = get_visible(get_parent(design)), ) add_inspector_to_poly!(polys, (self, i, p) -> get_hover_string(design)) polys.kw[:EMGUI_obj] = design @@ -525,7 +570,7 @@ function draw_icon!(gui::GUI, design::EnergySystemDesign) strokewidth = get_var(gui, :linewidth), depth_shift = get_var(gui, :depth_shift_components), stroke_depth_shift = get_var(gui, :depth_shift_components) - 1.0f-5, - visible = get_visible(design), + visible = get_visible(get_parent(design)), ) add_inspector_to_poly!( @@ -549,7 +594,7 @@ function draw_icon!(gui::GUI, design::EnergySystemDesign) inspectable = true, inspector_label = (self, i, p) -> get_hover_string(design), depth_shift = get_var(gui, :depth_shift_components), - visible = get_visible(design), + visible = get_visible(get_parent(design)), ) icon_image.kw[:EMGUI_obj] = design push!(get_plots(design), icon_image) @@ -630,7 +675,7 @@ function draw_label!(gui::GUI, component::EnergySystemDesign) inspectable = false, color = has_invested(component) ? RED : BLACK, depth_shift = get_var(gui, :depth_shift_components), - visible = get_visible(component), + visible = get_visible(get_parent(component)), ) label_text.kw[:EMGUI_obj] = component push!(get_plots(component), label_text) diff --git a/test/EMI_geography_2.jl b/test/EMI_geography_2.jl index 1842406..75c5ed3 100644 --- a/test/EMI_geography_2.jl +++ b/test/EMI_geography_2.jl @@ -279,6 +279,8 @@ function run_case_EMI_geography_2() scale_tot_opex = true, scale_tot_capex = false, pre_plot_sub_components = false, + simplified_connection_plotting = true, + simplify_all_levels = true, ) return case, model, m, gui diff --git a/test/test_interactivity.jl b/test/test_interactivity.jl index e997086..b2f9500 100644 --- a/test/test_interactivity.jl +++ b/test/test_interactivity.jl @@ -1,23 +1,3 @@ -import EnergyModelsGUI: - get_component, - toggle_selection_color!, - get_plots, - get_selection_color, - get_selected_systems, - get_selected_plots, - get_visible_data, - get_var, - get_xy, - get_toggle, - get_ax, - update_info_box!, - update_available_data_menu!, - update_sub_system_locations!, - get_design, - get_vis_plots, - get_plotted_data, - select_data! - # Load case 7 for testing case, model, m, gui = run_case() @@ -39,6 +19,9 @@ axes_menu = get_menu(gui, :axes) pin_plot_button = get_button(gui, :pin_plot) +expand_all_toggle = get_toggle(gui, :expand_all) +simplified_toggle = get_toggle(gui, :simplified) + # Test specific GUI functionalities @testset "Test interactivity" verbose = true begin op_cost = [3371970.00359, 5382390.00598, 2010420.00219] @@ -64,27 +47,92 @@ pin_plot_button = get_button(gui, :pin_plot) @testset "get_toggle(gui,:expand_all).active" begin # Check that sub-components are initially not plotted (as pre_plot_sub_components = false) @test all(isempty(get_plots(component)) for component ∈ get_components(area1)) - get_toggle(gui, :expand_all).active = true + expand_all_toggle.active = true # Test if node n_El 1 became invisible - get_toggle(gui, :expand_all).active = false + expand_all_toggle.active = false n_el_1 = get_component(area1, "El 1") # fetch the n_El 1 node # Test if node n_El 1 became invisible @test !get_plots(n_el_1)[1].visible[] # Test if node n_El 1 became visible again - get_toggle(gui, :expand_all).active = true + expand_all_toggle.active = true @test get_plots(n_el_1)[1].visible[] # Check that that sub-components are now plotted @test all(!isempty(get_plots(component)) for component ∈ get_components(area1)) end + # Test simplified toggle functionality + @testset "get_toggle(gui,:simplified).active" begin + # Check that simplified plots are initially not plotted + @test all(isempty(get_simplified_plots(connection)) for connection ∈ connections) + @test all( + isempty(get_simplified_plots(connection)) for component ∈ components for + connection ∈ get_connections(component) + ) + + # Also check that all colors are regular colors + test_all_connection_colors(gui, :regular_colors) + + # Activate simplified view (for expanded view - simplified for all levels) + simplified_toggle.active = true + test_all_connection_colors(gui, :black) + + # Deactivate simplified view + simplified_toggle.active = false + test_all_connection_colors(gui, :regular_colors) + + # deexpand + expand_all_toggle.active = false + pick_component!(gui, connections[1], :topo) + update!(gui) + simplified_toggle.active = true + + # Check that simplified plots are used only on the current level + test_connections_colors(gui, connections, :black) + test_connections_colors(gui, components, :regular_colors) + + pick_component!(gui, area1, :topo) + notify(get_button(gui, :open).clicks) + test_connections_colors(gui, get_connections(area1), :regular_colors) + simplified_toggle.active = true + test_connections_colors(gui, get_connections(area1), :black) + + # Test that the previous toggling did not affect other areas + for area ∈ [area2, area3, area4] + notify(get_button(gui, :up).clicks) + pick_component!(gui, area, :topo) + notify(get_button(gui, :open).clicks) + test_connections_colors(gui, get_connections(area), :regular_colors) + update!(gui) + end + end + + _, _, _, gui_2 = run_case_EMI_geography_2() + design_2 = get_root_design(gui_2) + components_2 = get_components(design_2) + connections_2 = get_connections(design_2) + simplified_toggle_2 = get_toggle(gui_2, :simplified) + + # Test simplify_all_levels=true functionality + @testset "Test simplify_all_levels=true functionality" begin + # Check that simplified plots are used on all levels + test_all_connection_colors(gui_2, :black) + for area ∈ components_2 + pick_component!(gui_2, area, :topo) + notify(get_button(gui_2, :open).clicks) + test_connections_colors(gui_2, get_connections(area), :black) + simplified_toggle_2.active = false + test_connections_colors(gui_2, get_connections(area), :regular_colors) + notify(get_button(gui_2, :up).clicks) + end + simplified_toggle_2.active = false + end + # Test color toggling @testset "Toggle colors" begin - _, _, _, gui_2 = run_case_EMI_geography_2() - design_2 = get_root_design(gui_2) oslo = get_component(design_2, 1) pick_component!(gui_2, get_plots(oslo)[1], :topo) update!(gui_2) @@ -103,24 +151,12 @@ pin_plot_button = get_button(gui, :pin_plot) notify(get_button(gui_2, :up).clicks) # Go back to the top level connection1 = get_connections(design_2)[5] # fetch the Oslo - Trondheim transmission - plt_connection1 = get_plots(connection1) - pick_component!(gui_2, plt_connection1[2], :topo) + pick_component!(gui_2, get_plots(connection1)[2], :topo) update!(gui_2) - @test all( - all(plot.color[] .== get_selection_color(gui_2)) for plot ∈ plt_connection1 - ) + test_connection_colors(gui_2, connection1, :selection_color) pick_component!(gui_2, nothing, :topo) # deselect update!(gui_2) - i::Int64 = 1 - no_colors::Int64 = length(connection1.colors) - for plot ∈ plt_connection1 - if isa(plot.color[], Vector) - @test plot.color[] == connection1.colors - else - @test plot.color[] == connection1.colors[((i-1)%no_colors)+1] - i += 1 - end - end + test_connection_colors(gui_2, connection1, :regular_colors) end # Test the open button functionality @@ -315,6 +351,8 @@ pin_plot_button = get_button(gui, :pin_plot) "Topo" => ["svg", "lp", "mps"], ) @testset "get_button(gui,:export).clicks" begin + tmpdir = mktempdir(testdir; prefix = "exported_files_") + get_vars(gui)[:path_to_results] = tmpdir path = get_var(gui, :path_to_results) # Loop through all combinations of export options diff --git a/test/utils.jl b/test/utils.jl index 0b2210d..1054aa8 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -1,12 +1,36 @@ import EnergyModelsGUI: get_root_design, + get_component, get_components, get_connections, + get_selection_color, + get_ref_element, + get_element, get_menu, get_button, update!, + BLACK, + Connection, pick_component!, - clear_selection! + clear_selection!, + toggle_selection_color!, + get_plots, + get_selected_systems, + get_selected_plots, + get_visible_data, + get_simplified_plots, + get_vars, + get_var, + get_xy, + get_toggle, + get_ax, + update_info_box!, + update_available_data_menu!, + update_sub_system_locations!, + get_design, + get_vis_plots, + get_plotted_data, + select_data! """ run_through_all(gui::GUI) @@ -99,3 +123,52 @@ function run_through_menu( end end end + +function test_connection_colors(gui::GUI, connection::Connection, type::Symbol) + plt_connection = get_plots(connection) + if type == :black || type == :selection_color + expected_color = type == :black ? BLACK : get_selection_color(gui) + + @test all( + all(plot.color[] .== expected_color) for plot ∈ plt_connection + ) + elseif type == :regular_colors + i::Int64 = 1 + no_colors::Int64 = length(connection.colors) + for plot ∈ plt_connection + if isa(plot.color[], Vector) + @test plot.color[] == connection.colors + else + @test plot.color[] == connection.colors[((i-1)%no_colors)+1] + i += 1 + end + end + end +end + +function test_connections_colors(gui::GUI, connections::Vector{Connection}, type::Symbol) + for connection ∈ connections + test_connection_colors(gui, connection, type) + end +end + +function test_connections_colors( + gui::GUI, + components::Vector{EnergySystemDesign}, + type::Symbol, +) + for component ∈ components + test_connections_colors(gui, get_connections(component), type) + end +end + +function test_all_connection_colors(gui::GUI, type::Symbol) + design = get_root_design(gui) + components = get_components(design) + connections = get_connections(design) + + test_connections_colors(gui, connections, type) + for component ∈ components + test_connections_colors(gui, get_connections(component), type) + end +end