Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 34 additions & 0 deletions src/AdA_Picker.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# This is the main function of the Adria Array Visualization and Picking tool.
# This tool is meant to work with the GeophysicalModelGenerator to help interpreting geophysical datasets.
# It puts together the building blocks defined in the other files.
# IMPORTANT: FOR NOW, ONLY VERTICAL PROFILES WORK

const LOGO_FILE = joinpath(@__DIR__, "..", "assets", "AdA_Picker_logo_tr.png")

"""
start_AdA_Picker(; data = nothing)

Start the AdA Picker GUI. No input arguments are required. Optionally, a profile can be
given as `data` (a `ProfileData` structure or the name of a jld2 file containing a
`Profile`), which is then loaded directly.

Returns the figure. The state of the GUI can be accessed with `AdriaArrayGeometryPicker.current_gui()`.
"""
function start_AdA_Picker(; data = nothing)
fig, panels = create_main_layout()
gui = PickerGUI(fig = fig, panels = panels)
CURRENT_GUI[] = gui

gui.pick_toggle, gui.user_name, gui.compare_toggle = set_picking_controls!(panels.pick_controls)
add_logo!(panels.logo[1, 1], LOGO_FILE)

gui.menu = set_fileIO_menu!(fig, panels.main_menu) # see layout_main_controls.jl
on(s -> handle_menu_selection!(gui, s), gui.menu.selection) # see fileIO_utils.jl

connect_picking!(gui) # see picking_interaction.jl

isnothing(data) || load_profile!(gui, data)

display(fig)
return fig
end
575 changes: 0 additions & 575 deletions src/AdA_VizPickTool_V1.jl

This file was deleted.

23 changes: 20 additions & 3 deletions src/AdriaArrayGeometryPicker.jl
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,28 @@ using GLMakie
using GeophysicalModelGenerator
using DelimitedFiles
using Dates
using FileIO
using NativeFileDialog
using JLD2
using Interpolations

# export the function that starts the tool
export start_AdA_Picker # this is the function that starts the GUI, it is defined in AdA_VizPickTool_V1.jl
export start_AdA_Picker # this is the function that starts the GUI, it is defined in AdA_Picker.jl

# now include the main function that starts the GUI
include("AdA_VizPickTool_V1.jl")
# The code is split into:
# - functions without any graphics, that extract data from profiles and handle picks
include("Data/profile_data.jl") # get the data to plot from a GMG ProfileData structure
include("Picks/picks_io.jl") # convert, save and load picks
# - generic building blocks for GUIs (styled panels, toggle lists, logo, ...)
include("Layout/building_blocks.jl")
# - the parts of this specific GUI
include("GUI/gui_state.jl") # struct that holds the state of one GUI window
include("Layout/main_layout.jl") # main layout of the window
include("Controls/layout_main_controls.jl") # main menu and picking controls
include("Controls/data_controls.jl") # controls for field, surface and point data
include("Plotting/profile_plot.jl") # plots a vertical profile
include("Picks/picking_interaction.jl") # adding, deleting and dragging picks with the mouse
include("FileIO/fileIO_utils.jl") # file dialogs and the actions of the main menu
include("AdA_Picker.jl") # start_AdA_Picker, which puts everything together

end
115 changes: 115 additions & 0 deletions src/Controls/data_controls.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# this is data_controls.jl
# It contains the controls for the data of a profile: field data (menu, colormap, color range),
# surface data and point data (toggles to show/hide them)

const COLORMAPS = [:seismic, :roma, :glasgow, :lipari, :vik, :managua, :lajolla, :inferno, :plasma, :magma, :RdBu, :RdYlBu]

"""
set_field_controls!(pos, field_names, colrange)

Adds the controls for the field data (tomographies etc.): a menu to choose the field, a menu
for the colormap, and a slider and textboxes for the color range, which can be chosen within
`colrange`. Returns a named tuple with the controls.
"""
function set_field_controls!(pos, field_names, colrange)
panel = titled_panel(pos, "Field data (Tomographies etc.)")

field_menu = Menu(panel[2, 1:5], options = field_names)

Label(panel[3, 1], "Colormap", fontsize = 14, halign = :left, width = nothing)
colormap_menu = Menu(panel[3, 2:5], options = COLORMAPS, fontsize = 12)

# textboxes to enter min and max of the colorbar manually
colmin_textbox = Textbox(panel[4, 1], width = 50)
colmax_textbox = Textbox(panel[4, 5], width = 50)

# the slider is recreated when the field changes, so it is stored in a Ref
controls = (panel = panel, field_menu = field_menu, colormap_menu = colormap_menu,
colmin_textbox = colmin_textbox, colmax_textbox = colmax_textbox,
slider = Ref{Any}(nothing), slider_label = Ref{Any}(nothing))
set_colorrange_slider!(controls, colrange)

