meteogate / ui.R
alexdum's picture
Sync current state to Hugging Face
edbd958
# ui.R
ui <- page_navbar(
id = "main_nav",
header = tagList(
shinyjs::useShinyjs(),
tags$head(
tags$link(rel = "stylesheet", type = "text/css", href = "styles.css"),
tags$script(src = "app.js"),
# Layer Control Interactions
tags$script(HTML("
$(document).ready(function() {
// Layer Control Interactions
var $layerControl = $('.map-layer-control');
$layerControl.on('mouseenter', function() {
$(this).addClass('expanded');
});
$layerControl.on('mouseleave', function() {
$(this).removeClass('expanded');
});
// Auto-close when a radio button (basemap) is selected
$layerControl.on('change', 'input[type=\\'radio\\']', function() {
$layerControl.removeClass('expanded');
});
});
"))
),
# Frozen overlay div (active by default to show 'Loading base map...' on startup)
tags$script(HTML("document.body.classList.add('ui-frozen');")),
div(
class = "frozen-overlay active",
div(
class = "frozen-overlay-content",
tags$button(class = "frozen-overlay-close", style = "display: none;", "×", title = "Cancel loading"),
div(class = "spinner-border text-primary", role = "status"),
p(class = "frozen-overlay-message", "Loading base map..."),
div(
id = "fetch_progress_container",
style = "display: none; width: 100%; margin-top: 20px;",
div(
class = "progress",
style = "height: 8px;",
div(
id = "fetch_progress_bar",
class = "progress-bar progress-bar-striped progress-bar-animated",
role = "progressbar",
style = "width: 0%; transition: width 0.3s ease;"
)
),
p(id = "fetch_progress_status", style = "font-size: 0.85rem; color: #666; margin-top: 8px;", "")
)
)
)
),
title = "EuroMeteo Explorer",
theme = bs_theme(version = 5, bootswatch = "cosmo"),
fillable = c("Map View", "Stations Info"),
sidebar = sidebar(
id = "main_sidebar",
title = "Filters",
open = list(desktop = "open", mobile = "closed"),
# Country Filter
selectizeInput(
"country_selector",
label = span(
"Zoom to Country",
tooltip(
bs_icon("info-circle", size = "0.9em", class = "ms-1", style = "cursor: help; color: #0056b3;"),
"Filter the list of stations by country. Selecting a country will automatically zoom the map to its extent.",
placement = "right"
)
),
choices = NULL,
multiple = TRUE,
options = list(placeholder = "All Countries", maxItems = 1, plugins = list("remove_button"))
),
# Station Search
selectizeInput(
"station_selector",
label = span(
"Find Station",
tooltip(
bs_icon("info-circle", size = "0.9em", class = "ms-1", style = "cursor: help; color: #0056b3;"),
"Search for a specific station by name or ID. Selecting a station will load its data in the Dashboard tab.",
placement = "right"
)
),
choices = NULL,
multiple = FALSE,
options = list(placeholder = "Type to search...")
),
# Date Filter
dateRangeInput(
"date_range",
label = span(
"Select Period",
tooltip(
bs_icon("info-circle", size = "0.9em", class = "ms-1", style = "cursor: help; color: #0056b3;"),
"This range controls the available data for the dashboard. The map's time slider (0-23h) is anchored to the 23:00 UTC hour of your selected End Date.",
placement = "right"
)
),
start = default_start_date,
end = default_end_date,
min = "2026-03-05",
max = Sys.Date()
),
# Map Parameter Selector
selectInput(
"map_parameter",
label = span(
"Map Parameter",
tooltip(
bs_icon("info-circle", size = "0.9em", class = "ms-1", style = "cursor: help; color: #0056b3;"),
"Choose the weather parameter to visualize on the map. Data is fetched for the specific hour selected on the map's time slider.",
placement = "right"
)
),
choices = c(
"Air Temperature" = "air_temperature",
"Precipitation" = "precipitation_amount",
"Sea Level Pressure" = "air_pressure_at_mean_sea_level",
"Wind Speed" = "wind_speed"
),
selected = "air_temperature"
),
hr(),
textOutput("station_count_filtered"),
uiOutput("temp_data_info"),
hr(),
p("Data source: MeteoGate / E-SOH"),
p(style = "font-size: 0.8em; color: #666;", "Includes: Air Temperature, Precip, Wind, Solar* all parameters processed"),
p(style = "font-size: 0.8em; color: #666; margin-top: 5px;", "Note: For the current hour, not all stations may have data due to delays between measurement time and loading on the MeteoGate server."),
hr(),
div(
style = "font-size: 0.7em; color: #aaa; margin-top: 5px;",
"Map data \u00a9 ", tags$a(href = "https://www.openstreetmap.org/copyright", target = "_blank", "OpenStreetMap"), " contributors via ", tags$a(href = "https://openfreemap.org", target = "_blank", "OpenFreeMap")
)
),
nav_panel(
title = "Map View",
div(
class = "outer",
maplibreOutput("map", height = "100%", width = "100%"),
# Layer Control Panel (Collapsible)
absolutePanel(
top = 130, left = 10,
class = "map-layer-control",
style = "z-index: 1000;",
div(class = "control-icon", icon("layer-group")),
div(
class = "control-content",
radioButtons(
inputId = "basemap",
label = "Basemap",
choices = c(
"OpenFreeMap Positron" = "ofm_positron",
"OpenFreeMap Bright" = "ofm_bright",
"Satellite (Sentinel-2)" = "sentinel"
),
selected = "ofm_positron"
),
hr(style = "margin: 8px 0;"),
checkboxInput(
inputId = "show_labels",
label = "Show Labels",
value = TRUE
),
checkboxInput(
inputId = "detailed_tooltips",
label = "Detailed Hover Cards (slower)",
value = FALSE
)
)
),
# Temperature Legend
absolutePanel(
bottom = 200, right = 10,
class = "map-legend-panel",
style = "z-index: 1000;",
uiOutput("map_legend")
),
# Home Button Overlay
absolutePanel(
id = "zoom_home_panel",
top = 80, left = 10,
actionButton("zoom_home", bsicons::bs_icon("house-fill"), class = "btn-home", title = "Zoom to all stations")
),
# Time Slider at bottom of map
absolutePanel(
id = "time_slider_panel",
bottom = 10, left = 60, right = 60,
class = "time-slider-panel-outer",
style = "z-index: 1000; background: rgba(255,255,255,0.95); padding: 5px 20px; border-radius: 8px; box-shadow: 0 2px 6px rgba(0,0,0,0.15);",
div(
style = "display: flex; flex-direction: column; align-items: center;",
div(
style = "display: flex; align-items: center; gap: 15px; width: 100%;",
bsicons::bs_icon("clock-history", size = "1.2em"),
div(
style = "flex: 1;",
uiOutput("time_slider_ui")
)
),
div(
style = "margin-top: -5px; padding-bottom: 5px; font-weight: 500;",
textOutput("selected_time_label", inline = TRUE)
)
)
)
)
),
nav_panel(
title = "Stations Info",
card(
card_header(
div(
style = "display: flex; align-items: center; gap: 10px;",
bsicons::bs_icon("info-circle-fill", class = "text-primary"),
span(class = "d-none d-md-inline", "Double-click a row to load station data and view the dashboard."),
span(class = "d-inline d-md-none", "Tap a row to load station data and view the dashboard.")
)
),
DT::dataTableOutput("table")
)
),
nav_panel(
title = "Dashboard",
value = "Dashboard",
conditionalPanel(
condition = "!output.station_ready",
div(
style = "height: 600px; display: flex; align-items: center; justify-content: center;",
p("Select a station from the map or double-click a row in the Stations Info table to view the dashboard.", style = "color: #999;")
)
),
conditionalPanel(
condition = "output.station_ready",
uiOutput("station_info_header"),
navset_card_pill(
id = "dashboard_subtabs",
nav_panel(
title = "Plots",
uiOutput("details_tabs")
),
nav_panel(
title = "Data",
div(
style = "margin-top: 10px;",
uiOutput("param_definitions_ui"),
DT::dataTableOutput("hourly_data_table")
)
)
)
)
),
nav_spacer(),
nav_item(
tooltip(
tags$a(
id = "fullscreen_toggle",
href = "#",
onclick = "toggleFullScreen(); return false;",
style = "display: flex; align-items: center; color: rgba(0,0,0,0.55); padding: 0.5rem 1rem; text-decoration: none;",
bsicons::bs_icon("arrows-fullscreen", size = "1.2rem")
),
"Toggle Fullscreen",
placement = "bottom"
)
)
)