diff --git a/NEWS.md b/NEWS.md index b51c6fb..15e7f4b 100644 --- a/NEWS.md +++ b/NEWS.md @@ -2,6 +2,10 @@ ## Version 0.5.18 (2025-11-24) +### Bugfix + +* Fix missing distinction between different `TransmissionMode`s in the "data"-menu by including the `id` field in parentheses. + ### 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. @@ -16,6 +20,8 @@ * 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]` +* Also expand `TransmissionMode`s in the info-box. +* For the labeling, use square brackets around indices in the string construction. ## Version 0.5.17 (2025-11-19) diff --git a/docs/src/figures/EMI_geography.png b/docs/src/figures/EMI_geography.png index 7b43885..6cbf0c1 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 2d7f0a8..d980b9e 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 12115e1..73f1c75 100644 --- a/ext/EMGExt/EMGExt.jl +++ b/ext/EMGExt/EMGExt.jl @@ -27,17 +27,11 @@ Returns the `Transmission`s of a `SystemGeo` `system`. EMG.get_transmissions(system::EMGUI.SystemGeo) = EMGUI.get_connections(system) """ - get_modes(system::EMGUI.SystemGeo) + EMG.modes(conn::EMGUI.Connection) -Get all transmission modes of a `SystemGeo` `system`. +Returns an array of the transmission modes for a `Connection` `conn`. """ -function get_modes(system::EMGUI.SystemGeo) - transmission_modes = TransmissionMode[] - for t ∈ get_transmissions(system) - append!(transmission_modes, modes(t)) - end - return transmission_modes -end +EMG.modes(conn::EMGUI.Connection) = EMG.modes(EMGUI.get_element(conn)) ############################################################################################ ## From datastructures.jl @@ -85,7 +79,7 @@ function EMGUI.get_plotables(system::EMGUI.SystemGeo) get_nodes(system), get_links(system), get_areas(system), - get_modes(system), + modes(get_transmissions(system)), ) end @@ -194,4 +188,47 @@ end Map types to header symbols for saving results. """ EMGUI._type_to_header(::Type{<:TransmissionMode}) = :element + +############################################################################################ +## From info_axis_utils.jl +""" + print_nested_structure!( + element::Vector{<:TransmissionMode}, + io::IOBuffer, + indent::Int64, + vector_limit::Int64, + show_the_n_last_elements::Int64, + ) + +Appends the nested structure of element in a nice format to the `io` buffer for `element`. +The parameter `indent` tracks the indentation level, the parameter `vector_limit` is used +to truncate large vectors and `show_the_n_last_elements` specifies how many of the last elements to show. +""" +function EMGUI.print_nested_structure!( + element::Vector{<:TransmissionMode}, + io::IOBuffer, + indent::Int64, + vector_limit::Int64, + show_the_n_last_elements::Int64, +) + indent += 1 + indent_str::String = EMGUI.create_indent_string(indent) + for (i, field1) ∈ enumerate(element) + if i == vector_limit + 1 + println(io, indent_str, "...") + continue + end + if i <= vector_limit || i > length(element) - show_the_n_last_elements + type = typeof(field1) + println(io, indent_str, i, " (", type, "):") + EMGUI.print_nested_structure!( + field1, + io, + indent, + vector_limit, + show_the_n_last_elements, + ) + end + end +end end diff --git a/src/utils_GUI/GUI_utils.jl b/src/utils_GUI/GUI_utils.jl index 4f77b7d..292bf86 100644 --- a/src/utils_GUI/GUI_utils.jl +++ b/src/utils_GUI/GUI_utils.jl @@ -644,22 +644,30 @@ function update_descriptive_names!(gui::GUI) end """ - select_data!(gui::GUI, name::String) + select_data!(gui::GUI, name::String; selection::Vector = Any[]) -Select the data with name `name` from the `available_data` menu. +Select the data with name `name` from the `available_data` menu. If `selection` is provided, +it is used to further specify which data to select. """ -function select_data!(gui::GUI, name::String) +function select_data!(gui::GUI, name::String; selection::Vector = Any[]) # Fetch the available data menu object menu = get_menu(gui, :available_data) - # Fetch all menu options - available_data = [get_name(x[2]) for x ∈ collect(menu.options[])] + items = collect(menu.options[]) # Find menu number for data with name `name` - i_selected = findfirst(x -> x == name, available_data) + if isempty(selection) + i_selected = findfirst(x -> get_name(x[2]) == name, items) + else + i_selected = findfirst( + x -> get_name(x[2]) == name && issubset(selection, get_selection(x[2])), + items, + ) + end # Select data menu.i_selected = i_selected + return nothing end """ diff --git a/src/utils_GUI/info_axis_utils.jl b/src/utils_GUI/info_axis_utils.jl index bd8b475..743d638 100644 --- a/src/utils_GUI/info_axis_utils.jl +++ b/src/utils_GUI/info_axis_utils.jl @@ -1,3 +1,16 @@ +Expandable::Type = Union{ + Vector, + Dict, + EMB.Node, + Resource, + Link, + TimeStructure, + Data, + AbstractInvData, + Investment, + LifetimeMode, + TimeProfile, +} """ update_info_box!(gui::GUI, element) @@ -10,114 +23,148 @@ function update_info_box!(gui::GUI, element) return nothing end io = IOBuffer() - print_nested_structure!( - element, - io; - vector_limit = 5, - show_the_n_last_elements = 1, - ) + type = typeof(element) + if isa(element, Dict) || isa(element, Vector) + println(io, type) + else + println(io, element, " (", type, ")") + end + print_nested_structure!(element, io, 0, 5, 1) info_text[] = String(take!(io)) end +""" + create_indent_string(indent::Int64) + +Create an indentation string based on the `indent` level. +""" +create_indent_string(indent::Int64) = " "^indent + """ print_nested_structure!( element, - io::IOBuffer; - indent::Int64=0, - vector_limit::Int64=typemax(Int64), + io::IOBuffer, + indent::Int64, + vector_limit::Int64, + show_the_n_last_elements::Int64, ) -Appends the nested structure of element in a nice format to the io buffer. The -parameter `vector_limit` is used to truncate large vectors. +Appends the nested structure of element in a nice format to the `io` buffer for `element`. +The parameter `indent` tracks the indentation level, the parameter `vector_limit` is used +to truncate large vectors and `show_the_n_last_elements` specifies how many of the last elements to show. """ function print_nested_structure!( - element, - io::IOBuffer; - indent::Int64 = 0, - vector_limit::Int64 = typemax(Int64), - show_the_n_last_elements::Int64 = 3, + element::Any, + io::IOBuffer, + indent::Int64, + vector_limit::Int64, + show_the_n_last_elements::Int64, ) - if indent == 0 - type = typeof(element) - if isa(element, Dict) || isa(element, Vector) - println(io, type) + indent += 1 + indent_str::String = create_indent_string(indent) + for field1 ∈ fieldnames(typeof(element)) + value1 = getfield(element, field1) + if isa(value1, Expandable) + println(io, indent_str, field1, " (", typeof(value1), "):") + print_nested_structure!( + value1, + io, + indent, + vector_limit, + show_the_n_last_elements, + ) else - println(io, element, " (", type, ")") + if isa(value1, OperationalProfile) && + !isa(value1, FixedProfile) && + length(value1.vals) > vector_limit + # Truncate large vectors + println(io, indent_str, field1, ": ", typeof(value1)) + else + println(io, indent_str, field1, ": ", value1) + end end end + return nothing +end +function print_nested_structure!( + element::Dict, + io::IOBuffer, + indent::Int64, + vector_limit::Int64, + show_the_n_last_elements::Int64, +) indent += 1 - indent_str::String = " "^indent - expandable::Union = Union{ - Vector, - Dict, - EMB.Node, - Resource, - Link, - TimeStructure, - Data, - AbstractInvData, - Investment, - LifetimeMode, - TimeProfile, - } - if isa(element, Vector) - if eltype(element) <: expandable - for (i, field1) ∈ enumerate(element) - if i == vector_limit + 1 - println(io, indent_str, "...") - continue - end - if i <= vector_limit || i > length(element) - show_the_n_last_elements - type = typeof(field1) - if isa(field1, expandable) - println(io, indent_str, i, " (", type, "):") - print_nested_structure!(field1, io; indent, vector_limit) - else - println(io, indent_str, i, ": ", type, "(", field1, ")") - end - end - end + indent_str::String = create_indent_string(indent) + for field1 ∈ keys(element) + if isa(element[field1], Expandable) + println(io, indent_str, field1, " (", typeof(element[field1]), "):") + print_nested_structure!( + element[field1], + io, + indent, + vector_limit, + show_the_n_last_elements, + ) else - print(io, indent_str, "[") - for (i, field1) ∈ enumerate(element) - if i == vector_limit + 1 - print(io, " ... ") - continue - end - if i <= vector_limit || i > length(element) - show_the_n_last_elements - print(io, field1) - if i != length(element) - print(io, ", ") - end - end - end - println(io, "]") + println(io, indent_str, field1, " => ", element[field1]) + end + end +end +function print_nested_structure!( + element::Vector{T}, + io::IOBuffer, + indent::Int64, + vector_limit::Int64, + show_the_n_last_elements::Int64, +) where {T<:Expandable} + indent += 1 + indent_str::String = create_indent_string(indent) + for (i, field1) ∈ enumerate(element) + if i == vector_limit + 1 + println(io, indent_str, "...") + continue end - elseif isa(element, Dict) - for field1 ∈ keys(element) - if isa(element[field1], expandable) - println(io, indent_str, field1, " (", typeof(element[field1]), "):") - print_nested_structure!(element[field1], io; indent, vector_limit) + if i <= vector_limit || i > length(element) - show_the_n_last_elements + type = typeof(field1) + if isa(field1, Expandable) + println(io, indent_str, i, " (", type, "):") + print_nested_structure!( + field1, + io, + indent, + vector_limit, + show_the_n_last_elements, + ) else - println(io, indent_str, field1, " => ", element[field1]) + println(io, indent_str, i, ": ", type, "(", field1, ")") end end - else - for field1 ∈ fieldnames(typeof(element)) - value1 = getfield(element, field1) - if isa(value1, expandable) - println(io, indent_str, field1, " (", typeof(value1), "):") - print_nested_structure!(value1, io; indent, vector_limit) - else - if isa(value1, OperationalProfile) && - !isa(value1, FixedProfile) && - length(value1.vals) > vector_limit - # Truncate large vectors - println(io, indent_str, field1, ": ", typeof(value1)) - else - println(io, indent_str, field1, ": ", value1) - end + end + return nothing +end +function print_nested_structure!( + element::Vector{T}, + io::IOBuffer, + indent::Int64, + vector_limit::Int64, + show_the_n_last_elements::Int64, +) where {T} + indent += 1 + indent_str::String = create_indent_string(indent) + print(io, indent_str, "[") + + for (i, field1) ∈ enumerate(element) + if i == vector_limit + 1 + print(io, " ... ") + continue + end + if i <= vector_limit || i > length(element) - show_the_n_last_elements + print(io, field1) + if i != length(element) + print(io, ", ") end end end + println(io, "]") + return nothing end diff --git a/src/utils_GUI/results_axis_utils.jl b/src/utils_GUI/results_axis_utils.jl index 8228787..ecd889a 100644 --- a/src/utils_GUI/results_axis_utils.jl +++ b/src/utils_GUI/results_axis_utils.jl @@ -309,21 +309,10 @@ function create_label(selection::PlotContainer) else print(io, get_description(selection), " (", get_name(selection), ")") end - otherRes::Bool = false - for select ∈ get_selection(selection) - if isa(select, Resource) - if !otherRes - print(io, " (") - otherRes = true - end - print(io, string(select)) - if select != get_selection(selection)[end] - print(io, ", ") - end - end - end - if otherRes - print(io, ")") + other_indices = + filter(x -> !isa(x, AbstractElement) && !isnothing(x), get_selection(selection)) + if !isempty(other_indices) + print(io, " [", join(string.(other_indices), ", "), "]") end return String(take!(io)) end diff --git a/src/utils_gen/structures_utils.jl b/src/utils_gen/structures_utils.jl index 9d610ba..9cef445 100644 --- a/src/utils_gen/structures_utils.jl +++ b/src/utils_gen/structures_utils.jl @@ -264,7 +264,7 @@ end Get the colors linked to the resources in the link `l` based on the mapping `id_to_color_map`. """ function get_resource_colors(l::Link, id_to_color_map::Dict{Any,Any}) - resources::Vector{Resource} = EMB.link_res(l) + resources::Vector{Resource} = unique(vcat(inputs(l), outputs(l))) return get_resource_colors(resources, id_to_color_map) end diff --git a/test/EMI_geography_2.jl b/test/EMI_geography_2.jl index 75c5ed3..cb32560 100644 --- a/test/EMI_geography_2.jl +++ b/test/EMI_geography_2.jl @@ -172,6 +172,17 @@ function generate_example_data_geo_all_resources() [], ) + H2_Transport_50MW_OT = RefDynamic( + "H2_Transport_50_OT", + H2, + FixedProfile(50.0), + FixedProfile(0.05), + FixedProfile(0), + FixedProfile(0), + 2, + [], + ) + Waste_Transport_50MW_OT = RefDynamic( "Waste_Transport_50_OT", Waste, @@ -219,6 +230,7 @@ function generate_example_data_geo_all_resources() Coal_Transport_50MW_OT, CO2_Pipeline_100MW_OT, H2_Pipeline_100MW_OT, + H2_Transport_50MW_OT, Waste_Transport_50MW_OT, ], ), diff --git a/test/test_interactivity.jl b/test/test_interactivity.jl index b2f9500..793d3aa 100644 --- a/test/test_interactivity.jl +++ b/test/test_interactivity.jl @@ -115,6 +115,7 @@ simplified_toggle = get_toggle(gui, :simplified) components_2 = get_components(design_2) connections_2 = get_connections(design_2) simplified_toggle_2 = get_toggle(gui_2, :simplified) + available_data_menu_2 = get_menu(gui_2, :available_data) # Test simplify_all_levels=true functionality @testset "Test simplify_all_levels=true functionality" begin @@ -159,6 +160,27 @@ simplified_toggle = get_toggle(gui, :simplified) test_connection_colors(gui_2, connection1, :regular_colors) end + # Test data-menu labeling + @testset "Test data-menu labeling" begin + Oslo_Trondheim = connections_2[5] + pick_component!(gui_2, Oslo_Trondheim, :topo) # Select Oslo - Trondheim transmission + modes_OT = modes(Oslo_Trondheim) + update!(gui_2) + options = collect(available_data_menu_2.options[]) + for name ∈ ["trans_out", "trans_in", "trans_neg", "trans_pos", "trans_loss"] + for trans_mode ∈ ["PowerLine_50_OT", "Coal_Transport_50_OT"] + element = fetch_element(modes_OT, trans_mode) + + select_data!(gui_2, name; selection = [element]) + + i_selected = available_data_menu_2.i_selected[] + str = options[i_selected][2].description * " ($name) [$trans_mode]" + @test options[i_selected][1] == str + end + end + end + EMGUI.close(gui_2) + # Test the open button functionality @testset "get_button(gui,:open).clicks" begin pick_component!(gui, area1, :topo) # Select Area 1 diff --git a/test/utils.jl b/test/utils.jl index 1054aa8..7f68a06 100644 --- a/test/utils.jl +++ b/test/utils.jl @@ -172,3 +172,17 @@ function test_all_connection_colors(gui::GUI, type::Symbol) test_connections_colors(gui, get_connections(component), type) end end + +""" + fetch_element(elements::Vector, id) + +Fetch the element with the given `id` from the `elements` array. +""" +function fetch_element(elements::Vector, id) + for element ∈ elements + if element.id == id + return element + end + end + error("Element with id $id not found") +end