rowgap!(panel, 2)
return controls
end

"""
set_colorrange_slider!(controls, colrange)

Adds an interval slider (with a label) for the color range to the field controls.
"""
function set_colorrange_slider!(controls, colrange)
panel = controls.panel
slider = IntervalSlider(panel[4, 2:4], linewidth = 20, range = colrange)

# text for the label of the colorbar
colrange_text = lift(int -> string(round.(int, digits = 2)), slider.interval)
label = Label(panel[5, 2:4], colrange_text, fontsize = 14)

controls.slider[] = slider
controls.slider_label[] = label
return slider
end

# change the color limits of the heatmap if the slider changes, this is directly reflected in the colorbar
connect_colorrange_slider!(slider, hm) = on(int -> hm.colorrange = int, slider.interval)

# sets one end of the color range slider to `val`, keeping the end with index `other_index`
function set_colorrange_limit!(slider, val, other_index)
other = slider.interval[][other_index]
tmpmin = max(min(val, other), minimum(slider.range[]))
tmpmax = min(max(val, other), maximum(slider.range[]))
set_close_to!(slider, tmpmin, tmpmax)
return nothing
end

"""
connect_field_controls!(gui, controls, value, colrange)

Defines what happens if the field, the colormap or the color range are changed.
`value` is the Observable with the data of the heatmap.
"""
function connect_field_controls!(gui, controls, value, colrange)
hm = gui.heatmap
connect_colorrange_slider!(controls.slider[], hm)

# change values in textboxes to set the colorbar limits manually
on(s -> set_colorrange_limit!(controls.slider[], parse(Float64, s), 2), controls.colmin_textbox.stored_string)
on(s -> set_colorrange_limit!(controls.slider[], parse(Float64, s), 1), controls.colmax_textbox.stored_string)

# field data selection
on(controls.field_menu.selection) do s
# delete the interval slider and its label, and create them again after the data is updated.
# This is necessary, as there are sometimes issues with the interval if the heatmap values change drastically
delete!(controls.slider[])
delete!(controls.slider_label[])

# update the heatmap value -> this also updates colrange
value[] = volume_slice(gui.profile, s)

connect_colorrange_slider!(set_colorrange_slider!(controls, colrange), hm)

# adapt colorbar label
gui.colorbar_label.text = String(s)

println("Field data changed to: ", s)
end

# change colormap in colormap menu
on(controls.colormap_menu.selection) do s
hm.colormap = Reverse(s) # colormap of the colorbar is automatically updated
end
return nothing
end

"""
set_toggle_panel!(pos, title, names)

Panel with a title and a toggle for each of the data sets `names`. Returns the toggles.
"""
function set_toggle_panel!(pos, title, names)
panel = titled_panel(pos, title; tellwidth = false)
toggles = toggle_list!(panel, names)
rowgap!(panel, 2)
return toggles
end
39 changes: 31 additions & 8 deletions src/Controls/layout_main_controls.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# This is set_main_controls.jl
# This is layout_main_controls.jl
# It provides the functions to set the controls on:
# - file input/output
# - picking
# - panel switching (vertical/horizontal picking/ data comparison)

# function to set the buttons for panel switching

# function to set the buttons for panel switching (not used yet)
function set_panel_buttons!(fig,panel)
panel[1,1] = buttongrid = GridLayout(tellwidth = false)
button_picking = buttongrid[1,1] = Button(fig,label = "Pick")
Expand All @@ -13,16 +13,39 @@ function set_panel_buttons!(fig,panel)
return button_picking,button_comparison
end

# the menu contains save picks, save screenshotload data, load picks (to modify and without modifying) and close
# the entries of the main menu, their actions are defined in fileIO_utils.jl
const MAIN_MENU_OPTIONS = ["Click to choose ... ", "Load Profile...", "Load Picks...", "Load Picks (not modifyable)...",
"Save All...", "Save Picks...", "Save Screenshot...", "Close"]

