| | source("funs/scraper.R") |
| | source("funs/map_helpers.R") |
| | source("funs/plot_weather_jma.R") |
| |
|
| | server <- function(input, output, session) { |
| | selected_station <- reactiveVal(NULL) |
| | station_data <- reactiveVal(NULL) |
| | prev_dates <- reactiveVal(NULL) |
| | url_initialized <- reactiveVal(FALSE) |
| |
|
| | |
| | style_change_trigger <- reactiveVal(0) |
| | map_initialized <- reactiveVal(FALSE) |
| | stations_before_id <- reactiveVal(NULL) |
| | current_raster_layers <- reactiveVal(character(0)) |
| |
|
| | |
| | parse_url_params <- function(query_string) { |
| | if (is.null(query_string) || query_string == "" || query_string == "?") { |
| | return(list()) |
| | } |
| | |
| | qs <- sub("^\\?", "", query_string) |
| | if (qs == "") { |
| | return(list()) |
| | } |
| |
|
| | pairs <- strsplit(qs, "&")[[1]] |
| | params <- list() |
| | for (pair in pairs) { |
| | kv <- strsplit(pair, "=")[[1]] |
| | if (length(kv) == 2) { |
| | params[[kv[1]]] <- URLdecode(kv[2]) |
| | } |
| | } |
| | params |
| | } |
| |
|
| | |
| | broadcast_state <- function(view_override = NULL) { |
| | st <- selected_station() |
| | |
| | station_id <- if (!is.null(st) && nrow(st) > 0) as.character(st$ID[[1]]) else NULL |
| | station_name <- if (!is.null(st) && nrow(st) > 0) as.character(st$DisplayName[[1]]) else NULL |
| | prefecture <- if (!is.null(st) && nrow(st) > 0 && !is.na(st$PrecName[[1]])) as.character(st$PrecName[[1]]) else NULL |
| | resolution <- input$data_resolution |
| |
|
| | |
| | main_tab <- input$main_nav |
| | view <- if (!is.null(view_override)) { |
| | view_override |
| | } else if (!is.null(main_tab)) { |
| | if (main_tab == "Map View") { |
| | "map" |
| | } else if (main_tab == "Station Info") { |
| | "station-info" |
| | } else if (main_tab == "Dashboard") { |
| | subtab <- input$dashboard_subtabs |
| | if (!is.null(subtab) && subtab == "Data") { |
| | "dashboard-data" |
| | } else { |
| | "dashboard-plots" |
| | } |
| | } else { |
| | "map" |
| | } |
| | } else { |
| | "map" |
| | } |
| |
|
| | start_date <- if (!is.null(input$date_range)) as.character(input$date_range[1]) else NULL |
| | end_date <- if (!is.null(input$date_range)) as.character(input$date_range[2]) else NULL |
| |
|
| | session$sendCustomMessage("updateParentURL", list( |
| | station = station_id, |
| | stationName = station_name, |
| | prefecture = prefecture, |
| | resolution = resolution, |
| | view = view, |
| | start = start_date, |
| | end = end_date |
| | )) |
| | } |
| |
|
| | |
| | observe({ |
| | req(!url_initialized()) |
| |
|
| | query <- session$clientData$url_search |
| | params <- parse_url_params(query) |
| |
|
| | if (length(params) == 0) { |
| | url_initialized(TRUE) |
| | return() |
| | } |
| |
|
| | |
| | if (!is.null(params$resolution) && params$resolution %in% c("10 Minutes", "Hourly", "Daily", "Monthly")) { |
| | updateRadioButtons(session, "data_resolution", selected = params$resolution) |
| | } |
| |
|
| | |
| | if (!is.null(params$start) && !is.null(params$end)) { |
| | start_date <- tryCatch(as.Date(params$start), error = function(e) NULL) |
| | end_date <- tryCatch(as.Date(params$end), error = function(e) NULL) |
| | if (!is.null(start_date) && !is.null(end_date)) { |
| | updateDateRangeInput(session, "date_range", start = start_date, end = end_date) |
| | prev_dates(c(start_date, end_date)) |
| | } |
| | } |
| |
|
| | |
| | if (!is.null(params$station)) { |
| | station_id <- params$station |
| | |
| | st <- stations %>% |
| | filter(ID == station_id | DisplayName == station_id) %>% |
| | head(1) |
| | if (nrow(st) > 0) { |
| | selected_station(st) |
| | updateSelectizeInput(session, "station_selector", selected = st$DisplayName) |
| | |
| | shinyjs::delay(500, { |
| | start_download(st) |
| | }) |
| | } |
| | } |
| |
|
| | |
| | if (!is.null(params$view)) { |
| | view <- params$view |
| | shinyjs::delay(600, { |
| | if (view == "map") { |
| | session$sendCustomMessage("switchTab", list(tabId = "Map View")) |
| | } else if (view == "station-info") { |
| | session$sendCustomMessage("switchTab", list(tabId = "Station Info")) |
| | } else if (view %in% c("dashboard-plots", "dashboard-data")) { |
| | session$sendCustomMessage("switchTab", list(tabId = "Dashboard")) |
| | if (view == "dashboard-data") { |
| | shinyjs::delay(800, { |
| | |
| | |
| | shinyjs::runjs("$('a[data-value=\"Data\"]').tab('show');") |
| | }) |
| | } |
| | } |
| | }) |
| | } |
| |
|
| | url_initialized(TRUE) |
| | }) |
| |
|
| | |
| | generate_label <- function(name, id, type, start, end, vars) { |
| | paste0( |
| | "<div style='font-size:14px; min-width: 200px;'>", |
| | "<b>", htmltools::htmlEscape(name), "</b> (", id, ")<br>", |
| | "Type: ", type, "<br>", |
| | "Period: ", start, " - ", end, "<br>", |
| | "<div style='margin-top:5px; font-size:12px; color:#555;'>", |
| | "Vars: ", substring(vars, 1, 50), "...", |
| | "</div>", |
| | "</div>" |
| | ) |
| | } |
| |
|
| | add_resolution_period <- function(df, resolution) { |
| | df %>% |
| | mutate( |
| | ResolutionStart = case_when( |
| | resolution == "Daily" ~ Start_Daily, |
| | resolution == "Hourly" ~ Start_Hourly, |
| | resolution == "10 Minutes" ~ Start_10min, |
| | resolution == "Monthly" ~ Start_Monthly, |
| | TRUE ~ NA_real_ |
| | ), |
| | ResolutionEnd = case_when( |
| | resolution == "Daily" ~ End_Daily, |
| | resolution == "Hourly" ~ End_Hourly, |
| | resolution == "10 Minutes" ~ End_10min, |
| | resolution == "Monthly" ~ End_Monthly, |
| | TRUE ~ NA_real_ |
| | ) |
| | ) |
| | } |
| |
|
| | get_resolution_period <- function(st, resolution) { |
| | start_val <- switch(resolution, |
| | "Daily" = st$Start_Daily, |
| | "Hourly" = st$Start_Hourly, |
| | "10 Minutes" = st$Start_10min, |
| | "Monthly" = st$Start_Monthly, |
| | NA_real_ |
| | ) |
| | end_val <- switch(resolution, |
| | "Daily" = st$End_Daily, |
| | "Hourly" = st$End_Hourly, |
| | "10 Minutes" = st$End_10min, |
| | "Monthly" = st$End_Monthly, |
| | NA_real_ |
| | ) |
| | list(start = start_val, end = end_val) |
| | } |
| |
|
| | format_period_labels <- function(start, end, current_year = NULL) { |
| | start_label <- ifelse(is.na(start), "Unknown", start) |
| | if (is.null(current_year)) { |
| | end_label <- ifelse(is.na(end), "Present", end) |
| | } else { |
| | end_label <- ifelse(is.na(end) | end == current_year, "Present", end) |
| | } |
| | list(start = start_label, end = end_label) |
| | } |
| |
|
| | filtered_stations <- reactive({ |
| | res <- stations |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | if (!is.null(input$date_range) && !is.null(input$data_resolution)) { |
| | start_yr <- as.numeric(format(input$date_range[1], "%Y")) |
| | end_yr <- as.numeric(format(input$date_range[2], "%Y")) |
| | resolution <- input$data_resolution |
| |
|
| | |
| | res <- res %>% |
| | add_resolution_period(resolution) %>% |
| | |
| | filter( |
| | !is.na(ResolutionStart) & |
| | ResolutionStart <= end_yr & |
| | (is.na(ResolutionEnd) | ResolutionEnd >= start_yr) |
| | ) %>% |
| | select(-ResolutionStart, -ResolutionEnd) |
| | } |
| |
|
| | res |
| | }) |
| |
|
| |
|
| | output$visible_count <- renderText({ |
| | count <- nrow(filtered_stations()) |
| | sprintf("Visible Stations: %d", count) |
| | }) |
| |
|
| | |
| | observe({ |
| | |
| | |
| | |
| | df <- filtered_stations() |
| |
|
| | |
| | |
| | |
| |
|
| | updateSelectizeInput(session, "station_selector", |
| | choices = c("All", sort(df$DisplayName)), |
| | server = TRUE |
| | ) |
| | }) |
| |
|
| | output$map <- renderMaplibre({ |
| | maplibre( |
| | style = ofm_positron_style, |
| | center = c(138, 36), |
| | zoom = 4 |
| | ) %>% |
| | add_navigation_control(show_compass = FALSE, visualize_pitch = FALSE, position = "top-left") |
| | }) |
| |
|
| | |
| | observe({ |
| | req(!map_initialized()) |
| | req(input$map_zoom) |
| |
|
| | |
| | map_initialized(TRUE) |
| | }) |
| |
|
| | |
| | observeEvent(input$zoom_home, { |
| | df <- filtered_stations() |
| | req(df) |
| | if (nrow(df) > 0) { |
| | |
| | lons <- range(df$Lon, na.rm = TRUE) |
| | lats <- range(df$Lat, na.rm = TRUE) |
| |
|
| | |
| | if (nrow(df) == 1) { |
| | maplibre_proxy("map") %>% |
| | fly_to(center = c(df$Lon[1], df$Lat[1]), zoom = 12) |
| | } else { |
| | maplibre_proxy("map") %>% |
| | fit_bounds(c(lons[1], lats[1], lons[2], lats[2]), animate = TRUE) |
| | } |
| | } |
| | }) |
| |
|
| | |
| | observeEvent(input$main_nav, { |
| | if (input$main_nav == "Map View") { |
| | |
| | shinyjs::runjs(" |
| | setTimeout(function() { |
| | var map = document.getElementById('map'); |
| | if (map && map.__mapgl) { |
| | map.__mapgl.resize(); |
| | } |
| | }, 200); |
| | ") |
| | } |
| | }) |
| |
|
| | |
| | label_layer_ids <- c( |
| | |
| | "waterway_line_label", "water_name_point_label", "water_name_line_label", |
| | "highway-name-path", "highway-name-minor", "highway-name-major", |
| | "highway-shield-non-us", "highway-shield-us-interstate", "road_shield_us", |
| | "airport", "label_other", "label_village", "label_town", "label_state", |
| | "label_city", "label_city_capital", "label_country_3", "label_country_2", "label_country_1", |
| | |
| | "road_oneway", "road_oneway_opposite", "poi_r20", "poi_r7", "poi_r1", "poi_transit", |
| | |
| | "waterway-line-label", "water-name-point-label", "water-name-line-label", |
| | "highway-shield-non-us", "highway-shield-us-interstate", "road-shield-us", |
| | "label-other", "label-village", "label-town", "label-state", |
| | "label-city", "label-city-capital", "label-country-3", "label-country-2", "label-country-1", |
| | |
| | "place_villages", "place_town", "place_country_2", "place_country_1", |
| | "place_state", "place_continent", "place_city_r6", "place_city_r5", |
| | "place_city_dot_r7", "place_city_dot_r4", "place_city_dot_r2", "place_city_dot_z7", |
| | "place_capital_dot_z7", "place_capital", "roadname_minor", "roadname_sec", |
| | "roadname_pri", "roadname_major", "motorway_name", "watername_ocean", |
| | "watername_sea", "watername_lake", "watername_lake_line", "poi_stadium", |
| | "poi_park", "poi_zoo", "airport_label", "country-label", "state-label", |
| | "settlement-major-label", "settlement-minor-label", "settlement-subdivision-label", |
| | "road-label", "waterway-label", "natural-point-label", "poi-label", "airport-label" |
| | ) |
| |
|
| | |
| | |
| | non_label_layer_ids <- c( |
| | "background", "park", "water", "landcover_ice_shelf", "landcover_glacier", |
| | "landuse_residential", "landcover_wood", "waterway", "building", |
| | "tunnel_motorway_casing", "tunnel_motorway_inner", "aeroway-taxiway", |
| | "aeroway-runway-casing", "aeroway-area", "aeroway-runway", |
| | "road_area_pier", "road_pier", "highway_path", "highway_minor", |
| | "highway_major_casing", "highway_major_inner", "highway_major_subtle", |
| | "highway_motorway_casing", "highway_motorway_inner", "highway_motorway_subtle", |
| | "railway_transit", "railway_transit_dashline", "railway_service", |
| | "railway_service_dashline", "railway", "railway_dashline", |
| | "highway_motorway_bridge_casing", "highway_motorway_bridge_inner", |
| | "boundary_3", "boundary_2", "boundary_disputed" |
| | ) |
| |
|
| | apply_label_visibility <- function(proxy, show_labels) { |
| | visibility <- if (isTRUE(show_labels)) "visible" else "none" |
| | for (layer_id in label_layer_ids) { |
| | tryCatch( |
| | { |
| | proxy %>% set_layout_property(layer_id, "visibility", visibility) |
| | }, |
| | error = function(e) { |
| | |
| | } |
| | ) |
| | } |
| | } |
| |
|
| | observeEvent(input$basemap, { |
| | proxy <- maplibre_proxy("map") |
| | basemap <- input$basemap |
| |
|
| | |
| | old_layers <- isolate(current_raster_layers()) |
| | if (length(old_layers) > 0) { |
| | for (layer_id in old_layers) { |
| | proxy %>% clear_layer(layer_id) |
| | } |
| | current_raster_layers(character(0)) |
| | } |
| |
|
| | if (basemap %in% c("ofm_positron", "ofm_bright")) { |
| | |
| | style_url <- switch(basemap, |
| | "ofm_positron" = ofm_positron_style, |
| | "ofm_bright" = ofm_bright_style |
| | ) |
| |
|
| | proxy %>% |
| | set_style(style_url, preserve_layers = FALSE) |
| |
|
| | |
| | stations_before_id("waterway_line_label") |
| |
|
| | current_session <- shiny::getDefaultReactiveDomain() |
| | selected_basemap <- basemap |
| |
|
| | later::later(function() { |
| | shiny::withReactiveDomain(current_session, { |
| | |
| | current_basemap <- isolate(input$basemap) |
| | if (current_basemap != selected_basemap) { |
| | return() |
| | } |
| |
|
| | |
| | apply_label_visibility(maplibre_proxy("map"), isolate(input$show_labels)) |
| | style_change_trigger(isolate(style_change_trigger()) + 1) |
| | }) |
| | }, delay = 0.5) |
| | } else if (basemap == "sentinel") { |
| | |
| | |
| | proxy %>% |
| | set_style(ofm_positron_style, preserve_layers = FALSE) |
| |
|
| | current_session <- shiny::getDefaultReactiveDomain() |
| | selected_basemap <- basemap |
| |
|
| | later::later(function() { |
| | shiny::withReactiveDomain(current_session, { |
| | current_basemap <- isolate(input$basemap) |
| | if (current_basemap != selected_basemap) { |
| | return() |
| | } |
| |
|
| | unique_suffix <- as.numeric(Sys.time()) * 1000 |
| | source_id <- paste0("sentinel_source_", unique_suffix) |
| | layer_id <- paste0("sentinel_layer_", unique_suffix) |
| |
|
| | |
| | maplibre_proxy("map") %>% |
| | add_raster_source(id = source_id, tiles = sentinel_url, tileSize = 256, attribution = sentinel_attribution) %>% |
| | add_layer( |
| | id = layer_id, |
| | type = "raster", |
| | source = source_id, |
| | paint = list("raster-opacity" = 1), |
| | before_id = "background" |
| | ) |
| |
|
| | |
| | for (layer_id_kill in non_label_layer_ids) { |
| | tryCatch( |
| | { |
| | maplibre_proxy("map") %>% set_layout_property(layer_id_kill, "visibility", "none") |
| | }, |
| | error = function(e) { |
| | |
| | } |
| | ) |
| | } |
| |
|
| | |
| | apply_label_visibility(maplibre_proxy("map"), isolate(input$show_labels)) |
| |
|
| | |
| | stations_before_id("waterway_line_label") |
| | current_raster_layers(c(layer_id)) |
| | style_change_trigger(isolate(style_change_trigger()) + 1) |
| | }) |
| | }, delay = 0.5) |
| | } |
| | }) |
| |
|
| | |
| | observeEvent(input$show_labels, |
| | { |
| | apply_label_visibility(maplibre_proxy("map"), input$show_labels) |
| | }, |
| | ignoreInit = TRUE |
| | ) |
| |
|
| | |
| | observe({ |
| | |
| | req(map_initialized()) |
| |
|
| | |
| | style_change_trigger() |
| |
|
| | |
| | current_year <- as.numeric(format(Sys.Date(), "%Y")) |
| | resolution <- input$data_resolution |
| |
|
| | |
| | data <- filtered_stations() %>% |
| | add_resolution_period(resolution) %>% |
| | mutate( |
| | station_label = DisplayName, |
| | station_type_label = ifelse(station_type == "a", "AMeDAS", "Manned station"), |
| | period_start_label = ifelse(is.na(ResolutionStart), "Unknown", ResolutionStart), |
| | period_end_label = ifelse(is.na(ResolutionEnd) | ResolutionEnd == current_year, "Present", ResolutionEnd), |
| | period_label = ifelse(is.na(ResolutionStart), "Unknown", paste0(ResolutionStart, " - ", period_end_label)), |
| | hover_content = generate_label(DisplayName, ID, station_type_label, period_start_label, period_end_label, ObservedVariables) |
| | ) |
| |
|
| | if (nrow(data) > 0) { |
| | |
| | map_data <- st_as_sf(data, coords = c("Lon", "Lat"), crs = 4326) |
| |
|
| | maplibre_proxy("map") %>% |
| | clear_layer("stations") %>% |
| | add_circle_layer( |
| | id = "stations", |
| | source = map_data, |
| | circle_color = "navy", |
| | circle_radius = 6, |
| | circle_stroke_color = "#00000000", |
| | circle_stroke_width = 0, |
| | circle_opacity = 0.7, |
| | tooltip = "hover_content", |
| | before_id = stations_before_id() |
| | ) |
| | } else { |
| | maplibre_proxy("map") %>% clear_layer("stations") |
| | } |
| |
|
| | |
| | st <- isolate(selected_station()) |
| | if (!is.null(st)) { |
| | |
| | period <- get_resolution_period(st, resolution) |
| | period_labels <- format_period_labels(period$start, period$end, current_year) |
| | lbl <- generate_label( |
| | st$DisplayName, st$ID, |
| | ifelse(st$station_type == "a", "AMeDAS", "Manned station"), |
| | period_labels$start, period_labels$end, |
| | st$ObservedVariables |
| | ) |
| | highlight_selected_station(maplibre_proxy("map"), st$Lon, st$Lat, lbl) |
| | } |
| | }) |
| |
|
| | |
| | observeEvent(input$map_feature_click, { |
| | click <- input$map_feature_click |
| |
|
| | |
| | if (!is.null(click) && (isTRUE(click$layer_id == "stations") || isTRUE(click$layer == "stations"))) { |
| | |
| | props <- click$properties |
| | |
| | |
| | |
| |
|
| | id_val <- props$ID |
| |
|
| | if (!is.null(id_val)) { |
| | |
| | closest <- stations %>% |
| | filter(ID == id_val) %>% |
| | head(1) |
| |
|
| | selected_station(closest) |
| |
|
| | |
| | current_year <- as.numeric(format(Sys.Date(), "%Y")) |
| | period <- get_resolution_period(closest, input$data_resolution) |
| | period_labels <- format_period_labels(period$start, period$end, current_year = current_year) |
| | lbl <- generate_label( |
| | closest$DisplayName, closest$ID, |
| | ifelse(closest$station_type == "a", "AMeDAS", "Manned station"), |
| | period_labels$start, period_labels$end, |
| | closest$ObservedVariables |
| | ) |
| | highlight_selected_station(maplibre_proxy("map"), closest$Lon, closest$Lat, lbl) |
| |
|
| | |
| | updateSelectizeInput(session, "station_selector", selected = closest$DisplayName) |
| |
|
| | |
| | start_download(closest) |
| |
|
| | |
| | broadcast_state() |
| | } |
| | } |
| | }) |
| |
|
| | |
| | |
| | check_and_limit_dates <- function(start, end, resolution, anchor = "end") { |
| | limit_days <- Inf |
| | limit_msg <- "" |
| |
|
| | if (resolution %in% c("Hourly", "10 Minutes")) { |
| | limit_days <- 93 |
| | limit_msg <- "3 months" |
| | } else if (resolution == "Daily") { |
| | limit_days <- 366 |
| | limit_msg <- "1 year" |
| | } else if (resolution == "Monthly") { |
| | limit_days <- 1826 |
| | limit_msg <- "5 years" |
| | } |
| |
|
| | diff <- as.numeric(end - start) |
| | if (diff > limit_days) { |
| | new_start <- start |
| | new_end <- end |
| |
|
| | if (anchor == "start") { |
| | |
| | new_end <- start + limit_days |
| | } else { |
| | |
| | new_start <- end - limit_days |
| | } |
| | return(list(start = new_start, end = new_end, limited = TRUE, msg = limit_msg)) |
| | } |
| | return(list(start = start, end = end, limited = FALSE)) |
| | } |
| |
|
| | |
| | observeEvent(input$data_resolution, { |
| | st <- selected_station() |
| | req(st) |
| | req(input$date_range) |
| |
|
| | start <- as.Date(input$date_range[1]) |
| | end <- as.Date(input$date_range[2]) |
| |
|
| | |
| | res_val <- input$data_resolution |
| |
|
| | if (res_val == "Monthly") { |
| | |
| | new_start <- end - 365 * 3 |
| | res <- list(start = new_start, end = end, limited = TRUE, msg = "3 years") |
| | } else { |
| | res <- check_and_limit_dates(start, end, res_val, anchor = "end") |
| | } |
| |
|
| | if (res$limited) { |
| | updateDateRangeInput(session, "date_range", start = res$start, end = res$end) |
| | showNotification(paste0("Range adjusted to ", res$msg, " for ", input$data_resolution, " resolution."), type = "warning") |
| | |
| | prev_dates(c(res$start, res$end)) |
| | } |
| |
|
| | |
| | start_download(st, override_start = res$start, override_end = res$end) |
| | }) |
| |
|
| | |
| | observeEvent(input$date_range, { |
| | st <- selected_station() |
| | req(input$date_range) |
| |
|
| | current_dates <- input$date_range |
| | current_start <- as.Date(current_dates[1]) |
| | current_end <- as.Date(current_dates[2]) |
| |
|
| | last_dates <- prev_dates() |
| |
|
| | |
| | anchor <- "end" |
| |
|
| | if (!is.null(last_dates)) { |
| | last_start <- as.Date(last_dates[1]) |
| | last_end <- as.Date(last_dates[2]) |
| |
|
| | |
| | if (current_start != last_start) { |
| | anchor <- "start" |
| | } |
| | |
| | } |
| |
|
| | |
| | res <- check_and_limit_dates(current_start, current_end, input$data_resolution, anchor = anchor) |
| |
|
| | if (res$limited) { |
| | |
| | updateDateRangeInput(session, "date_range", start = res$start, end = res$end) |
| | showNotification(paste0("Limit exceeded. Adjusted to ", res$msg, "."), type = "warning") |
| | prev_dates(c(res$start, res$end)) |
| |
|
| | |
| | if (!is.null(st)) { |
| | start_download(st, override_start = res$start, override_end = res$end) |
| | } |
| | } else { |
| | |
| | prev_dates(current_dates) |
| | |
| | if (!is.null(st)) { |
| | |
| | if (tab_down_trigger_check(current_dates, last_dates)) { |
| | start_download(st, override_start = current_start, override_end = current_end) |
| | } |
| | } |
| | } |
| | }) |
| |
|
| | |
| | tab_down_trigger_check <- function(curr, last) { |
| | if (is.null(last)) { |
| | return(TRUE) |
| | } |
| | if (curr[1] != last[1] || curr[2] != last[2]) { |
| | return(TRUE) |
| | } |
| | return(FALSE) |
| | } |
| |
|
| | |
| |
|
| | |
| | fetch_stage <- reactiveVal(0) |
| | fetch_queue <- reactiveVal(list()) |
| | fetch_queue_idx <- reactiveVal(0) |
| | parsed_data_list <- reactiveVal(list()) |
| | fetch_current_token <- reactiveVal(NULL) |
| | final_data_path <- reactiveVal(NULL) |
| |
|
| | reset_fetch <- function() { |
| | fetch_stage(0) |
| | fetch_current_token(as.numeric(Sys.time())) |
| | |
| | session$sendCustomMessage("unfreezeUI", list()) |
| | |
| | } |
| |
|
| | |
| | observeEvent(input$cancel_loading, { |
| | reset_fetch() |
| | showNotification("Loading cancelled by user.", type = "warning") |
| | }) |
| |
|
| | start_download <- function(st, override_start = NULL, override_end = NULL) { |
| | req(input$date_range) |
| |
|
| | |
| | station_data(NULL) |
| |
|
| | if (!is.null(override_start)) { |
| | start_date <- as.Date(override_start) |
| | } else { |
| | start_date <- as.Date(input$date_range[1]) |
| | } |
| |
|
| | if (!is.null(override_end)) { |
| | end_date <- as.Date(override_end) |
| | } else { |
| | end_date <- as.Date(input$date_range[2]) |
| | } |
| |
|
| | |
| | |
| | |
| | |
| | |
| |
|
| | |
| | |
| | msg <- paste0("Initializing fetch for ", st$DisplayName, "...") |
| | session$sendCustomMessage("freezeUI", list(text = msg, station = st$DisplayName)) |
| |
|
| | |
| | resolution <- input$data_resolution |
| |
|
| | if (resolution %in% c("Daily", "Monthly")) { |
| | |
| | seq_start <- as.Date(format(start_date, format = "%Y-%m-01")) |
| | seq_end <- as.Date(format(end_date, format = "%Y-%m-01")) |
| |
|
| | |
| | current_month_start <- as.Date(format(Sys.Date(), "%Y-%m-01")) |
| | if (seq_end > current_month_start) { |
| | seq_end <- current_month_start |
| | } |
| |
|
| | dates <- seq(seq_start, seq_end, by = "month") |
| |
|
| | if (length(dates) == 0) { |
| | showNotification("Invalid date range.", type = "error") |
| | reset_fetch() |
| | return() |
| | } |
| |
|
| | |
| | q <- list() |
| | for (i in seq_along(dates)) { |
| | d <- dates[i] |
| | yr <- as.numeric(format(d, format = "%Y")) |
| | mo <- as.numeric(format(d, format = "%m")) |
| | q[[length(q) + 1]] <- list(year = yr, month = mo, day = NULL) |
| | } |
| | } else { |
| | |
| | |
| | today <- Sys.Date() |
| | if (end_date > today) { |
| | end_date <- today |
| | } |
| |
|
| | dates <- seq(start_date, end_date, by = "day") |
| |
|
| | if (length(dates) == 0) { |
| | showNotification("Invalid date range.", type = "error") |
| | reset_fetch() |
| | return() |
| | } |
| |
|
| | |
| | q <- list() |
| | for (i in seq_along(dates)) { |
| | d <- as.Date(dates[i], origin = "1970-01-01") |
| | yr <- as.numeric(format(d, format = "%Y")) |
| | mo <- as.numeric(format(d, format = "%m")) |
| | dy <- as.numeric(format(d, format = "%d")) |
| | q[[length(q) + 1]] <- list(year = yr, month = mo, day = dy) |
| | } |
| | } |
| |
|
| |
|
| | fetch_queue(q) |
| | fetch_queue_idx(1) |
| | parsed_data_list(list()) |
| | fetch_current_token(as.numeric(Sys.time())) |
| |
|
| | fetch_stage(2) |
| | } |
| |
|
| | |
| | observe({ |
| | req(fetch_stage() == 2) |
| | idx <- fetch_queue_idx() |
| | q <- fetch_queue() |
| |
|
| | if (idx > length(q)) { |
| | fetch_stage(6) |
| | } else { |
| | |
| | item <- q[[idx]] |
| | if (is.null(item$day)) { |
| | msg <- paste0( |
| | "Fetching data for ", item$year, "-", sprintf("%02d", item$month), |
| | " (", idx, "/", length(q), ")..." |
| | ) |
| | } else { |
| | msg <- paste0( |
| | "Fetching data for ", item$year, "-", sprintf("%02d", item$month), "-", sprintf("%02d", item$day), |
| | " (", idx, "/", length(q), ")..." |
| | ) |
| | } |
| | session$sendCustomMessage("freezeUI", list(text = msg)) |
| | fetch_stage(4) |
| | } |
| | }) |
| |
|
| | |
| | observe({ |
| | req(fetch_stage() == 4) |
| | token <- fetch_current_token() |
| |
|
| | later::later(function() { |
| | if (!identical(isolate(fetch_current_token()), token)) { |
| | return() |
| | } |
| |
|
| | isolate({ |
| | idx <- fetch_queue_idx() |
| | q <- fetch_queue() |
| | item <- q[[idx]] |
| | st <- selected_station() |
| |
|
| | |
| | df <- tryCatch( |
| | get_jma_data( |
| | block_no = st$ID, |
| | year = item$year, |
| | month = item$month, |
| | day = item$day, |
| | prec_no = st$prec_no, |
| | type = paste0(st$station_type, "1"), |
| | resolution = input$data_resolution |
| | ), |
| | error = function(e) NULL |
| | ) |
| |
|
| | if (!is.null(df)) { |
| | if ("Day" %in% names(df)) { |
| | df <- df %>% |
| | mutate(Date = as.Date(sprintf("%04d-%02d-%02d", Year, Month, Day))) |
| | } |
| | plist <- parsed_data_list() |
| | plist[[length(plist) + 1]] <- df |
| | parsed_data_list(plist) |
| | } |
| |
|
| | fetch_queue_idx(idx + 1) |
| | fetch_stage(2) |
| | }) |
| | }, 0.5) |
| | fetch_stage(-1) |
| | }) |
| |
|
| | |
| | observe({ |
| | req(fetch_stage() == 6) |
| | token <- fetch_current_token() |
| | msg <- "Merging data..." |
| | session$sendCustomMessage("freezeUI", list(text = msg)) |
| |
|
| | later::later(function() { |
| | if (!identical(isolate(fetch_current_token()), token)) { |
| | return() |
| | } |
| |
|
| | isolate({ |
| | plist <- parsed_data_list() |
| |
|
| | if (length(plist) == 0) { |
| | showNotification("No data found for the selected range.", type = "warning", session = session) |
| | reset_fetch() |
| | return() |
| | } |
| |
|
| | |
| | final_df <- bind_rows(plist) |
| |
|
| | |
| | start_date <- input$date_range[1] |
| | end_date <- input$date_range[2] |
| |
|
| | if ("Date" %in% names(final_df)) { |
| | final_df <- final_df %>% |
| | filter(Date >= start_date & Date <= end_date) %>% |
| | select(-Date) |
| | } |
| |
|
| | if (nrow(final_df) == 0) { |
| | showNotification("No data in exact range.", type = "warning", session = session) |
| | reset_fetch() |
| | return() |
| | } |
| |
|
| | |
| | tmp <- tempfile(fileext = ".xlsx") |
| | write_xlsx(final_df, path = tmp) |
| | final_data_path(tmp) |
| |
|
| | |
| | station_data(final_df) |
| |
|
| | |
| | reset_fetch() |
| |
|
| | |
| | if (is.null(input$main_nav) || input$main_nav != "Dashboard") { |
| | session$sendCustomMessage("switchTab", list(tabId = "Dashboard")) |
| | } |
| | showNotification(paste("Successfully loaded", nrow(final_df), "rows."), type = "message", session = session) |
| | }) |
| | }, 0.1) |
| | fetch_stage(-1) |
| | }) |
| |
|
| | |
| | output$station_ready <- reactive({ |
| | !is.null(station_data()) |
| | }) |
| | outputOptions(output, "station_ready", suspendWhenHidden = FALSE) |
| |
|
| | |
| | output$station_info_header <- renderUI({ |
| | st <- selected_station() |
| | if (is.null(st)) { |
| | return(NULL) |
| | } |
| |
|
| | |
| | res_label <- input$data_resolution |
| | res_class <- if (res_label == "Hourly") "bg-primary" else "bg-success" |
| |
|
| | |
| | df <- station_data() |
| | if (is.null(df) || nrow(df) == 0) { |
| | dates_text <- "No data loaded" |
| | } else { |
| | |
| | if ("Date" %in% names(df)) { |
| | d_range <- range(df$Date, na.rm = TRUE) |
| | dates_text <- paste(d_range[1], "to", d_range[2]) |
| | } else if ("Year" %in% names(df) && "Month" %in% names(df) && "Day" %in% names(df)) { |
| | |
| | |
| | start_d <- as.Date(paste(df$Year[1], df$Month[1], df$Day[1], sep = "-")) |
| | n <- nrow(df) |
| | end_d <- as.Date(paste(df$Year[n], df$Month[n], df$Day[n], sep = "-")) |
| | dates_text <- paste(start_d, "to", end_d) |
| | } else { |
| | dates_text <- "Loaded" |
| | } |
| | } |
| |
|
| | |
| | card( |
| | style = "margin-bottom: 20px; border-left: 5px solid #007bff;", |
| | card_body( |
| | padding = 15, |
| | layout_columns( |
| | fill = FALSE, |
| | |
| | div( |
| | strong("Station"), br(), |
| | span(st$DisplayName, style = "font-size: 1.1rem;"), br(), |
| | tags$small(class = "text-muted", paste("ID:", st$ID)) |
| | ), |
| | |
| | div( |
| | strong("Location"), br(), |
| | span(paste0(st$Lat, "°N, ", st$Lon, "°E")), br(), |
| | tags$small(class = "text-muted", st$PrecLabel) |
| | ), |
| | |
| | div( |
| | strong("Technical"), br(), |
| | span(paste0(st$Elevation, " m")), br(), |
| | span(class = paste("badge", res_class), res_label) |
| | ), |
| | |
| | div( |
| | strong("Data Selection"), br(), |
| | span(dates_text) |
| | ), |
| | |
| | div( |
| | class = "d-flex align-items-center justify-content-end", |
| | downloadButton( |
| | "downloadData", |
| | label = "Export Excel", |
| | class = "btn-sm btn-primary", |
| | icon = icon("file-excel") |
| | ) |
| | ) |
| | ) |
| | ) |
| | ) |
| | }) |
| |
|
| | |
| | output$downloadData <- downloadHandler( |
| | filename = function() { |
| | req(selected_station()) |
| | st <- selected_station() |
| | res <- if (!is.null(input$data_resolution)) tolower(input$data_resolution) else "data" |
| | sprintf("jma_%s_%s_%s.xlsx", st$ID, res, format(Sys.Date(), "%Y%m%d")) |
| | }, |
| | content = function(file) { |
| | req(final_data_path()) |
| | file.copy(final_data_path(), file) |
| | } |
| | ) |
| |
|
| | |
| | |
| |
|
| | |
| |
|
| |
|
| | |
| | |
| |
|
| | |
| | output$jma_plots_container <- renderUI({ |
| | df <- station_data() |
| | req(df) |
| |
|
| | |
| | |
| |
|
| | plot_list <- tagList() |
| |
|
| | |
| | has_temp <- any(c("Temperature", "Temp_Mean", "Temp_Max", "Temp_Min") %in% names(df) & !all(is.na(df[[if ("Temperature" %in% names(df)) "Temperature" else "Temp_Mean"]]))) |
| | if (has_temp) { |
| | plot_list <- tagList(plot_list, div(class = "col-12 col-lg-6", plotlyOutput("plot_temp", height = "350px"))) |
| | } |
| |
|
| | |
| | if ("Precipitation" %in% names(df) && !all(is.na(df$Precipitation))) { |
| | plot_list <- tagList(plot_list, div(class = "col-12 col-lg-6", plotlyOutput("plot_precip", height = "300px"))) |
| | } |
| |
|
| | |
| | if ("Wind_Speed" %in% names(df) && !all(is.na(df$Wind_Speed))) { |
| | plot_list <- tagList(plot_list, div(class = "col-12 col-lg-6", plotlyOutput("plot_wind", height = "350px"))) |
| | } |
| |
|
| | |
| | if ("Humidity" %in% names(df) && !all(is.na(df$Humidity))) { |
| | plot_list <- tagList(plot_list, div(class = "col-12 col-lg-6", plotlyOutput("plot_humidity", height = "350px"))) |
| | } |
| |
|
| | |
| | if ("Pressure" %in% names(df) && !all(is.na(df$Pressure))) { |
| | plot_list <- tagList(plot_list, div(class = "col-12 col-lg-6", plotlyOutput("plot_pressure", height = "300px"))) |
| | } |
| |
|
| | |
| | has_sun <- any(c("Sunshine_Hours", "Sunshine_Minutes") %in% names(df) & !all(is.na(df[[if ("Sunshine_Hours" %in% names(df)) "Sunshine_Hours" else "Sunshine_Minutes"]]))) |
| | if (has_sun) { |
| | plot_list <- tagList(plot_list, div(class = "col-12 col-lg-6", plotlyOutput("plot_sunshine", height = "300px"))) |
| | } |
| |
|
| | |
| | if ("Solar_Radiation" %in% names(df) && !all(is.na(df$Solar_Radiation))) { |
| | plot_list <- tagList(plot_list, div(class = "col-12 col-lg-6", plotlyOutput("plot_solar", height = "300px"))) |
| | } |
| |
|
| | |
| | has_snow <- any(c("Snow_Depth", "Snowfall") %in% names(df) & !all(is.na(df[[if ("Snow_Depth" %in% names(df)) "Snow_Depth" else "Snowfall"]]))) |
| | if (has_snow) { |
| | plot_list <- tagList(plot_list, div(class = "col-12 col-lg-6", plotlyOutput("plot_snow", height = "300px"))) |
| | } |
| |
|
| | if (length(plot_list) == 0) { |
| | return(div(class = "alert alert-warning", "No plottable weather data found for this period.")) |
| | } |
| |
|
| | |
| | div( |
| | class = "row g-3", |
| | style = "padding: 10px;", |
| | plot_list |
| | ) |
| | }) |
| |
|
| | |
| | |
| | |
| |
|
| | |
| | get_plot_df <- reactive({ |
| | df <- station_data() |
| | req(df) |
| | res <- input$data_resolution |
| |
|
| | if (res == "Monthly") { |
| | df$DateTime <- as.Date(paste(df$Year, df$Month, "01", sep = "-")) |
| | } else if (res == "Daily") { |
| | df$DateTime <- as.Date(paste(df$Year, df$Month, df$Day, sep = "-")) |
| | } else if (res == "Hourly") { |
| | df$DateTime <- as.POSIXct(paste0(df$Year, "-", df$Month, "-", df$Day, " ", df$Hour, ":00"), format = "%Y-%m-%d %H:%M") |
| | } else { |
| | df$DateTime <- as.POSIXct(paste0(df$Year, "-", df$Month, "-", df$Day, " ", df$Time), format = "%Y-%m-%d %H:%M") |
| | } |
| | df |
| | }) |
| |
|
| | output$plot_temp <- renderPlotly({ |
| | df <- get_plot_df() |
| | create_temperature_plot_jma(df, input$data_resolution) |
| | }) |
| |
|
| | output$plot_precip <- renderPlotly({ |
| | df <- get_plot_df() |
| | create_precipitation_plot_jma(df, input$data_resolution) |
| | }) |
| |
|
| | output$plot_wind <- renderPlotly({ |
| | df <- get_plot_df() |
| | resolution <- input$data_resolution |
| | if (resolution == "Hourly") { |
| | create_wind_rose_jma(df, resolution) |
| | } else { |
| | create_wind_plot_jma(df, resolution) |
| | } |
| | }) |
| |
|
| | output$plot_humidity <- renderPlotly({ |
| | df <- get_plot_df() |
| | create_humidity_plot_jma(df, input$data_resolution) |
| | }) |
| |
|
| | output$plot_pressure <- renderPlotly({ |
| | df <- get_plot_df() |
| | create_pressure_plot_jma(df, input$data_resolution) |
| | }) |
| |
|
| | output$plot_sunshine <- renderPlotly({ |
| | df <- get_plot_df() |
| | create_sunshine_plot_jma(df, input$data_resolution) |
| | }) |
| |
|
| | output$plot_solar <- renderPlotly({ |
| | df <- get_plot_df() |
| | create_solar_plot_jma(df, input$data_resolution) |
| | }) |
| |
|
| | output$plot_snow <- renderPlotly({ |
| | df <- get_plot_df() |
| | create_snow_plot_jma(df, input$data_resolution) |
| | }) |
| |
|
| | |
| | output$daily_data <- DT::renderDataTable({ |
| | req(station_data()) |
| | df <- station_data() |
| |
|
| | |
| | col_map <- c( |
| | "Year" = "Year", |
| | "Month" = "Month", |
| | "Day" = "Day", |
| | "Hour" = "Hour", |
| | "Time" = "Time", |
| | "Pressure" = "Pressure [hPa]", |
| | "Pressure_Sea_Level" = "Sea Level Pressure [hPa]", |
| | "Precipitation" = "Precipitation [mm]", |
| | "Precipitation_Max_1h" = "Max 1h Precipitation [mm]", |
| | "Precipitation_Max_10min" = "Max 10m Precipitation [mm]", |
| | "Temp_Mean" = "Mean Temperature [°C]", |
| | "Temp_Max" = "Max Temperature [°C]", |
| | "Temp_Min" = "Min Temperature [°C]", |
| | "Temperature" = "Temperature [°C]", |
| | "Humidity" = "Humidity [%]", |
| | "Humidity_Min" = "Min Humidity [%]", |
| | "Wind_Speed" = "Wind Speed [m/s]", |
| | "Wind_Speed_Max" = "Max Wind Speed [m/s]", |
| | "Wind_Gust_Speed" = "Max Gust Speed [m/s]", |
| | "Wind_Direction" = "Wind Direction", |
| | "Wind_Direction_Deg" = "Wind Direction [Deg]", |
| | "Sunshine_Hours" = "Sunshine Duration [h]", |
| | "Sunshine_Minutes" = "Sunshine Duration [min]", |
| | "Solar_Radiation" = "Solar Radiation [MJ/m²]", |
| | "Snowfall" = "Snowfall [cm]", |
| | "Snow_Depth" = "Snow Depth [cm]", |
| | "Snow_Days" = "Snow Days", |
| | "Fog_Days" = "Fog Days", |
| | "Thunder_Days" = "Thunder Days", |
| | "Dew_Point" = "Dew Point [°C]", |
| | "Vapor_Pressure" = "Vapor Pressure [hPa]", |
| | "Cloud_Cover" = "Cloud Cover [1/10]", |
| | "Visibility" = "Visibility [km]" |
| | ) |
| |
|
| | |
| | current_names <- names(df) |
| | for (i in seq_along(current_names)) { |
| | cn <- current_names[i] |
| | if (cn %in% names(col_map)) { |
| | current_names[i] <- col_map[[cn]] |
| | } |
| | } |
| | names(df) <- current_names |
| |
|
| | DT::datatable(df, options = list(pageLength = 15, scrollX = TRUE)) |
| | }) |
| |
|
| | |
| | observeEvent(input$station_selector, { |
| | if (input$station_selector != "All") { |
| | closest <- stations %>% |
| | filter(DisplayName == input$station_selector) %>% |
| | head(1) |
| | if (nrow(closest) > 0) { |
| | selected_station(closest) |
| |
|
| | |
| | period <- get_resolution_period(closest, input$data_resolution) |
| | period_labels <- format_period_labels(period$start, period$end, current_year = as.numeric(format(Sys.Date(), "%Y"))) |
| | lbl <- generate_label( |
| | closest$DisplayName, closest$ID, |
| | ifelse(closest$station_type == "a", "AMeDAS", "Manned station"), |
| | period_labels$start, period_labels$end, |
| | closest$ObservedVariables |
| | ) |
| | highlight_selected_station(maplibre_proxy("map"), closest$Lon, closest$Lat, lbl) |
| |
|
| | |
| | start_download(closest) |
| | } |
| | } |
| | }) |
| |
|
| | |
| | output$station_info_table <- DT::renderDataTable({ |
| | df <- filtered_stations() |
| | req(df) |
| |
|
| | |
| | res <- input$data_resolution |
| |
|
| | |
| | start_col <- switch(res, |
| | "Daily" = "Start_Daily", |
| | "Hourly" = "Start_Hourly", |
| | "10 Minutes" = "Start_10min", |
| | "Monthly" = "Start_Monthly" |
| | ) |
| | end_col <- switch(res, |
| | "Daily" = "End_Daily", |
| | "Hourly" = "End_Hourly", |
| | "10 Minutes" = "End_10min", |
| | "Monthly" = "End_Monthly" |
| | ) |
| |
|
| | df_display <- df %>% |
| | select( |
| | ID, |
| | Name = DisplayName, |
| | Prefecture = PrecLabel, |
| | Lat, |
| | Lon, |
| | Elevation, |
| | Variables = ObservedVariables, |
| | !!sym(start_col), |
| | !!sym(end_col) |
| | ) |
| |
|
| | DT::datatable(df_display, |
| | selection = "none", |
| | rownames = FALSE, |
| | callback = DT::JS(" |
| | table.on('dblclick', 'tr', function() { |
| | var rowData = table.row(this).data(); |
| | if (rowData !== undefined && rowData !== null) { |
| | var stationId = rowData[0]; |
| | Shiny.setInputValue('station_info_table_dblclick', stationId, {priority: 'event'}); |
| | } |
| | }); |
| | "), |
| | options = list( |
| | pageLength = 20, |
| | scrollX = TRUE, |
| | searching = TRUE |
| | ) |
| | ) |
| | }) |
| |
|
| | |
| | observeEvent(input$station_info_table_dblclick, { |
| | id_val <- input$station_info_table_dblclick |
| | req(id_val) |
| |
|
| | closest <- stations %>% |
| | filter(ID == id_val) %>% |
| | head(1) |
| |
|
| | req(nrow(closest) > 0) |
| |
|
| | selected_station(closest) |
| |
|
| | |
| | period <- get_resolution_period(closest, input$data_resolution) |
| | period_labels <- format_period_labels(period$start, period$end, current_year = as.numeric(format(Sys.Date(), "%Y"))) |
| | lbl <- generate_label( |
| | closest$DisplayName, closest$ID, |
| | ifelse(closest$station_type == "a", "AMeDAS", "Manned station"), |
| | period_labels$start, period_labels$end, |
| | closest$ObservedVariables |
| | ) |
| | highlight_selected_station(maplibre_proxy("map"), closest$Lon, closest$Lat, lbl) |
| |
|
| | |
| | start_download(closest) |
| |
|
| | |
| | broadcast_state() |
| |
|
| | |
| | |
| | |
| | |
| | }) |
| |
|
| | |
| | observeEvent(input$main_nav, |
| | { |
| | req(url_initialized()) |
| | broadcast_state() |
| | }, |
| | ignoreInit = TRUE |
| | ) |
| |
|
| | |
| | observeEvent(input$dashboard_subtabs, |
| | { |
| | req(url_initialized()) |
| | broadcast_state() |
| | }, |
| | ignoreInit = TRUE |
| | ) |
| |
|
| | |
| | observeEvent(input$data_resolution, |
| | { |
| | req(url_initialized()) |
| | broadcast_state() |
| | }, |
| | ignoreInit = TRUE |
| | ) |
| |
|
| | |
| | observeEvent(input$date_range, |
| | { |
| | req(url_initialized()) |
| | broadcast_state() |
| | }, |
| | ignoreInit = TRUE |
| | ) |
| | } |
| |
|