Building blocks
The picker window is assembled from building blocks that can be reused for other windows. They are not exported; call them as AdriaArrayGeometryPicker.picker_layout etc., or import them with using AdriaArrayGeometryPicker: picker_layout, ....
AdriaArrayGeometryPicker.AdriaArrayGeometryPicker Module
AdriaArrayGeometryPickerThe AdriaArray geometry picker: a GLMakie window for visualizing GeophysicalModelGenerator.jl profiles (vertical cross-sections and horizontal slices) and picking points on them. Start it with geometry_picker.
The code is split into two parts:
generic building blocks, independent of the profile data structure:
Picks: container for a single set of picked points plus metadatasave_picks/load_picks: JLD2 and CSV file IOfile dialogs that do not block the GUI (
save_picks_dialog, ...)panels with a header, optionally collapsible (
control_panel!,fit_panel!,exclusive_panels!) and widget helpers (link_range_controls!,parse_levels,select_label!,set_text!)interactive picking on an
Axis(pickable!,set_pick_points!)
GeophysicalModelGenerator profiles, built on the generic part:
the picker window:
picker_layout,on_profile_loaded,menu_selection, assembled bygeometry_pickerplots of the volume, surface, point and topography data, the map overview and picks of other files, each with a state (e.g.
volume_plot_state) that is filled for every loaded profile (e.g.load_volume_data!)picks on a profile:
picking_state,current_picks,set_picks!,pick_lonlatsave_gui_state/load_gui_state!: profile and GUI state IO
Derived from MakiePickerGUI.jl (GeometryPicker repository); it contains only the code of its ready-made picker window.
sourceAssembling a window
geometry_picker builds the window like this:
using AdriaArrayGeometryPicker
using AdriaArrayGeometryPicker: picker_layout, on_profile_loaded, picking_state, menu_selection
using GLMakie
profile = Observable{Any}(nothing)
profile_file = Observable("")
profile_path = Observable("")
fig, panels, axis, widgets = picker_layout(; profile)
states = on_profile_loaded(profile, profile_file, axis, panels, widgets)
picking = picking_state(fig, profile, axis, widgets)
session = (; fig, profile, profile_file, profile_path, panels, axes = axis, widgets, picking, states...)
menu_selection(widgets.menu, session)
display(fig)AdriaArrayGeometryPicker.picker_layout Function
picker_layout(; size = (1200, 1000), profile = nothing, empty_height = 20,
logo_file = <assets/AdA_Picker_logo_tr.png>) -> NamedTupleCreate the figure and window layout of the picker:
┌─────────────┬───────────────┬─────────────┬──────────┐
│ menu │ pick controls │ pick legend │ logo │
├─────────────┼───────────────┴─────────────┴──────────┤
│ volume data │ topography (20 %) │
│ surface data│ profile │
│ point data │ │
│ topography │ │
│ compare │ │
│ map overview│ colorbar / legends (160 px) │
│ screenshot │ │
└─────────────┴────────────────────────────────────────┘This is the ready-made window for profile data and therefore specific to GeophysicalModelGenerator profiles (the widgets belong to the plot states). It is made of control_panel! panels, profile_plot_area! (plot panel), volume_panel! (volume data widgets) and map_panel! (map overview widgets); on_profile_loaded connects them. geometry_picker builds the whole window.
Keywords
size: size of the figure in pixels.profile: theObservablethat holds the profile. If given, every panel that has widgets switches to following its content as soon asprofileholds a profile. This listener has the same priority (-10) as the one ofconnect_profile!and is registered first, so it runs before the loaders; this is harmless because everyload_*!function fits its panel itself.empty_height: the menu panels start with this small fixed content height.logo_file: image shown in the top right corner (a warning is logged if it does not exist).
The first row and column of the window adapt to the panels; the plot panel takes the remaining space. The menu panel holds the dropdown menu (see menu_selection for its entries), the picking panel the picking toggle and a user name field, the picks panel the compare-picks toggle. The volume data panel holds the widgets of volume_panel! (a data dropdown, the colorbar limits, a colormap dropdown with a "reverse" checkbox and the same controls for the contour lines); volume_plot_state connects them. The plot panel is made by profile_plot_area!. The map overview panel holds the widgets of map_panel! (a "Load topography" button, a label and a map axis; connected by map_plot_state). The other collapsible panels are filled when a profile is loaded (see on_profile_loaded); only one of them is expanded at a time (see exclusive_panels!). The topography and profile axes are linked in x.
Returns
A NamedTuple (; fig, panels, axes, widgets):
fig: the figure,panels: the contentGridLayoutof each menu panel (main_menu,pick_controls,pick_legend,volume_data_panel,surface_data_panel,point_data_panel,topography_data_panel,compare_picks_panel,map_data_panel,screenshot_data_panel), plus the layoutslogo,plotandcolorbar,axes:topo_ax,profile,logoandmap,widgets:menu,pick_toggle,pick_name,pick_warning,compare_toggle, the volume data and contour controlsvolume_*(seevolume_panel!),map_load_button,map_label(seemap_panel!) and the expand / collapse toggles*_panel_toggleof the collapsible panels (volume,surface,point,topography,compare,map,screenshot).
Do not destructure the result into a variable named axes: that would shadow Base.axes in your module. The examples use axis.
Example
profile = Observable{Any}(nothing)
fig, panels, axis, widgets = picker_layout(; profile)
display(fig)AdriaArrayGeometryPicker.profile_plot_area! Function
profile_plot_area!(gp; topography = true) -> NamedTupleCreate the plot panel of a profile GUI at the grid position gp (the plot panel of picker_layout):
┌────────────────────────────────────────────┐
│ topography axis (20 % of the height) │
├────────────────────────────────────────────┤
│ profile axis │
│ │
├──────────┬─────────┬─────────┬─────────────┤
│ colorbars│ surface │ point │ compare │ colorbar layout (160 px)
│ │ legend │ legend │ legend │
└──────────┴─────────┴─────────┴─────────────┘The topography axis has no decorations and is linked to the profile axis in x; the profile axis has no grid (it would show through the heatmap). The colorbar layout holds the colorbars of the volume data in its first column (see volume_plot_state) and right of them the legends of the surface data, point data and compared picks, in this order. The legends are packed from the left: a legend that does not exist (e.g. no surface data) leaves no gap. Requires nothing but a figure; the axes are meant for GeophysicalModelGenerator profiles.
Arguments
gp: grid position, e.g.fig[1, 2].
Keywords
topography:falsecreates no topography axis (topo_axis thennothing), the profile axis takes the whole height.
Returns
A NamedTuple (; plot, topo_ax, profile, colorbar): the layout of the plot panel, the topography axis, the profile axis and the colorbar layout. It has the fields of the axes (profile, topo_ax) and panels (plot, colorbar) of picker_layout that the plot states and connect_profile! use, so it can be passed as either.
Example
fig = Figure()
area = profile_plot_area!(fig[1, 2])
panel = control_panel!(fig[1, 1], "Volume data"; width = 340)
volume = volume_plot_state(area.profile, volume_panel!(panel), area.colorbar)AdriaArrayGeometryPicker.set_profile_layout! Function
set_profile_layout!(axes, profile; show_topography = true)Adapt the plot panel to the type of profile: a vertical cross-section gets the topography axis above the profile axis. For a horizontal slice the topography axis is hidden and the profile axis takes its space; the profile axis then shows longitude / latitude and the depth of the slice as its title. Requires a GeophysicalModelGenerator ProfileData.
The topography axis is hidden by shrinking the layout row it sits in to zero height (its original size is restored when it is shown again), so it can be in any row of any layout.
Arguments
axes: aNamedTuplewith the profile axisprofileand, optionally, the topography axistopo_ax(nothingor missing: only the profile axis is adapted), e.g. theaxesofpicker_layoutor the result ofprofile_plot_area!.profile: the loaded profile (nothingis treated as vertical).
Keywords
show_topography: set tofalseto hide the topography axis of a vertical profile as well (e.g. if it has no topography data).
Returns nothing.
AdriaArrayGeometryPicker.on_profile_loaded Function
on_profile_loaded(profile::Observable, profile_file::Observable, axes::NamedTuple,
panels::NamedTuple, widgets::NamedTuple)Create all plot states of the window of picker_layout (colorbar and volume data controls, surface, point and topography data, map overview, compared picks) and fill them with every profile loaded into profile (see connect_profile!). Requires a GeophysicalModelGenerator ProfileData and the layout of picker_layout; for a layout of your own, create the states you need and call connect_profile!.
Arguments
profile:Observablethat holds the profile (seeload_profile!).profile_file:Observablewith the name of the loaded file (set beforeprofilechanges), used as title of the topography axis.axes,panels,widgets: as returned bypicker_layout(the states are made with thepicker_layoutforms of the state functions, e.g.volume_plot_state(axes, panels, widgets)).
Every time a profile is loaded (listener with priority -10), the layout is adapted to the profile type (vertical cross-section or horizontal slice, see set_profile_layout!), the topography is plotted (with file name and start/end coordinates), the volume data dropdown is filled and its first field plotted as a heatmap, the axis limits of a horizontal slice are set (see fit_horizontal_axis!), and the surface data, point data, map overview and compared picks are replaced by those of the new profile. An error in one of these steps is logged and does not stop the others. See load_volume_data!, load_surface_data!, load_point_data!, load_topography_panel!, update_map_line! and clear_compare_picks! for the individual steps.
Returns the plot states (; volume, surface, points, topography, topomap, compare), which the GUI state (see gui_settings) is read from.
Example
profile = Observable{Any}(nothing)
profile_file = Observable("")
fig, panels, axis, widgets = picker_layout(; profile)
states = on_profile_loaded(profile, profile_file, axis, panels, widgets)
profile_file[] = "Profile1.pgmg"
load_profile!(profile, joinpath(pkgdir(AdriaArrayGeometryPicker), "assets", "Profile1.pgmg"))AdriaArrayGeometryPicker.connect_profile! Function
connect_profile!(profile::Observable, states::NamedTuple; axes, profile_file = "",
priority = -10) -> ObserverFunctionFill the plot states states with every profile that is loaded into profile: the listener that makes a GUI follow its profile. Only the states that are present are filled, so a GUI with e.g. only a volume and a point data panel passes (; volume, points). Requires a GeophysicalModelGenerator ProfileData.
Every time a profile is loaded, these steps are taken (in this order, each only if its state is present):
| step | state (field of states) | function |
|---|---|---|
| layout of the plot panel | – (always, if there are axes) | set_profile_layout! |
topography (axis title: profile_file) | topography (topography_panel_state) | load_topography_panel! |
| volume data | volume (volume_plot_state) | load_volume_data! |
| axis limits of a horizontal slice | – (always, if there are axes) | fit_horizontal_axis! |
| surface data | surface (surface_plot_state) | load_surface_data! |
| point data | points (point_plot_state) | load_point_data! |
| map overview | topomap (map_plot_state) | update_map_line! |
| compared picks are removed | compare (compare_plot_state) | clear_compare_picks! |
Other fields of states are ignored. An error in one of the steps is logged and does not stop the others. Without a topography state the topography axis (if any) stays hidden.
Arguments
profile:Observablethat holds the profile (seeload_profile!);nothingis ignored.states:NamedTupleof the plot states, with any of the fieldsvolume(volume_plot_state),surface(surface_plot_state),points(point_plot_state),topography(topography_panel_state),topomap(map_plot_state) andcompare(compare_plot_state); each made with its explicit form (nopicker_layoutneeded) or from the output ofpicker_layout.
Keywords
axes:NamedTuplewith the profile axisprofileand optionally the topography axistopo_ax, e.g. the result ofprofile_plot_area!or theaxesofpicker_layout(required;nothing: the layout and the axis limits are left alone).profile_file: name of the profile file (StringorObservable, read when a profile is loaded), the title of the topography axis.priority: priority of the listener. The default-10runs it after the listeners with default priority, and before the one ofpicking_state(-20).
Returns the listener, which can be removed with off.
Example
fig = Figure(size = (1200, 900))
area = profile_plot_area!(fig[1, 2])
left = fig[1, 1] = GridLayout()
volume_panel = control_panel!(left[1, 1], "Volume data"; width = 340)
point_panel = control_panel!(left[2, 1], "Point data"; width = 340)
map_panel = control_panel!(left[3, 1], "Map"; width = 340)
map_widgets = map_panel!(map_panel)
states = (; volume = volume_plot_state(area.profile, volume_panel!(volume_panel), area.colorbar),
points = point_plot_state(area.profile, point_panel, area.colorbar),
topomap = map_plot_state(map_widgets.map_ax, map_widgets.map_load_button, map_widgets.map_label))
profile = Observable{Any}(nothing)
connect_profile!(profile, states; axes = area)
load_profile!(profile, joinpath(pkgdir(AdriaArrayGeometryPicker), "assets", "Profile1.pgmg"))AdriaArrayGeometryPicker.menu_selection Function
menu_selection(menu::Menu, session) -> ObserverFunctionConnect the entries of the dropdown menu (widgets.menu of picker_layout, or a Menu of your own with some of these entries) to their actions. Requires a GeophysicalModelGenerator profile GUI.
Each entry reads only the fields of session it needs. If a field is missing, choosing the entry logs a warning that names it and does nothing else; so a custom GUI can use the menu with the parts it has. Unknown entries are ignored.
| entry | action | session fields |
|---|---|---|
| "Load Profile" | choose a .pgmg file and load it (see open_profile!) | profile; optional profile_file, profile_path |
| "Save State" | save the path of the loaded profile and the settings of the GUI to a .aagps file (a JLD2 file, see save_gui_state) | profile, profile_path; the parts listed in gui_settings |
| "Load State" | restore them from a .aagps file (or a .jld2 state file of MakiePickerGUI.jl; see load_gui_state!) | as "Save State" |
| "Save Picks" | save the picks to a .aagpp (JLD2) or .csv file (see current_picks) | profile, picking; optional profile_file, widgets.pick_name |
| "Load Picks" | load picks (editable) from a file if they belong to the loaded profile (see set_picks!) | as "Save Picks" |
| "Load Compare Picks" | show the picks of a file (e.g. of another user) as a dashed line for comparison, not editable (see load_compare_picks!) | profile, compare |
| "Save Screenshot" | choose a file name and save an image of the whole window (see save_screenshot_dialog) | fig |
| "Close" | close the window | fig |
The session of the ready-made picker has all of them (see the example below).
Dialogs run in a background task, so the window keeps rendering while they are open; errors are logged and the GUI keeps running.
Arguments
menu: theMenu; create it withdefault = nothing, so that no entry is preselected.session: aNamedTuple(or any object with these properties) with the fields above.
Returns the listener, which can be removed with off.
Example
profile = Observable{Any}(nothing)
profile_file = Observable("")
profile_path = Observable("")
fig, panels, axis, widgets = picker_layout(; profile)
states = on_profile_loaded(profile, profile_file, axis, panels, widgets)
picking = picking_state(fig, profile, axis, widgets)
session = (; fig, profile, profile_file, profile_path, panels, axes = axis, widgets, picking, states...)
menu_selection(widgets.menu, session)AdriaArrayGeometryPicker.is_state_file Function
is_state_file(path) -> BoolWhether path is a GUI state file (see save_gui_state; usually .aagps): a JLD2 file with a "format" entry "AdriaArrayGeometryPicker state" (or "MakiePickerGUI state", written by MakiePickerGUI.jl). Decided by the content, not by the extension; anything that is not a JLD2 file, or a JLD2 file without such an entry (e.g. a .pgmg profile), gives false.
Profiles
AdriaArrayGeometryPicker.load_profile! Function
load_profile!(profile::Observable, path) -> profile dataLoad the GeophysicalModelGenerator profile stored in the .pgmg file path and assign it to profile, which notifies everything that listens to it (see on_profile_loaded). Returns the loaded ProfileData. The file may hold a vertical cross-section or a horizontal slice, see is_vertical.
Arguments
profile:Observablethat holds the current profile (nothingbefore a profile is loaded).path: path of the.pgmgfile (a JLD2 file with a singleProfileDataentry).
Example
profile = Observable{Any}(nothing)
load_profile!(profile, joinpath(pkgdir(AdriaArrayGeometryPicker), "assets", "Profile1.pgmg"))AdriaArrayGeometryPicker.open_profile! Function
open_profile!(session, path) -> profile dataLoad the profile file path into the GUI (see load_profile!) and remember its file name (session.profile_file, the title of the topography axis and stored with the picks) and its full path (session.profile_path, for save_gui_state). Both are set before the profile, so the listeners of profile see the new names. Returns the loaded profile.
Arguments
session: aNamedTuple(or any object with these properties):profile(required): theObservablethat holds the profile,profile_file(optional):Observable{String}that receives the file name,profile_path(optional):Observable{String}that receives the absolute path.
The
sessionof the picker has them all (seemenu_selection).path: path of the.pgmgfile.
Requires a GeophysicalModelGenerator ProfileData in the file.
AdriaArrayGeometryPicker.is_vertical Function
is_vertical(profile) -> BoolWhether profile is a vertical cross-section (true, also for nothing and for profiles without a vertical field) or a horizontal slice at a certain depth (false). Vertical and horizontal profiles are plotted and picked differently: along the profile (distance, depth) or in the lon / lat plane. Accepts a GeophysicalModelGenerator ProfileData or nothing.
AdriaArrayGeometryPicker.slice_depth Function
slice_depth(profile) -> Float64Depth of the horizontal slice profile (same sign convention as the depth of the data). Only meaningful for horizontal slices (is_vertical(profile) == false). Requires a GeophysicalModelGenerator ProfileData.
Volume data
AdriaArrayGeometryPicker.volume_panel! Function
volume_panel!(panel::GridLayout; contours = true, colormaps = <all matplotlib and Crameri maps>,
colormap = "viridis", contour_colormap = "coolwarm") -> NamedTupleCreate the widgets of the volume data panel in the layout panel (e.g. the content of a control_panel!) and let the panel follow their size (fit_panel!). These are the widgets of the volume data panel of picker_layout; they do nothing until volume_plot_state connects them. Requires nothing but a layout; the widgets are meant for the volume data of a GeophysicalModelGenerator ProfileData.
The rows of panel, from the top: a dropdown for the data field, the colorbar limits (min / max text fields around an interval slider), a label with the current limits, a colormap dropdown with a "reverse" checkbox; with contours = true also the same controls for contour lines of a second field (dropdown with a "show" toggle, colormap with "reverse" checkbox, colour range) and a text field for the contour levels.
Arguments
panel: the (empty) content layout of a panel made bycontrol_panel!.
Keywords
contours:falseleaves out the contour line widgets (the volume data are then only shown as a heatmap).colormaps: the names of the colormaps in the colormap dropdowns.colormap,contour_colormap: the initially selected colormaps.
Returns
A NamedTuple of the widgets, named as in the widgets of picker_layout: volume_menu, volume_cbar_min, volume_cbar_slider, volume_cbar_max, volume_cbar_label, volume_cmap_menu, volume_cmap_reverse and, with contours = true, volume_contour_menu, volume_contour_toggle, volume_contour_cmap_menu, volume_contour_cmap_reverse, volume_contour_cbar_min, volume_contour_cbar_slider, volume_contour_cbar_max and volume_contour_levels.
Example
fig = Figure()
area = profile_plot_area!(fig[1, 2])
panel = control_panel!(fig[1, 1], "Volume data"; collapsible = true, width = 340)
widgets = volume_panel!(panel; colormaps = ["viridis", "roma", "coolwarm"])
volume = volume_plot_state(area.profile, widgets, area.colorbar)AdriaArrayGeometryPicker.volume_plot_state Function
volume_plot_state(ax::Axis, widgets, colorbar::GridLayout) -> NamedTuple
volume_plot_state(axes, panels, widgets) -> NamedTupleCreate the colorbars of the volume data and connect the volume data widgets (field dropdown, colorbar limits slider and text fields, colormap dropdown and reverse checkbox, and the same for the contour lines with their own colorbar and levels). Returns the state that load_volume_data! fills when a profile is loaded. Created once, before a profile is loaded; the plots follow the widgets from then on. Works for vertical and horizontal GeophysicalModelGenerator profiles alike.
Arguments
ax: the profile axis, in which the heatmap and the contour lines are plotted.widgets: the widgets made byvolume_panel!(anyNamedTuplewith these fields, e.g. thewidgetsofpicker_layout). Without thevolume_contour_*widgets (volume_panel!(panel; contours = false)) there are no contour lines and no contour colorbar.colorbar: the layout for the colorbars (seeprofile_plot_area!). The colorbar of the heatmap and its title go into the rows 1 and 2 of its first column, those of the contour lines into the rows 3 and 4.axes,panels: as returned bypicker_layout;axes.profileandpanels.colorbarare used.
Returns
A NamedTuple with the plots (heatmap, contour: Refs), the Observables they follow (colormap, colorrange, contour_colormap, contour_colorrange, levels), the profile axis ax, the widgets and a few internal fields. contours says whether there are contour lines.
Example
fig = Figure()
area = profile_plot_area!(fig[1, 2])
panel = control_panel!(fig[1, 1], "Volume data"; width = 340)
volume = volume_plot_state(area.profile, volume_panel!(panel), area.colorbar)
profile = Observable{Any}(nothing)
connect_profile!(profile, (; volume); axes = area) # or call `load_volume_data!` yourself
load_profile!(profile, joinpath(pkgdir(AdriaArrayGeometryPicker), "assets", "Profile1.pgmg"))AdriaArrayGeometryPicker.load_volume_data! Function
load_volume_data!(state, profile) -> nothingFill the field dropdowns with the data fields of profile.VolData and plot the first one as a heatmap and as contour lines (plot_volume!, plot_contours!), replacing the plots of the previous profile. On a horizontal slice the depth of the SurfData entries is offered as additional fields. Without volume data the dropdowns say so and the previous plots are removed. Called by connect_profile! for the state volume. Requires a GeophysicalModelGenerator ProfileData.
GeophysicalModelGenerator declares VolData as a single GeoData (or nothing); this package also accepts a NamedTuple of GeoData, whose fields are then all offered (a field name that occurs in several of them gets the key as prefix).
On a vertical profile the heatmap is over the distance along the profile and the depth; on a horizontal slice over longitude and latitude.
Arguments
state: the volume state ofvolume_plot_state(without contour widgets only the heatmap is plotted).profile: the loaded profile.
Returns nothing.
AdriaArrayGeometryPicker.plot_volume! Function
plot_volume!(state, geo::GeoData, field::Symbol)Plot field of geo as a heatmap in the profile axis, replacing the previous one, and adapt the colorbar slider (range and initial limits: minus/plus the largest absolute value of the field, so they are symmetric around zero) and the colorbar title.
AdriaArrayGeometryPicker.plot_contours! Function
plot_contours!(state, geo::GeoData, field::Symbol)Plot field of geo as contour lines on top of the heatmap, replacing the previous ones. The levels follow state.levels, the lines are coloured with the contour colormap and colour range (own slider, range: plus/minus the largest absolute value of field) and are visible as long as the contour toggle is on. Their colorbar (created in volume_plot_state) sits below the one of the heatmap and is titled with the field name.
AdriaArrayGeometryPicker.clear_contours! Function
clear_contours!(state)Remove the contour lines and the title of their colorbar.
sourceAdriaArrayGeometryPicker.volume_entries Function
volume_entries(voldata) -> Vector{Tuple{String,Tuple}}Dropdown entries (label, (geodata, fieldname)) for all data fields in voldata, which is nothing, a single GeoData (as GeophysicalModelGenerator declares VolData) or a NamedTuple of them (also accepted by this package). The coordinate fields (*x_profile) and the *FlatCrossSection fields are left out. A field name that occurs in several GeoData objects gets the key of its object as prefix.
AdriaArrayGeometryPicker.surface_entries Function
surface_entries(profile) -> Vector{Tuple{String,Tuple}}Dropdown entries ("name: depth", (geodata, :depth)) for the surface data sets of a horizontal slice, which can be plotted like volume data (the depth of the surface is the value). Sets that only contain NaN are left out.
Surface and point data
AdriaArrayGeometryPicker.surface_plot_state Function
surface_plot_state(ax::Axis, panel::GridLayout, colorbar::GridLayout) -> NamedTuple
surface_plot_state(axes, panels) -> NamedTupleState for the surface data: what load_surface_data! created for the current profile (toggles and labels in the surface data panel, lines in the profile axis, the legend). Created once, before a profile is loaded; it creates nothing itself. Meant for a GeophysicalModelGenerator ProfileData.
Arguments
ax: the profile axis.panel: the (empty) layout for the toggles, e.g. the content of acontrol_panel!.colorbar: the colorbar layout (seeprofile_plot_area!); the legend goes right of the colorbars (the first legend).axes,panels: as returned bypicker_layout;axes.profile,panels.surface_data_panelandpanels.colorbarare used.
Returns the state, a NamedTuple with the fields ax, panel, colorbar, the plots, the toggles with their labels and the legend (Ref).
AdriaArrayGeometryPicker.load_surface_data! Function
load_surface_data!(state, profile) -> nothingReplace the surface data of the previous profile by profile.SurfData: for each surface a label and a toggle in the surface data panel, a white line with a coloured line on top of it in the profile axis (visible as long as the toggle is on) and an entry in the "Surface data" legend, the first legend right of the colorbars (greyed out by Makie while the toggle is off). Surfaces that only contain NaN do not intersect the profile and are skipped, as is a Topography entry, which is plotted separately. On a horizontal slice, SurfData entries that cross the depth of the slice are shown as a contour line at that depth. The panel is empty and there is no legend if there is no surface data. Called by connect_profile! for the state surface. Requires a GeophysicalModelGenerator ProfileData.
Arguments
state: the surface state ofsurface_plot_state.profile: the loaded profile.
AdriaArrayGeometryPicker.point_plot_state Function
point_plot_state(ax::Axis, panel::GridLayout, colorbar::GridLayout) -> NamedTuple
point_plot_state(axes, panels) -> NamedTupleState for the point data: what load_point_data! created for the current profile (toggles and labels in the point data panel, scatter plots in the profile axis, the legend). Created once, before a profile is loaded; it creates nothing itself. Meant for a GeophysicalModelGenerator ProfileData.
Arguments
ax: the profile axis.panel: the (empty) layout for the toggles, e.g. the content of acontrol_panel!.colorbar: the colorbar layout (seeprofile_plot_area!); the legend goes right of the colorbars and the surface data legend (if there is one).axes,panels: as returned bypicker_layout;axes.profile,panels.point_data_panelandpanels.colorbarare used.
Returns the state, a NamedTuple with the fields ax, panel, colorbar, the plots, the toggles with their labels and the legend (Ref).
AdriaArrayGeometryPicker.load_point_data! Function
load_point_data!(state, profile) -> nothingReplace the point data of the previous profile by profile.PointData: for each dataset a label and a toggle in the point data panel, a scatter plot in the profile axis (x_profile against depth_proj, visible as long as the toggle is on) and an entry in the "Point data" legend, right of the surface data legend if there is one (greyed out by Makie while the toggle is off). For a horizontal slice the points are plotted as longitude against latitude. Datasets without points are skipped. Called by connect_profile! for the state points. Requires a GeophysicalModelGenerator ProfileData.
Arguments
state: the point state ofpoint_plot_state.profile: the loaded profile.
Topography
AdriaArrayGeometryPicker.topography_panel_state Function
topography_panel_state(ax::Axis, panel::GridLayout; topo_ax = nothing) -> NamedTuple
topography_panel_state(axes, panels) -> NamedTupleState of the topography panel: what load_topography_panel! created for the current profile (widgets in the panel, plots in the profile axis or the topography axis). Created once, before a profile is loaded; it creates nothing itself. Meant for a GeophysicalModelGenerator ProfileData.
Arguments
ax: the profile axis (the topography of a horizontal slice is plotted on top of it).panel: the (empty) content layout for the widgets, made bycontrol_panel!.axes,panels: as returned bypicker_layout;axes.profile,axes.topo_ax(optional) andpanels.topography_data_panelare used.
Keywords
topo_ax: the axis above the profile axis in which the topography of a vertical profile is plotted (seeprofile_plot_area!); without it, the topography of vertical profiles is not shown (horizontal slices do not need it).
Returns
A NamedTuple with the fields axes ((; profile, topo_ax)), panel, the widgets (blocks) and plots (plots) of the current profile and controls, a Ref to the widgets of the panel that can be set programmatically (filled by load_topography_panel!):
vertical profile:
(; menu, show, fill): data set dropdown (nothingif there is only one data set), theTogglethat shows the topography axis and theCheckboxfor the fill,horizontal slice:
(; menu, mode, opacity, levels): data set dropdown (ornothing), the display modeMenu("Heatmap", "Contours", "Off"), the opacitySliderof the heatmap and theTextboxof the contour levels,nothingwithout topography data (or a vertical profile withouttopo_ax).
Example
fig = Figure()
area = profile_plot_area!(fig[1, 2])
panel = control_panel!(fig[1, 1], "Topography"; width = 340)
topography = topography_panel_state(area.profile, panel; topo_ax = area.topo_ax)
profile = Observable{Any}(nothing)
connect_profile!(profile, (; topography); axes = area)
load_profile!(profile, joinpath(pkgdir(AdriaArrayGeometryPicker), "assets", "Profile1.pgmg"))
# vertical profile: (; menu, show, fill); switch the fill below the topography line off
topography.controls[].fill.checked[] = false
# on a horizontal slice it is (; menu, mode, opacity, levels), e.g.
# select_label!(topography.controls[].mode, "Contours")AdriaArrayGeometryPicker.load_topography_panel! Function
load_topography_panel!(state, profile, name)Replace the content of the topography panel and the topography plot by those of profile (profile.TopoData; name, the file name, is the title of the topography axis). state is created by topography_panel_state. Requires a GeophysicalModelGenerator ProfileData; the result depends on the profile type:
vertical profile: the topography is plotted in the topography axis above the profile (see
plot_topography!). The panel has a dropdown for the data set (if there are several), a toggle that shows or hides the axis (the profile axis then takes its space) and a checkbox for the grey / blue fill below the line.horizontal slice: the topography is plotted on top of the volume data, as a heatmap (
oleroncolormap, centred around zero, with a slider for the opacity) or as contour lines (levels typed in a text field, default: one line at 0), or not at all. The panel has a dropdown for the data set (if there are several) and these controls.
Without topography data the panel only says so and the topography axis is hidden; the same for a vertical profile if the state has no topography axis. The widgets of the panel are available in state.controls[] (see topography_panel_state), so they can be set programmatically, e.g. with select_label! and set_text!. Called by connect_profile! for the state topography.
Arguments
state: the topography state oftopography_panel_state.profile: the loaded profile.name: title of the topography axis (usually the file name).
Returns nothing.
AdriaArrayGeometryPicker.plot_topography! Function
plot_topography!(ax::Axis, profile, name; key = nothing, fill = true) -> BoolPlot the topography of a vertical profile in ax, replacing what was plotted before: an entry of profile.TopoData as a black line over the distance along the profile (x_profile), with a grey fill below the line and a blue fill where it lies below sea level. The profile ends are marked with "A" and "B". Requires a GeophysicalModelGenerator ProfileData of a vertical cross-section (the topography of a horizontal slice is plotted by load_topography_panel!).
Arguments
ax: the axis to plot in; its title is set tonameand the start / end coordinates of the profile are written in its top left / top right corner.profile: the loaded profile.name: title of the axis, usually the file name.
Keywords
key: entry ofTopoDatato plot (default: the first).fill:falseplots only the line.
Returns false (and only clears ax) if the profile has no topography, true otherwise.
AdriaArrayGeometryPicker.fit_horizontal_axis! Function
fit_horizontal_axis!(ax::Axis, profile)Set the limits of the axis of a horizontal slice to the lon / lat extent of its topography (of the volume data if there is no topography) and its aspect ratio such that distances look approximately equal: a degree of longitude is cos(latitude) times as long as one of latitude. For a vertical profile the axis gets its default aspect ratio back and its limits are left alone. Called by connect_profile!. Requires a GeophysicalModelGenerator ProfileData.
Arguments
ax: the profile axis, e.g.area.profileofprofile_plot_area!.profile: the loaded profile.
Returns nothing.
Map overview
AdriaArrayGeometryPicker.map_panel! Function
map_panel!(panel::GridLayout; width = 250, height = 200) -> NamedTupleCreate the widgets of the map overview in the layout panel (the content of a control_panel!) and let the panel follow their size (fit_panel!): a "Load topography" button, a label with the name of the loaded topography file and a map axis (longitude / latitude). These are the widgets of the map overview panel of picker_layout; they do nothing until map_plot_state connects them. Requires nothing but a layout; the map is meant for the profile line of a GeophysicalModelGenerator ProfileData.
Arguments
panel: the (empty) content layout of a panel made bycontrol_panel!.
Keywords
width,height: initial size of the map axis in pixels (it gets the aspect ratio of the region once a profile or a topography is shown, at most 250 × 300).
Returns
A NamedTuple (; map_ax, map_load_button, map_label): the map Axis, the Button and the Label, named as in the axes (map) and widgets of picker_layout.
Example
fig = Figure()
panel = control_panel!(fig[1, 1], "Map overview"; collapsible = true, width = 340)
map_widgets = map_panel!(panel)
topomap = map_plot_state(map_widgets.map_ax, map_widgets.map_load_button, map_widgets.map_label)AdriaArrayGeometryPicker.map_plot_state Function
map_plot_state(ax::Axis, load_button = nothing, label = nothing) -> NamedTuple
map_plot_state(axes, widgets) -> NamedTupleCreate the state of the map overview: the profile line in the map axis ax (a white line with a red one on top, from the start to the end point of the profile, empty until a profile is loaded) and, if given, connect the load_button, which asks for a file and calls load_topography!. Created once, before a profile is loaded; update_map_line! draws each loaded profile (called by connect_profile! for the state topomap). Meant for a GeophysicalModelGenerator ProfileData.
Arguments
ax: the map axis, e.g.map_axofmap_panel!.load_button: aButtonthat loads a topography file (a JLD2 file with aGeoData), ornothing(load topography from code withload_topography!).label: aLabelthat shows the name of the loaded topography file, ornothing.axes,widgets: as returned bypicker_layout;axes.map,widgets.map_load_buttonandwidgets.map_labelare used.
Returns
A NamedTuple with the map axis ax, the label, the Observables of the profile line (line_points) and of its labels (markers), and Refs to the topography heatmap, its extent and the path of the loaded topography file ("" if none).
Example
fig = Figure()
panel = control_panel!(fig[1, 1], "Map overview"; collapsible = true, width = 340)
map_widgets = map_panel!(panel)
topomap = map_plot_state(map_widgets.map_ax, map_widgets.map_load_button, map_widgets.map_label)
load_topography!(topomap, "path/to/etopo1.jld2")AdriaArrayGeometryPicker.load_topography! Function
load_topography!(state, path)Load the topography file path (a JLD2 file holding a GeoData object, e.g. etopo1.jld2) and plot it in the map overview as a heatmap with the oleron colormap, centred around zero, replacing the previous one. Only every n-th grid point is plotted, so that large grids stay responsive. The map is shown with the aspect ratio of the region, and the label of the state (if any) shows the file name. Needs no profile, but the topography file has to hold a GeophysicalModelGenerator GeoData.
Arguments
state: the map state ofmap_plot_state.path: the topography file.
Returns the GeoData that was plotted; throws an error if the file holds no GeoData.
AdriaArrayGeometryPicker.update_map_line! Function
update_map_line!(state, profile)Draw the profile in the map overview, replacing the previous one: a vertical profile as a line from its start to its end point (marked "A" and "B"), a horizontal slice as a box of its lon / lat extent labelled with its depth. Without a loaded topography the map limits follow the profile. Called by connect_profile! for the state topomap. Requires a GeophysicalModelGenerator ProfileData.
Arguments
state: the map state ofmap_plot_state.profile: the loaded profile.
AdriaArrayGeometryPicker.fit_map! Function
fit_map!(state)Set limits and aspect ratio of the map to the loaded topography, or, as long as there is none, to the profile line. Internal helper of the map overview.
sourceCompared picks
AdriaArrayGeometryPicker.compare_plot_state Function
compare_plot_state(ax::Axis, panel::GridLayout, colorbar::GridLayout, compare_toggle = nothing) -> NamedTuple
compare_plot_state(axes, panels, widgets) -> NamedTupleCreate the state for picks shown for comparison (picks of other files, not editable): what add_compare_picks! and load_compare_picks! create (a label and a toggle per file in the panel, a dashed line per file in the profile axis, a legend in the colorbar layout). Created once, before a profile is loaded; it creates nothing itself. clear_compare_picks! removes the compared picks (called by connect_profile! for the state compare whenever a profile is loaded). Meant for a GeophysicalModelGenerator ProfileData.
Arguments
ax: the profile axis.panel: the (empty) content layout for the labels and toggles, made bycontrol_panel!.colorbar: the colorbar layout (seeprofile_plot_area!); the legend goes right of the colorbars and the other legends.compare_toggle: an optionalTogglethat shows or hides all compared picks at once (a line is visible while its own toggle andcompare_toggleare on);nothing: each line follows only its own toggle.axes,panels,widgets: as returned bypicker_layout;axes.profile,panels.compare_picks_panel,panels.colorbarandwidgets.compare_toggle(the "Compare Picks" toggle of the pick legend panel) are used.
Returns
A NamedTuple with the fields ax, panel, colorbar, compare_toggle, the dashed lines with their toggles, labels, file paths, other_profile flags and picks (one entry per shown file), and the legend (Ref).
Example
fig = Figure()
area = profile_plot_area!(fig[1, 2])
panel = control_panel!(fig[1, 1], "Compare picks"; collapsible = true, width = 340)
compare_toggle = Toggle(fig[2, 1]; active = true)
compare = compare_plot_state(area.profile, panel, area.colorbar, compare_toggle)
add_compare_picks!(compare, Picks([(100.0, -50.0), (300.0, -80.0)]), "example picks")AdriaArrayGeometryPicker.add_compare_picks! Function
add_compare_picks!(state, picks::Picks, label; other_profile = false, horizontal = false) -> line plotShow picks (not editable) as a dashed coloured line on top of a solid white one in the profile axis, with a toggle and label in the panel of the state and an entry in the comparison legend (the last legend of the colorbar layout; greyed out by Makie while the line is hidden). The line is visible while its toggle and the compare_toggle of the state (if any) are on. With horizontal = true (horizontal slice) the picks are drawn at their longitude / latitude. With other_profile = true (picks of another profile) the label in the panel gets a red background. state is created by compare_plot_state.
Arguments
state: the compare state.picks: thePicksto show (the first two coordinates are drawn).label: text of the label and the legend entry.
Keywords
other_profile: mark the picks as belonging to another profile.horizontal: the picks are made on a horizontal slice (drawn at lon / lat).
Returns the dashed line plot. Needs no profile data itself; the coordinates of picks must be those of the profile axis (see current_picks for the columns of picks made on a profile).
AdriaArrayGeometryPicker.load_compare_picks! Function
load_compare_picks!(session, path) -> BoolLoad the pick file path (see load_picks) and show its picks for comparison (see add_compare_picks!), labelled with the file name (and the user, if stored). Picks of another profile (see picks_match_profile) are shown as well, as they may still be useful, but their label in the panel gets a red background. Switches the compare_toggle of the compare state (if it has one) on, so that the new line is visible.
Arguments
session: aNamedTuple(or any object with these properties) with the fieldsprofile(theObservablethat holds the profile) andcompare(the state ofcompare_plot_state), e.g.(; profile, compare); the session of the picker (seemenu_selection) has both.path: a pick file (seeload_picks).
Returns true if the picks were added; false (and warns) if no profile is loaded or the file is already shown. Requires a GeophysicalModelGenerator ProfileData.
AdriaArrayGeometryPicker.clear_compare_picks! Function
clear_compare_picks!(state)Remove all picks shown for comparison (their lines, toggles, labels and the legend). Called when another profile is loaded, since the picks belong to the previous one (see connect_profile!). Needs no profile.
Arguments
state: the compare state ofcompare_plot_state.
Returns state.
Picks on a profile
AdriaArrayGeometryPicker.picking_state Function
picking_state(fig, profile::Observable, ax::Axis; active = Observable(true)) -> NamedTuple
picking_state(fig, profile::Observable, axes, widgets) -> NamedTupleMake the profile axis pickable with pickable! and return its picking state (; picks, scatter, dragging, index). The data coordinates of the picks are x (distance along the profile) and depth; on a horizontal slice they are longitude and latitude instead. picks holds them as Point3d (double precision, z = PICK_Z); set them from code with set_pick_points! or set_picks!. Profile-specific wrapper of the generic pickable!; requires a GeophysicalModelGenerator ProfileData in profile.
Picking is active while active is true. A newly loaded profile clears the picks (listener of profile with priority -20). See current_picks and set_picks! for the connection to Picks.
Arguments
fig: the figure of the window (figofpicker_layout).profile:Observablethat holds the profile.ax: the profile axis.axes,widgets: as returned bypicker_layout; picks are made inaxes.profilewhile the "Picking" togglewidgets.pick_toggleis on.
Keywords
active:Observable{Bool}that switches picking on and off, e.g. theactiveof aToggle(default: always on).
Returns
The picking state of pickable!, (; picks, scatter, dragging, index).
Example
# window of `picker_layout`
profile = Observable{Any}(nothing)
fig, panels, axis, widgets = picker_layout(; profile)
picking = picking_state(fig, profile, axis, widgets)
# own layout
fig = Figure()
area = profile_plot_area!(fig[1, 1])
toggle = Toggle(fig[2, 1]; active = true)
picking = picking_state(fig, profile, area.profile; active = toggle.active)AdriaArrayGeometryPicker.pick_lonlat Function
pick_lonlat(profile, x) -> (lon, lat)Longitude and latitude of the picks at the distances x along profile, interpolated linearly from the coordinates of the profile data (see profile_track). NaN for distances outside the profile, and for all picks if the profile has no coordinates. Vertical profiles only; requires a GeophysicalModelGenerator ProfileData.
Arguments
profile: the loaded profile (ornothing).x: vector of distances along the profile.
Returns the vectors (lon, lat), one value per distance.
AdriaArrayGeometryPicker.profile_track Function
profile_track(profile) -> Union{Nothing,NTuple{3,Vector{Float64}}}Distance along the profile (x_profile) with the longitude and latitude at each distance, sorted by distance: from the topography of profile (TopoData, finest sampling) or, without topography, from the first column of the volume data. nothing if neither is available (and for horizontal slices, which have no x_profile). Requires a GeophysicalModelGenerator ProfileData of a vertical cross-section.
Returns (x, lon, lat), three vectors of the same length.
AdriaArrayGeometryPicker.picks_match_profile Function
picks_match_profile(profile, picks::Picks; warn = true) -> BoolWhether picks belong to profile: they were made on the same type of profile (vertical or horizontal; files that do not say are taken as vertical) and their start / end coordinates (vertical) or depth (horizontal), if the file has them, are those of the profile. Requires a GeophysicalModelGenerator ProfileData.
Arguments
profile: the loaded profile (ornothing).picks: thePicks, e.g. fromload_picks; its metadataprofile_type,start_lonlat,end_lonlatanddepthare compared.
Keywords
warn: log a warning that says why the picks do not match.
Returns true if the picks match, false if not or if no profile is loaded.
Settings of a window
AdriaArrayGeometryPicker.gui_settings Function
gui_settings(session) -> Dict{String,Any}The settings of the GUI that can be restored together with the profile (see apply_settings!, save_gui_state). Only the parts that the session has are read; a custom GUI without e.g. a map overview or compared picks simply has no such entries. Requires a GeophysicalModelGenerator ProfileData to be loaded.
Arguments
session: aNamedTuple(or any object with these properties). The session of the picker (seemenu_selection) has all of them; a custom GUI passes those it has, with these names (all optional exceptprofile):
| session field | entries of the settings |
|---|---|
profile (required) | – (the profile type decides how compared picks are stored) |
volume (volume_plot_state) | "volume_field", "volume_limits", "volume_cmap", "volume_cmap_reverse"; with contour widgets also "contour_field", "contour_visible", "contour_limits", "contour_cmap", "contour_cmap_reverse", "contour_levels" |
surface (surface_plot_state) | "surface_visible": label => toggle state of each surface |
points (point_plot_state) | "point_visible": label => toggle state of each point data set |
topography (topography_panel_state) | "topography_controls": "dataset" (if there are several), "show" and "fill" (vertical profile) or "mode", "opacity" and "levels" (horizontal slice); empty without topography data |
topomap (map_plot_state) | "topography_path": the topography file of the map overview ("" if none) |
picking (picking_state) | "picks": the editable picks as x and depth vectors (longitude and latitude on a horizontal slice) |
compare (compare_plot_state) | "compare_picks": one Dict per file shown for comparison with its label, path, the picks (x, depth), other_profile and visible (its toggle); "compare_visible": the compare_toggle of the state, if it has one |
widgets.pick_name (a Textbox) | "user_name" |
widgets.<name>_panel_toggle (Toggles, <name> one of volume, surface, point, topography, compare, map, screenshot) | "expanded_panels": name => toggle state |
The picks themselves are stored (not only the paths of their files), so that a state can be restored even if the pick files have been moved or deleted.
Returns
The settings as a Dict{String,Any} of plain values (strings, numbers, vectors, dictionaries), so that it can be stored in a JLD2 file.
Example
fig = Figure()
area = profile_plot_area!(fig[1, 2])
volume_panel = control_panel!(fig[1, 1], "Volume data"; width = 340)
volume = volume_plot_state(area.profile, volume_panel!(volume_panel; contours = false), area.colorbar)
profile = Observable{Any}(nothing)
connect_profile!(profile, (; volume); axes = area)
picking = picking_state(fig, profile, area.profile)
load_profile!(profile, joinpath(pkgdir(AdriaArrayGeometryPicker), "assets", "Profile1.pgmg"))
session = (; profile, volume, picking)
settings = gui_settings(session) # "volume_field", "volume_limits", ..., "picks"
apply_settings!(session, settings)AdriaArrayGeometryPicker.apply_settings! Function
apply_settings!(session, settings) -> sessionRestore settings (see gui_settings) in the GUI. A profile has to be loaded already (requires a GeophysicalModelGenerator ProfileData). Only the parts that the session has are restored: entries for parts it does not have (e.g. contour settings for a volume state without contour widgets, or a map overview) are skipped, as are settings that do not fit the profile (e.g. a field that it does not contain; a warning is logged).
Arguments
session: as forgui_settings; the same fields are read (and written).settings: aDictas returned bygui_settings, e.g. from a state file.
Returns session. The topography of the map overview is loaded again from its file (if the session has a topomap and the file still exists).
Generic: panels and widgets
These do not depend on the profile data and work in any Makie figure.
AdriaArrayGeometryPicker.control_panel! Function
control_panel!(gp, title; color = :steelblue1, empty_height = 20, collapsible = false,
expanded = true, width = nothing) -> GridLayoutCreate a menu panel at the grid position gp: a colored header with title and below it an empty GridLayout for the panel's widgets, which is returned. Generic.
Arguments
gp: grid position, e.g.fig[1, 1].title: text of the header.
Keywords
color: color of the header.empty_height: height of the content while it has no widgets.collapsible: add an expand / collapse toggle to the header.expanded: initial state of the toggle (only withcollapsible = true).width: fixed width of the panel (default: determined by the content).
An empty layout has no size that Makie can determine, which would make the whole window layout undeterminable. The content row therefore starts with the fixed height empty_height; call fit_panel! once widgets have been added to make it follow its content.
With collapsible = true the header gets a toggle (on: expanded, initially expanded) that shows or hides the widgets of the panel; width fixes the panel width, so that the layout does not move when panels are collapsed. Collapsible panels start expanded unless expanded = false is given; exclusive_panels! keeps only one of several panels expanded.
Example
fig = Figure()
content = control_panel!(fig[1, 1], "Options"; collapsible = true)
Button(content[1, 1]; label = "Go")
fit_panel!(content)AdriaArrayGeometryPicker.fit_panel! Function
fit_panel!(content::GridLayout) -> BoolLet the menu panel whose content layout is content follow the size of its widgets; call it after adding widgets. Panels without widgets keep their fixed height, because an empty layout has no determinable size. A collapsible panel whose toggle is off is collapsed instead: its widgets are hidden and its content has no height. Widgets added later are hidden as well when fit_panel! is called again. Generic.
Arguments
content: the content layout of a panel, as returned bycontrol_panel!. The panel is resized through the layout aroundcontent(the row below the header), so any other layout throws anArgumentError. The plot states,volume_panel!andmap_panel!callfit_panel!on their panel, so their panels have to be made bycontrol_panel!as well.
Returns true if the panel now follows its content, false if it is empty or collapsed.
Example
fig = Figure()
panel = control_panel!(fig[1, 1], "Options")
Button(panel[1, 1]; label = "Go")
fit_panel!(panel) # trueAdriaArrayGeometryPicker.set_visible! Function
set_visible!(layout::GridLayout, visible::Bool)Show or hide all widgets in layout, including those in nested layouts. Hidden widgets still react to the mouse, and those of a collapsed panel sit on top of the panels below it, so hidden widgets with a mouse interaction are also shrunk to zero size (and get their size back when they are shown again). Generic. Returns nothing.
AdriaArrayGeometryPicker.panel_toggle Function
panel_toggle(content::GridLayout) -> Union{Toggle,Nothing}The expand / collapse toggle of the collapsible panel whose content layout is content (as returned by control_panel! with collapsible = true), nothing for a panel that is not collapsible. Generic.
AdriaArrayGeometryPicker.exclusive_panels! Function
exclusive_panels!(contents) -> Vector{Toggle}Let only one of the collapsible panels with the content layouts contents (a tuple or vector of layouts returned by control_panel! with collapsible = true) be expanded at a time: expanding one collapses the others. If several of them are expanded when this is called (control_panel! creates expanded panels by default), the first expanded one stays expanded and the others are collapsed. Returns the toggles of the panels (see panel_toggle), in the order of contents. Generic. Throws an ArgumentError if one of the panels is not collapsible.
AdriaArrayGeometryPicker.link_range_controls! Function
link_range_controls!(slider, minbox, maxbox, range::Observable, label = nothing)Connect an IntervalSlider with a min and a max Textbox: moving the slider sets range to its interval and shows the limits in the text fields (and in label, if given); entering a number in a text field moves the slider to the closest possible value. Generic.
Arguments
slider: anIntervalSlider.minbox,maxbox:Textboxes for the lower and upper limit.range:Observablethat receives the interval(lo, hi).label: optionalLabelshowing the limits as "Colorbar limits: lo to hi".
AdriaArrayGeometryPicker.parse_levels Function
parse_levels(text) -> Union{Nothing,Vector{Float64}}Contour levels from a string of numbers separated by commas, semicolons or spaces, sorted and without duplicates. nothing if text is empty or not a list of numbers. Generic.
Example
parse_levels("1, 2.5; 0.5 2.5") # [0.5, 1.0, 2.5]
parse_levels("a b") # nothingAdriaArrayGeometryPicker.label_of Function
label_of(option) -> StringThe text a dropdown shows for one of its options (a string, or a (label, value) tuple).
AdriaArrayGeometryPicker.select_label! Function
select_label!(menu::Menu, label) -> BoolSelect the entry of the dropdown menu whose text is label (options are strings or (label, value) tuples), as if the user had chosen it: menu.selection changes and its listeners run. Generic.
Arguments
menu: aMenu.label: the text of the entry (nothingselects nothing).
Returns true if the entry was found and selected, false otherwise (the selection is then left alone).
Example
fig = Figure()
menu = Menu(fig[1, 1]; options = ["Heatmap", "Contours", "Off"])
select_label!(menu, "Contours") # trueAdriaArrayGeometryPicker.set_text! Function
set_text!(box::Textbox, text::AbstractString)Set the text of the text field box programmatically, as if the user had typed text and pressed enter: the shown text (displayed_string) and the stored text (stored_string, whose listeners run) change. An empty text clears the field (stored_string becomes nothing). Generic. Returns the new stored text.
Example
fig = Figure()
levels = Textbox(fig[1, 1]; placeholder = "Contour levels")
set_text!(levels, "-1, 0, 1")Generic: picking on an axis
AdriaArrayGeometryPicker.PICK_Z Constant
PICK_Z = 1000z value of the pick markers of pickable!: the third coordinate of every pick, so that the markers are drawn above the heatmap, contours, surface lines and points. Generic. Use set_pick_points! to set picks from code without handling it yourself.
AdriaArrayGeometryPicker.pickable! Function
pickable!(fig, ax::Axis; active = Observable(true)) -> NamedTupleMake ax pickable and return the picking state (; picks, scatter, dragging, index). Generic: works on any Axis, independent of the profile data. picks is an Observable{Vector{Point3d}} with the picked points in data coordinates of ax, in double precision (Point3d = Point3{Float64}), so values set from code or loaded from a file are kept exactly. The third coordinate is always PICK_Z; it only keeps the markers on top of everything else. The picks are drawn by scatter.
Picking is active while active (e.g. the active Observable of a Toggle) is true. The mouse is handled with high priority, so clicks on picks do not zoom or pan the axis:
A + click adds a pick at the mouse position (inside
ax),D + click removes the pick under the mouse,
click and drag a pick moves it (release the button to drop it).
(Holding ctrl in addition is allowed.) Picks(state.picks[]) converts the picks to a Picks; set_pick_points! sets them from code.
Arguments
fig: the figure that containsax(its mouse events are used).ax: the axis to pick on.
Keywords
active:Observable{Bool}that switches picking on and off (default: always on).
Returns
The picking state (; picks, scatter, dragging, index). dragging and index are Refs used internally to track the pick being moved. For picking on a GMG profile see picking_state.
Example
fig = Figure()
ax = Axis(fig[1, 1])
toggle = Toggle(fig[2, 1]; active = true)
state = pickable!(fig, ax; active = toggle.active)
on(state.picks) do pts
println(length(pts), " picks")
endAdriaArrayGeometryPicker.set_pick_points! Function
set_pick_points!(picking, points) -> pickingReplace the picks of the picking state picking (of pickable! or picking_state) by points, given as 2D points in data coordinates of the axis: any indexable objects with at least two coordinates (tuples, Point2, Point3, vectors; a third coordinate is ignored). The picks are stored in double precision with the z value PICK_Z, which keeps the markers on top. Generic.
Arguments
picking: the picking state; only its fieldpicksis used.points: the new picks, e.g.[(1.0, 2.0), (3.0, 4.0)]orpoints(p)of aPicks(an empty vector removes all picks).
Returns picking.
Example
fig = Figure()
ax = Axis(fig[1, 1])
picking = pickable!(fig, ax)
set_pick_points!(picking, [(1.0, 2.0), (3.0, 4.5)])
picking.picks[] # [Point3d(1.0, 2.0, 1000.0), Point3d(3.0, 4.5, 1000.0)]Generic: file dialogs and screenshots
The native file dialogs block the thread they run on. The with_* functions run them in a background task, so that the window keeps drawing while a dialog is open. The dialogs can be replaced, for example in tests:
AdriaArrayGeometryPicker.OPEN_DIALOG[] = (; kwargs...) -> "picks.aagpp"AdriaArrayGeometryPicker.open_dialog Function
open_dialog(; filter = "", dir = "") -> Union{String,Nothing}Show a native "open file" dialog and return the chosen path, or nothing if cancelled. Generic, independent of any profile data.
Keywords
filter: file filter in NativeFileDialog syntax, e.g."aagpp,csv"or"png;jpg,jpeg".dir: directory the dialog starts in (default: the dialog's own choice).
This call blocks; inside GUI callbacks use with_open_dialog. The dialog can be replaced (e.g. in tests) by setting AdriaArrayGeometryPicker.OPEN_DIALOG[].
AdriaArrayGeometryPicker.save_dialog Function
save_dialog(; filter = "", dir = "") -> Union{String,Nothing}Show a native "save file" dialog and return the chosen path, or nothing if cancelled. Keywords as for open_dialog. Generic, independent of any profile data. This call blocks; inside GUI callbacks use with_save_dialog.
AdriaArrayGeometryPicker.with_open_dialog Function
with_open_dialog(f; filter = "", dir = "") -> TaskShow an "open file" dialog without blocking the GUI and call f(path) with the chosen path. Nothing happens if the dialog is cancelled. Errors thrown by f are logged, not rethrown. The task's result is the return value of f (or nothing). Keywords as for open_dialog. Generic.
Example
with_open_dialog(path -> @info("chosen: $path"); filter = "csv")AdriaArrayGeometryPicker.with_save_dialog Function
with_save_dialog(f; filter = "", dir = "") -> TaskShow a "save file" dialog without blocking the GUI and call f(path) with the chosen path. Keywords as for open_dialog; see with_open_dialog. Generic.
AdriaArrayGeometryPicker.save_picks_dialog Function
save_picks_dialog(picks; extra = nothing, filter = "aagpp,csv", dir = "") -> TaskAsk for a file name and save picks there (see save_picks). picks can be a Picks, a vector of points, or a zero-argument function returning either (evaluated when the dialog is closed, so the state at that moment is saved). Generic.
Arguments
picks: what to save, see above.
Keywords
extra: functionpoints -> NamedTuplecomputing additional columns, seesave_picks.filter,dir: as foropen_dialog.
Returns the Task of with_save_dialog; its result is the path written.
AdriaArrayGeometryPicker.load_picks_dialog Function
load_picks_dialog(f = identity; filter = "aagpp,jld2,csv", dir = "") -> TaskAsk for a pick file, load it with load_picks and call f(picks) with the resulting Picks. Generic.
Arguments
f: function called with the loaded picks (defaultidentity).
Keywords
filter,dir: as foropen_dialog.
Returns the Task of with_open_dialog; its result is the return value of f.
Example
load_picks_dialog(p -> println(length(p), " picks loaded"))AdriaArrayGeometryPicker.OPEN_DIALOG Constant
Function (; filter, dir) -> path used by open_dialog. Return "" or nothing on cancel.
AdriaArrayGeometryPicker.SAVE_DIALOG Constant
Function (; filter, dir) -> path used by save_dialog. Return "" or nothing on cancel.
AdriaArrayGeometryPicker.PICKS_FILTER Constant
File filter for pick files, in NativeFileDialog syntax.
sourceAdriaArrayGeometryPicker.IMAGE_FILTER Constant
File filter for screenshots, in NativeFileDialog syntax.
sourceAdriaArrayGeometryPicker.save_screenshot Function
save_screenshot(path, fig; px_per_unit = 2, kwargs...) -> StringSave fig as an image and return the path written. A path without extension gets .png appended. Generic: works for any Makie Figure (or scene).
Arguments
path: output file; the format follows the extension (e.g..png).fig: the figure to save.
Keywords
px_per_unit: resolution scaling, 2 by default (twice the window resolution).kwargs...: passed toMakie.save.
Example
fig = Figure()
lines(fig[1, 1], 1:10)
save_screenshot(joinpath(tempdir(), "window.png"), fig)AdriaArrayGeometryPicker.save_screenshot_dialog Function
save_screenshot_dialog(fig; filter = "png", dir = "", kwargs...) -> TaskAsk for a file name and save a screenshot of fig there (see save_screenshot). Generic. Keywords filter and dir are as for open_dialog; the remaining kwargs... are passed to save_screenshot. Returns the Task of with_save_dialog.