# the menu contains save picks, save screenshotload data, load picks (to modify and without modifying) and close
function set_fileIO_menu!(fig,panel)
fileIO_menu_layout = GridLayout(panel[1,1],tellheight=false,halign= :left)
fileIO_menu_layout = GridLayout(panel[1,1],tellheight=false,halign= :left)
fileIO_menu_box = Box(fileIO_menu_layout[1, 1], color = :darkorange1,strokecolor = :darkorange1, alignmode= Outside(),cornerradius = 3);
# somehow add a label
# menu
Label(fileIO_menu_layout[1, 1], "Menu", fontsize = 16,halign = :left,width = nothing)
menu_fileIO = Menu(fileIO_menu_layout[2,1], options = ["Click to choose ... ","Load Profile...", "Load Picks...", "Load Picks (not modifyable)...", "Save All...", "Save Picks...","Save Screenshot...","Close"], fontsize=14, dropdown_arrow_color = (:darkorange1),dropdown_arrow_size = 14, prompt = "Select...")
menu_fileIO = Menu(fileIO_menu_layout[2,1], options = MAIN_MENU_OPTIONS, fontsize=14, dropdown_arrow_color = (:darkorange1),dropdown_arrow_size = 14, prompt = "Select...")

rowgap!(fileIO_menu_layout,2) # small vertical space
rowgap!(fileIO_menu_layout,2) # small vertical space
return menu_fileIO
end
end

"""
pick_toggle, user_name, compare_toggle = set_picking_controls!(panel)

Adds the controls to switch picking on/off, to enter the name of the user and to show/hide
the picks loaded for comparison.
"""
function set_picking_controls!(panel)
panel_pick_main = panel[1, 1:4] = GridLayout(tellheight = false, halign = :left)
panel_pick_compare = panel[2, 1:4] = GridLayout(tellheight = false, halign = :left)

Label(panel_pick_main[1, 1], "Picking", tellwidth = false)
pick_toggle = styled_toggle(panel_pick_main[1, 2])
user_name = Textbox(panel_pick_main[1, 3], width = 100, placeholder = "User name")

Label(panel_pick_compare[1, 1], "Compare Picks", tellwidth = false)
compare_toggle = styled_toggle(panel_pick_compare[1, 2])

return pick_toggle, user_name, compare_toggle
end
92 changes: 92 additions & 0 deletions src/Data/profile_data.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
# this is profile_data.jl
# It contains functions that extract the data to plot from a GMG ProfileData structure.
# They don't depend on the GUI, so they can be reused (and tested) independently.
# IMPORTANT: FOR NOW, ONLY VERTICAL PROFILES WORK

# fields that GMG adds to the volume data of a profile, which are not data to plot
const PROFILE_HELPER_FIELDS = (:x_profile, :FlatCrossSection)

"""
volume_field_names(profile)

Names of the volume data fields (tomographies etc.) of `profile` that can be plotted.
"""
volume_field_names(profile) = [name for name in keys(profile.VolData.fields) if name ∉ PROFILE_HELPER_FIELDS]

"""
surface_names(profile)

Names of the surface data sets (Moho etc.) that intersect the profile. The topography is
not included, as it is plotted separately.
"""
function surface_names(profile)
isnothing(profile.SurfData) && return Symbol[]
names = [name for name in keys(profile.SurfData) if name != :Topography]
# remove all datasets that only contain NaN values, this means that this surface does not intersect with the profile
return filter(name -> !all(isnan, profile.SurfData[name].depth.val), names)
end

"""
point_names(profile)

Names of the point data sets (seismicity etc.) that contain points close to the profile.
"""
function point_names(profile)
isnothing(profile.PointData) && return Symbol[]
return [name for name in keys(profile.PointData) if !isempty(profile.PointData[name].fields)]
end

"""
x, y = topography(profile)

Topography along the profile. Both `x` (distance along the profile) and `y` are in km.
"""
function topography(profile)
topo = profile.SurfData.Topography.fields
return topo.x_profile, Vector(ustrip.(topo.Topography))
end

"""
x, y = profile_grid(profile)

Coordinates of the (regular) grid of the volume data: distance along the profile and depth, in km.
"""
function profile_grid(profile)
x = profile.VolData.fields.x_profile[:, 1, 1]
y = profile.VolData.depth.val[1, :, 1]
return x, y
end

"""
volume_slice(profile, name)

2D matrix (without units) of the volume data field `name`, to be plotted as heatmap.
"""
volume_slice(profile, name) = ustrip.(profile.VolData.fields[Symbol(name)][:, :, 1])

"""
x, y = surface_line(profile, name)

Distance along the profile and depth of the surface data set `name`.
"""
function surface_line(profile, name)
surf = profile.SurfData[name]
return surf.fields.x_profile, surf.depth.val
end

"""
x, y = point_coordinates(profile, name)

Distance along the profile and depth of the points of point data set `name`, projected on the profile.
"""
function point_coordinates(profile, name)
points = profile.PointData[name]
return points.fields.x_profile, points.fields.depth_proj
end

"""
finite_extrema(A)

Minimum and maximum of `A`, ignoring NaN values.
"""
finite_extrema(A) = extrema(filter(!isnan, A))
Loading
Loading