diff --git a/.github/workflows/R-CMD-check.yaml b/.github/workflows/R-CMD-check.yaml index 0929ded..0f2fe08 100644 --- a/.github/workflows/R-CMD-check.yaml +++ b/.github/workflows/R-CMD-check.yaml @@ -1,13 +1,15 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: - - master + branches: [main, master] pull_request: - branches: - - master + branches: [main, master] name: R-CMD-check +permissions: read-all + jobs: R-CMD-check: runs-on: ${{ matrix.config.os }} @@ -18,81 +20,33 @@ jobs: fail-fast: false matrix: config: - - {os: macOS-latest, r: 'devel'} - - {os: macOS-latest, r: 'release'} + - {os: macos-latest, r: 'release'} - {os: windows-latest, r: 'release'} - - {os: windows-latest, r: '3.6'} - - {os: ubuntu-16.04, r: 'release', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"} - - {os: ubuntu-16.04, r: 'oldrel', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"} - - {os: ubuntu-16.04, r: '3.5', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"} - - {os: ubuntu-16.04, r: '3.4', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"} - - {os: ubuntu-16.04, r: '3.3', rspm: "https://packagemanager.rstudio.com/cran/__linux__/xenial/latest"} + - {os: ubuntu-latest, r: 'devel', http-user-agent: 'release'} + - {os: ubuntu-latest, r: 'release'} + - {os: ubuntu-latest, r: 'oldrel-1'} env: - R_REMOTES_NO_ERRORS_FROM_WARNINGS: true - RSPM: ${{ matrix.config.rspm }} GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + R_KEEP_PKG_SOURCE: yes steps: - - uses: actions/checkout@v2 + - uses: actions/checkout@v4 + + - uses: r-lib/actions/setup-pandoc@v2 - - uses: r-lib/actions/setup-r@master + - uses: r-lib/actions/setup-r@v2 with: r-version: ${{ matrix.config.r }} + http-user-agent: ${{ matrix.config.http-user-agent }} + use-public-rspm: true - - uses: r-lib/actions/setup-pandoc@master - - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} - - - name: Cache R packages - if: runner.os != 'Windows' - uses: actions/cache@v1 + - uses: r-lib/actions/setup-r-dependencies@v2 with: - path: ${{ env.R_LIBS_USER }} - key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} - restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- - - - name: Install system dependencies - if: runner.os == 'Linux' - env: - RHUB_PLATFORM: linux-x86_64-ubuntu-gcc - run: | - Rscript -e "remotes::install_github('r-hub/sysreqs')" - sysreqs=$(Rscript -e "cat(sysreqs::sysreq_commands('DESCRIPTION'))") - sudo -s eval "$sysreqs" - - - name: Install dependencies - run: | - remotes::install_deps(dependencies = TRUE) - remotes::install_cran("rcmdcheck") - shell: Rscript {0} - - - name: Session info - run: | - options(width = 100) - pkgs <- installed.packages()[, "Package"] - sessioninfo::session_info(pkgs, include_base = TRUE) - shell: Rscript {0} - - - name: Check - env: - _R_CHECK_CRAN_INCOMING_: false - run: rcmdcheck::rcmdcheck(args = c("--no-manual", "--as-cran"), error_on = "warning", check_dir = "check") - shell: Rscript {0} - - - name: Show testthat output - if: always() - run: find check -name 'testthat.Rout*' -exec cat '{}' \; || true - shell: bash + extra-packages: any::rcmdcheck + needs: check - - name: Upload check results - if: failure() - uses: actions/upload-artifact@master + - uses: r-lib/actions/check-r-package@v2 with: - name: ${{ runner.os }}-r${{ matrix.config.r }}-results - path: check + upload-snapshots: true + build_args: 'c("--no-manual","--compact-vignettes=gs+qpdf")' diff --git a/.github/workflows/pkgdown.yaml b/.github/workflows/pkgdown.yaml index 8bc9210..c9f0165 100644 --- a/.github/workflows/pkgdown.yaml +++ b/.github/workflows/pkgdown.yaml @@ -1,46 +1,50 @@ +# Workflow derived from https://github.com/r-lib/actions/tree/v2/examples +# Need help debugging build failures? Start at https://github.com/r-lib/actions#where-to-find-help on: push: - branches: master + branches: [main, master] + pull_request: + branches: [main, master] + release: + types: [published] + workflow_dispatch: name: pkgdown +permissions: read-all + jobs: pkgdown: - runs-on: macOS-latest + runs-on: ubuntu-latest + # Only restrict concurrency for non-PR jobs + concurrency: + group: pkgdown-${{ github.event_name != 'pull_request' || github.run_id }} env: GITHUB_PAT: ${{ secrets.GITHUB_TOKEN }} + permissions: + contents: write steps: - - uses: actions/checkout@v2 - - - uses: r-lib/actions/setup-r@master + - uses: actions/checkout@v4 - - uses: r-lib/actions/setup-pandoc@master + - uses: r-lib/actions/setup-pandoc@v2 - - name: Query dependencies - run: | - install.packages('remotes') - saveRDS(remotes::dev_package_deps(dependencies = TRUE), ".github/depends.Rds", version = 2) - writeLines(sprintf("R-%i.%i", getRversion()$major, getRversion()$minor), ".github/R-version") - shell: Rscript {0} + - uses: r-lib/actions/setup-r@v2 + with: + use-public-rspm: true - - name: Cache R packages - uses: actions/cache@v1 + - uses: r-lib/actions/setup-r-dependencies@v2 with: - path: ${{ env.R_LIBS_USER }} - key: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1-${{ hashFiles('.github/depends.Rds') }} - restore-keys: ${{ runner.os }}-${{ hashFiles('.github/R-version') }}-1- - - - name: Install dependencies - run: | - remotes::install_deps(dependencies = TRUE) - install.packages("pkgdown") - shell: Rscript {0} + extra-packages: any::pkgdown, local::. + needs: website - - name: Install package - run: R CMD INSTALL . + - name: Build site + run: pkgdown::build_site_github_pages(new_process = FALSE, install = FALSE) + shell: Rscript {0} - - name: Deploy package - run: | - git config --local user.email "actions@github.com" - git config --local user.name "GitHub Actions" - Rscript -e 'pkgdown::deploy_to_branch(new_process = FALSE)' + - name: Deploy to GitHub pages 🚀 + if: github.event_name != 'pull_request' + uses: JamesIves/github-pages-deploy-action@v4.5.0 + with: + clean: false + branch: gh-pages + folder: docs diff --git a/.github/workflows/pkgdown.yml b/.github/workflows/pkgdown.yml deleted file mode 100644 index 7d938fc..0000000 --- a/.github/workflows/pkgdown.yml +++ /dev/null @@ -1,23 +0,0 @@ -on: - push: - branches: master - -name: Pkgdown - -jobs: - pkgdown: - runs-on: macOS-latest - steps: - - uses: actions/checkout@master - - uses: r-lib/actions/setup-r@master - - uses: r-lib/actions/setup-pandoc@master - - name: Install dependencies - run: | - Rscript -e 'install.packages("remotes")' \ - -e 'remotes::install_deps(dependencies = TRUE)' \ - -e 'remotes::install_github("jimhester/pkgdown@github-actions-deploy")' - - name: Install package - run: R CMD INSTALL . - - name: Deploy package - run: | - Rscript -e "pkgdown:::deploy_local(new_process = FALSE, remote_url = 'https://x-access-token:${{secrets.GITHUB_TOKEN}}@github.com/${{github.repository}}.git')" diff --git a/DESCRIPTION b/DESCRIPTION index 01555d7..af1b244 100644 --- a/DESCRIPTION +++ b/DESCRIPTION @@ -18,7 +18,7 @@ RoxygenNote: 7.3.2 URL: https://github.com/RinteRface/shiny386, https://rinterface.github.io/shiny386 BugReports: https://github.com/RinteRface/shiny386/issues Imports: - htmltools, + htmltools (>= 0.5.2), shiny, httpuv Suggests: diff --git a/R/deps.R b/R/deps.R index 8c062dc..973a790 100644 --- a/R/deps.R +++ b/R/deps.R @@ -1,24 +1,3 @@ -bs4_deps <- htmltools::htmlDependency( - name = "Bootstrap", - version = "4.4.1", - src = c(file = "bootstrap-4.4.1"), - package = "shiny386", - script = c( - "js/bootstrap.bundle.js", - "js/custom.js", - "js/progress_handler.js", - "js/toast_handler.js", - "js/radioButtonsBinding.js", - "js/tabsetPanelBinding.js", - "js/dropdownInputBinding.js" - ), - stylesheet = c( - "css/bootstrap.min.css", - "css/custom.css" - ) -) - - #' Create shiny386 dependencies #' #' Add all necessary dependencies so that shiny386 renders well @@ -27,5 +6,26 @@ bs4_deps <- htmltools::htmlDependency( #' @export #' @seealso \link{page_386}. use_bs4_deps <- function(tag) { - tagList(tag, bs4_deps) + tagList( + tag, + htmltools::htmlDependency( + name = "Bootstrap", + version = "4.4.1", + src = c(file = "bootstrap-4.4.1"), + package = "shiny386", + script = c( + "js/bootstrap.bundle.js", + "js/custom.js", + "js/progress_handler.js", + "js/toast_handler.js", + "js/radioButtonsBinding.js", + "js/tabsetPanelBinding.js", + "js/dropdownInputBinding.js" + ), + stylesheet = c( + "css/bootstrap.min.css", + "css/custom.css" + ) + ) + ) } diff --git a/R/inputs.R b/R/inputs.R index 22ffc07..22ad9e6 100644 --- a/R/inputs.R +++ b/R/inputs.R @@ -604,6 +604,10 @@ update_checkbox_group_input_386 <- function (session, inputId, label = NULL, cho #' Create a Bootstrap 386 select input +#' +#' @note Incompatible with selectize. Set to FALSE +#' by default to have correct CSS rendering. +#' #' @inheritParams shiny::selectInput #' @export #' @examples @@ -627,7 +631,27 @@ update_checkbox_group_input_386 <- function (session, inputId, label = NULL, cho #' shinyApp(ui, server) #' #' } -select_input_386 <- shiny::selectInput +select_input_386 <- function( + inputId, label, choices, selected = NULL, multiple = FALSE, + selectize = FALSE, width = NULL, size = NULL +) { + htmltools::tagQuery( + shiny::selectInput( + inputId, + label, + choices, + selected, + multiple, + selectize = FALSE, + width, + size + ) + )$ + find("select")$ + addClass("custom-select")$ + removeClass("form-control")$ + allTags() +} diff --git a/R/layout.R b/R/layout.R index 51e9cea..a21d4fa 100644 --- a/R/layout.R +++ b/R/layout.R @@ -73,30 +73,22 @@ tabset_panel_386 <- function(..., id = NULL, selected = NULL, ) # Some edit below since Bootstrap 4 significantly changed the layout - nav_items <- temp_tabset$children[[1]]$children[[1]] - found_active <- FALSE - bs4_nav_items <- lapply(nav_items, function(x) { - if (!is.null(x$attribs$class)) { - if (grep(x = x$attribs$class, pattern = "active")) { - x$attribs$class <- NULL - found_active <- TRUE + htmltools::tagQuery(temp_tabset)$ + find("li")$ + each( + function(x, i) { + # replace text + current_tab <- x$children[[1]]$attribs$`data-value` + x$attribs$class <- "nav-item" + x$children[[1]]$attribs$class <- if (selected == current_tab) { + "nav-link active" + } else { + "nav-link" + } + x } - } - x$attribs$class <- if (is.null(x$attribs$class)) { - "nav-item" - } else { - paste("nav-item", x$attribs$class) - } - x$children[[1]]$attribs$class <- if (found_active) { - "nav-link active" - } else { - "nav-link" - } - x - }) - - temp_tabset$children[[1]]$children[[1]] <- bs4_nav_items - temp_tabset + )$ + allTags() } @@ -208,9 +200,9 @@ update_tabset_panel_386 <- shiny::updateTabsetPanel #' shinyApp(ui, server) #' } navbar_page_386 <- function (title, ..., id = NULL, selected = NULL, - position = c("static-top", "fixed-top", "fixed-bottom"), - header = NULL, footer = NULL, inverse = FALSE, - windowTitle = title) { + position = c("static-top", "fixed-top", "fixed-bottom"), + header = NULL, footer = NULL, inverse = FALSE, + windowTitle = title) { pageTitle <- title navbarClass <- "navbar navbar-expand-lg navbar-dark bg-primary" position <- match.arg(position) diff --git a/R/shiny386-package.R b/R/shiny386-package.R index 1b3600c..4a8d4ac 100644 --- a/R/shiny386-package.R +++ b/R/shiny386-package.R @@ -5,5 +5,4 @@ #' @name shiny386 #' @import htmltools #' @import shiny -#' @docType package "_PACKAGE" diff --git a/README.md b/README.md index ae347f1..d71526d 100644 --- a/README.md +++ b/README.md @@ -4,13 +4,10 @@ # shiny386 - -[![CRAN -status](https://www.r-pkg.org/badges/version/shiny386)](https://CRAN.R-project.org/package=shiny386) -[![Lifecycle: -experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) -[![R build -status](https://github.com/RinteRface/shiny386/workflows/R-CMD-check/badge.svg)](https://github.com/RinteRface/shiny386/actions) +[![CRAN status](https://www.r-pkg.org/badges/version/shiny386)](https://CRAN.R-project.org/package=shiny386) +[![Lifecycle: experimental](https://img.shields.io/badge/lifecycle-experimental-orange.svg)](https://www.tidyverse.org/lifecycle/#experimental) +[![R build status](https://github.com/RinteRface/shiny386/workflows/R-CMD-check/badge.svg)](https://github.com/RinteRface/shiny386/actions) +[![R-CMD-check](https://github.com/RinteRface/shiny386/actions/workflows/R-CMD-check.yaml/badge.svg)](https://github.com/RinteRface/shiny386/actions/workflows/R-CMD-check.yaml) The goal of shiny386 is to provide an old school Bootstrap 4 template diff --git a/inst/examples/gallery/R/gfts.R b/inst/examples/gallery/R/gfts.R new file mode 100644 index 0000000..96ab071 --- /dev/null +++ b/inst/examples/gallery/R/gfts.R @@ -0,0 +1,10 @@ +get_transit_gtfs <- memoise::memoise(function() { + download_and_read( + url = "https://svc.metrotransit.org/mtgtfs/gtfs.zip", + # Use consistent location for caching within R session + destfile = file.path(tempdir(), "gtfs.zip"), + read_fn = function(x) { + gtfstools::read_gtfs(x, c("trips", "shapes")) + } + ) +}) diff --git a/inst/examples/gallery/R/realtime.R b/inst/examples/gallery/R/realtime.R new file mode 100644 index 0000000..ca003c6 --- /dev/null +++ b/inst/examples/gallery/R/realtime.R @@ -0,0 +1,77 @@ +library(RProtoBuf) +library(dplyr) +library(purrr) + + +realtime_info <- memoise::memoise( + function() { + + # Altered from https://stackoverflow.com/a/71552368 + # to support https://svc.metrotransit.org/ + # Only add shapes if needed + if (! ("transit_realtime.VehiclePosition" %in% ls("RProtoBuf:DescriptorPool"))) { + download_and_read( + url = "https://gtfs.org/realtime/gtfs-realtime.proto", + # Use consistent location for caching within R session + destfile = file.path(tempdir(), "gtfs-realtime.proto"), + # Load proto shapes into RProtoBuf + read_fn = readProtoFiles + ) + # # View proto shapes + # ls("RProtoBuf:DescriptorPool") + } + + download_and_read( + url = "https://svc.metrotransit.org/mtgtfs/vehiclepositions.pb", + destfile = tempfile(fileext = ".pb"), + clean = TRUE, + read_fn = function(vehicle_positions) { + read( + transit_realtime.FeedMessage, + vehicle_positions + )[["entity"]] + } + ) + }, + # Cache for 10 seconds (then website will refresh data every 15s) + # Helps avoid duplicate work during startup + cache = cachem::cache_mem(max_age = 10) +) + + +# Route +# Direction +# VehicleLongitude +# VehicleLatitude +realtime_locations <- function(..., veh_info = realtime_info(), gtfs = get_transit_gtfs()) { + locations <- + veh_info %>% + map_dfr(~ { + vehicle <- .x$vehicle + trip <- vehicle$trip + position <- vehicle$position + tibble( + Route = trip$route_id, + trip_id = trip$trip_id, + VehicleLongitude = position$longitude, + VehicleLatitude = position$latitude + ) + }) %>% + # Remove vehicles with no location + filter(VehicleLatitude > 1) + + locations %>% + left_join( + gtfs$trips %>% + select(trip_id, direction), + by = "trip_id" + ) %>% + # Add route names + # 1=South, 2=East, 3=West, 4=North + mutate( + Direction = c("SB" = 1, "EB" = 2, "WB" = 3, "NB" = 4)[direction], + # Remove unnecessary columns + trip_id = NULL, + direction = NULL + ) +} diff --git a/inst/examples/gallery/R/utils.R b/inst/examples/gallery/R/utils.R new file mode 100644 index 0000000..5d3fc8f --- /dev/null +++ b/inst/examples/gallery/R/utils.R @@ -0,0 +1,30 @@ + + + +download_file <- function(url, destfile) { + download.file(url, destfile, quiet = TRUE) +} + +download_and_read <- function(url, destfile, read_fn, clean = FALSE) { + if (!file.exists(destfile)) { + download_file(url, destfile) + } + if (clean) { + on.exit(unlink(destfile), add = TRUE) + } + + read_fn(destfile) +} + + +# Method to recursively convert proto objects to lists +# Great for debugging +as_list <- function(x) { + if (inherits(x, "Message")) { + x <- as.list(x) + } + if (is.list(x)) { + return(lapply(x, as_list)) + } + x +} diff --git a/inst/examples/gallery/app.R b/inst/examples/gallery/app.R index 517ffd9..ad8042a 100644 --- a/inst/examples/gallery/app.R +++ b/inst/examples/gallery/app.R @@ -1,5 +1,4 @@ # packages ---------------------------------------------------------------- - library(shiny) library(shiny386) library(leaflet) @@ -7,46 +6,33 @@ library(dplyr) library(curl) # make the jsonlite suggested dependency explicit library(jsonlite) - -# utils ------------------------------------------------------------------- - # 1=South, 2=East, 3=West, 4=North -dirColors <- c("1" = "#595490", "2" = "#527525", "3" = "#A93F35", "4" = "#BA48AA") - -# Download data from the Twin Cities Metro Transit API -# http://svc.metrotransit.org/NexTrip/help -getMetroData <- function(path) { - url <- paste0("http://svc.metrotransit.org/NexTrip/", path, "?format=json") - jsonlite::fromJSON(url) -} - -# Load static trip and shape data -trips <- readRDS("metrotransit-data/rds/trips.rds") -shapes <- readRDS("metrotransit-data/rds/shapes.rds") +dirColors <-c("1"="#595490", "2"="#527525", "3"="#A93F35", "4"="#BA48AA") +# Dynamically load trip and shape data +gtfs <- get_transit_gtfs() # Get the shape for a particular route. This isn't perfect. Each route has a # large number of different trips, and each trip can have a different shape. # This function simply returns the most commonly-used shape across all trips for # a particular route. -get_route_shape <- function(route) { - routeid <- paste0(route, "-75") +get_route_shape <- function(routeid) { + # routeid <- paste0(route, "-75") - # For this route, get all the shape_ids listed in trips, and a count of how - # many times each shape is used. We'll just pick the most commonly-used shape. - shape_counts <- trips %>% - filter(route_id == routeid) %>% - group_by(shape_id) %>% - summarise(n = n()) %>% - arrange(-n) + # For this route, get all the shape_ids listed in trips, and a count of how + # many times each shape is used. We'll just pick the most commonly-used shape. + shape_counts <- gtfs$trips %>% + filter(route_id == routeid) %>% + group_by(shape_id) %>% + summarise(n = n()) %>% + arrange(-n) - shapeid <- shape_counts$shape_id[1] + shapeid <- shape_counts$shape_id[1] - # Get the coordinates for the shape_id - shapes %>% filter(shape_id == shapeid) + # Get the coordinates for the shape_id + gtfs$shapes %>% filter(shape_id == shapeid) } - # UI ---------------------------------------------------------------------- ui <- page_386( h1("Twin Cities Buses"), @@ -114,169 +100,166 @@ ui <- page_386( # server ------------------------------------------------------------------ - server <- function(input, output, session) { - # Route select input box - output$routeSelect <- renderUI({ - live_vehicles <- getMetroData("VehicleLocations/0") - - routeNums <- sort(unique(as.numeric(live_vehicles$Route))) - # Add names, so that we can add all=0 - names(routeNums) <- routeNums - routeNums <- c(All = 0, routeNums) - selectInput("routeNum", "Route", choices = routeNums, selected = routeNums[2]) - }) - - # Locations of all active vehicles - vehicleLocations <- reactive({ - input$refresh # Refresh if button clicked - - # Get interval (minimum 30) - interval <- max(as.numeric(input$interval), 30) - # Invalidate this reactive after the interval has passed, so that data is - # fetched again. - invalidateLater(interval * 1000, session) - - getMetroData("VehicleLocations/0") - }) - - # Locations of vehicles for a particular route - routeVehicleLocations <- reactive({ - if (is.null(input$routeNum)) { - return() - } - - locations <- vehicleLocations() - - if (as.numeric(input$routeNum) == 0) { - return(locations) - } - - locations[locations$Route == input$routeNum, ] - }) - - # Get time that vehicles locations were updated - lastUpdateTime <- reactive({ - vehicleLocations() # Trigger this reactive when vehicles locations are updated - Sys.time() - }) - - # Number of seconds since last update - output$timeSinceLastUpdate <- renderUI({ - # Trigger this every 5 seconds - invalidateLater(5000, session) - p( - class = "text-muted", - "Data refreshed ", - round(difftime(Sys.time(), lastUpdateTime(), units = "secs")), - " seconds ago." - ) - }) - - output$numVehiclesTable <- renderUI({ - locations <- routeVehicleLocations() - if (length(locations) == 0 || nrow(locations) == 0) { - return(NULL) - } - - # Create a Bootstrap-styled table - tags$table( - class = "table", - tags$thead(tags$tr( - tags$th("Color"), - tags$th("Direction"), - tags$th("Number of vehicles") - )), - tags$tbody( - tags$tr( - tags$td(span(style = sprintf( - "width:1.1em; height:1.1em; background-color:%s; display:inline-block;", - dirColors[4] - ))), - tags$td("Northbound"), - tags$td(nrow(locations[locations$Direction == "4", ])) - ), - tags$tr( - tags$td(span(style = sprintf( - "width:1.1em; height:1.1em; background-color:%s; display:inline-block;", - dirColors[1] - ))), - tags$td("Southbound"), - tags$td(nrow(locations[locations$Direction == "1", ])) - ), - tags$tr( - tags$td(span(style = sprintf( - "width:1.1em; height:1.1em; background-color:%s; display:inline-block;", - dirColors[2] - ))), - tags$td("Eastbound"), - tags$td(nrow(locations[locations$Direction == "2", ])) - ), - tags$tr( - tags$td(span(style = sprintf( - "width:1.1em; height:1.1em; background-color:%s; display:inline-block;", - dirColors[3] - ))), - tags$td("Westbound"), - tags$td(nrow(locations[locations$Direction == "3", ])) - ), - tags$tr( - style = "background-color: grey", - tags$td(), - tags$td("Total"), - tags$td(nrow(locations)) - ) - ) - ) - }) - - # Store last zoom button value so we can detect when it's clicked - lastZoomButtonValue <- NULL - - output$busmap <- renderLeaflet({ - locations <- routeVehicleLocations() - if (length(locations) == 0) { - return(NULL) - } - - # Show only selected directions - locations <- filter(locations, Direction %in% as.numeric(input$directions)) - - # Four possible directions for bus routes - dirPal <- colorFactor(dirColors, names(dirColors)) - - map <- leaflet(locations) %>% - addTiles("http://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png") %>% - addCircleMarkers( - ~VehicleLongitude, - ~VehicleLatitude, - color = ~ dirPal(Direction), - opacity = 0.8, - radius = 8 - ) - - if (as.numeric(input$routeNum) != 0) { - route_shape <- get_route_shape(input$routeNum) - - map <- addPolylines(map, - route_shape$shape_pt_lon, - route_shape$shape_pt_lat, - fill = FALSE - ) - } - - rezoom <- "first" - # If zoom button was clicked this time, and store the value, and rezoom - if (!identical(lastZoomButtonValue, input$zoomButton)) { - lastZoomButtonValue <<- input$zoomButton - rezoom <- "always" - } - - map <- map %>% mapOptions(zoomToLimits = rezoom) - - map - }) + # Route select input box + output$routeSelect <- renderUI({ + # live_vehicles <- getMetroData("VehicleLocations/0") + + routeNums <- + sort(unique(as.numeric( + realtime_locations(gtfs = gtfs)$Route + ))) + + # Add names, so that we can add all=0 + names(routeNums) <- routeNums + routeNums <- c(All = 0, routeNums) + select_input_386("routeNum", "Route", choices = routeNums, selected = routeNums[2]) + }) + + # Locations of all active vehicles + vehicleLocations <- reactive({ + input$refresh # Refresh if button clicked + + # Get interval (minimum 30) + interval <- max(as.numeric(input$interval), 30) + # Invalidate this reactive after the interval has passed, so that data is + # fetched again. + invalidateLater(interval * 1000, session) + + realtime_locations(gtfs = gtfs) + }) + + # Data frame of vehicle locations for a particular route + routeVehicleLocations <- reactive({ + if (is.null(input$routeNum)) + return() + + locations <- vehicleLocations() + + if (as.numeric(input$routeNum) == 0) + return(locations) + + locations[locations$Route == input$routeNum, ] + }) + + # Get time that vehicles locations were updated + lastUpdateTime <- reactive({ + vehicleLocations() # Trigger this reactive when vehicles locations are updated + Sys.time() + }) + + # Number of seconds since last update + output$timeSinceLastUpdate <- renderUI({ + # Trigger this every 5 seconds + invalidateLater(5000, session) + p( + class = "text-muted", + "Data refreshed ", + round(difftime(Sys.time(), lastUpdateTime(), units="secs")), + " seconds ago." + ) + }) + + output$numVehiclesTable <- renderUI({ + locations <- routeVehicleLocations() + if (length(locations) == 0 || nrow(locations) == 0) + return(NULL) + + # Create a Bootstrap-styled table + tags$table(class = "table", + tags$thead(tags$tr( + tags$th("Color"), + tags$th("Direction"), + tags$th("Number of vehicles") + )), + tags$tbody( + tags$tr( + tags$td(span(style = sprintf( + "width:1.1em; height:1.1em; background-color:%s; display:inline-block;", + dirColors[4] + ))), + tags$td("Northbound"), + tags$td(nrow(locations[locations$Direction == "4",])) + ), + tags$tr( + tags$td(span(style = sprintf( + "width:1.1em; height:1.1em; background-color:%s; display:inline-block;", + dirColors[1] + ))), + tags$td("Southbound"), + tags$td(nrow(locations[locations$Direction == "1",])) + ), + tags$tr( + tags$td(span(style = sprintf( + "width:1.1em; height:1.1em; background-color:%s; display:inline-block;", + dirColors[2] + ))), + tags$td("Eastbound"), + tags$td(nrow(locations[locations$Direction == "2",])) + ), + tags$tr( + tags$td(span(style = sprintf( + "width:1.1em; height:1.1em; background-color:%s; display:inline-block;", + dirColors[3] + ))), + tags$td("Westbound"), + tags$td(nrow(locations[locations$Direction == "3",])) + ), + tags$tr(class = "active", + tags$td(), + tags$td("Total"), + tags$td(nrow(locations)) + ) + ) + ) + }) + + # Store last zoom button value so we can detect when it's clicked + lastZoomButtonValue <- NULL + + output$busmap <- renderLeaflet({ + locations <- routeVehicleLocations() + if (length(locations) == 0) + return(NULL) + + # Show only selected directions + locations <- filter(locations, Direction %in% as.numeric(input$directions)) + + # Four possible directions for bus routes + dirPal <- colorFactor(dirColors, names(dirColors)) + + map <- leaflet(locations) %>% + addTiles('http://{s}.tile.thunderforest.com/transport/{z}/{x}/{y}.png') %>% + addCircleMarkers( + ~VehicleLongitude, + ~VehicleLatitude, + color = ~dirPal(Direction), + opacity = 0.8, + radius = 8 + ) + + if (as.numeric(input$routeNum) != 0) { + route_shape <- get_route_shape(input$routeNum) + + map <- addPolylines(map, + route_shape$shape_pt_lon, + route_shape$shape_pt_lat, + fill = FALSE + ) + } + + rezoom <- "first" + # If zoom button was clicked this time, and store the value, and rezoom + if (!identical(lastZoomButtonValue, input$zoomButton)) { + lastZoomButtonValue <<- input$zoomButton + rezoom <- "always" + } + + map <- map %>% mapOptions(zoomToLimits = rezoom) + + map + }) } shinyApp(ui, server) diff --git a/inst/examples/gallery/metrotransit-data/00-fetch-data.R b/inst/examples/gallery/metrotransit-data/00-fetch-data.R deleted file mode 100755 index 85201d8..0000000 --- a/inst/examples/gallery/metrotransit-data/00-fetch-data.R +++ /dev/null @@ -1,41 +0,0 @@ -# Information about data source at: -# http://datafinder.org/metadata/transit_schedule_google_feed.html - -# Data reference: -# https://developers.google.com/transit/gtfs/reference?csw=1 - -# These are the data files we want to use -datafiles <- c("shapes.txt", "trips.txt") - -# ============================================================================= -# Download and unzip data -# ============================================================================= -download.file("ftp://gisftp.metc.state.mn.us/google_transit.zip", - "google_transit.zip") - -dir.create("raw", showWarnings = FALSE) - -# Extract just the specified data files -unzip("google_transit.zip", files = datafiles, exdir = "raw") -unlink("google_transit.zip") - -# ============================================================================= -# Read in each of the data objects and save to an RDS file -# ============================================================================= -# Clean out old files -unlink("rds", recursive = TRUE) - -dir.create("rds", showWarnings = FALSE) - -for (datafile in datafiles) { - infile <- file.path("raw", datafile) - outfile <- file.path("rds", sub("\\.txt$", ".rds", datafile)) - - cat("Converting ", infile, " to ", outfile, ".\n", sep = "") - - obj <- read.csv(infile, stringsAsFactors = FALSE) - saveRDS(obj, outfile) -} - -# Remove raw data files -unlink("raw", recursive = TRUE) diff --git a/inst/examples/gallery/metrotransit-data/metrotransit-data.Rproj b/inst/examples/gallery/metrotransit-data/metrotransit-data.Rproj deleted file mode 100755 index a213108..0000000 --- a/inst/examples/gallery/metrotransit-data/metrotransit-data.Rproj +++ /dev/null @@ -1,15 +0,0 @@ -Version: 1.0 - -RestoreWorkspace: Default -SaveWorkspace: Default -AlwaysSaveHistory: Default - -EnableCodeIndexing: Yes -UseSpacesForTab: Yes -NumSpacesForTab: 2 -Encoding: UTF-8 - -RnwWeave: Sweave -LaTeX: pdfLaTeX - -AutoAppendNewline: Yes diff --git a/inst/examples/gallery/metrotransit-data/rds/shapes.rds b/inst/examples/gallery/metrotransit-data/rds/shapes.rds deleted file mode 100755 index fefbd07..0000000 Binary files a/inst/examples/gallery/metrotransit-data/rds/shapes.rds and /dev/null differ diff --git a/inst/examples/gallery/metrotransit-data/rds/trips.rds b/inst/examples/gallery/metrotransit-data/rds/trips.rds deleted file mode 100755 index 587df04..0000000 Binary files a/inst/examples/gallery/metrotransit-data/rds/trips.rds and /dev/null differ diff --git a/man/select_input_386.Rd b/man/select_input_386.Rd index a49aef0..98f9e03 100644 --- a/man/select_input_386.Rd +++ b/man/select_input_386.Rd @@ -10,7 +10,7 @@ select_input_386( choices, selected = NULL, multiple = FALSE, - selectize = TRUE, + selectize = FALSE, width = NULL, size = NULL ) @@ -46,6 +46,10 @@ but when \code{size} is set, it will be a box instead.} \description{ Create a Bootstrap 386 select input } +\note{ +Incompatible with selectize. Set to FALSE +by default to have correct CSS rendering. +} \examples{ if (interactive()) { library(shiny)