inst/shiny-scripts/app.R

library(shiny)
library(PeakMapper)
library(ggplot2)

ui <- fluidPage(
  tags$h1(tags$b("PeakMapper:"),"Flexible mapping of genomic coordinates to nearest
          features"),
  tags$hr(),
  tabsetPanel(

  # Use of the uiOutput function to create drop-down menus that update as new
  # datasets are imported or created is based on code from the following RStudio
  # Shiny tutorial: https://shiny.rstudio.com/articles/dynamic-ui.html (full
  # reference provided in the documentation for runPeakMapperApp())

    # This tab allows the user to import their own datasets using importBED
    tabPanel("Step 1: Import datasets",
      sidebarLayout(
        sidebarPanel(
          tags$p("Import your files of peak coordinates and feature coordinates,
                in BED format. All imported files will remain usable for mapping
                until the app is closed. BED files can also be previewed here
                after importing them."),
          tags$p("An example peak file (H3K27me3PeaksSmall.bed) and an example
                 feature file (WS263Genes.bed) can be found in the extdata
                 subdirectory of PeakMapper."),
          fileInput("file", "Select a dataset to import:"),
          uiOutput("selectInput"),
          actionButton(inputId = "button", label = "Display dataset")
        ),
        mainPanel(tableOutput("previewTable"))
      )

    ),

    # This tab allows the user to map imported peaks onto imported features
    # using mapPeaks
    tabPanel("Step 2: Map peaks to features",
      fluidRow(
        column(4, wellPanel(
            tags$p("Datasets imported in the previous step can be selected
                   from the following menus. After a peak dataset and a feature
                   dataset are selected from the appropriate menu, peaks can
                   be mapped onto their closest features by clicking the
                   'Map peaks onto features' button below. (This may take several
                   minutes for large datasets)"),
            tags$p("Performing a peak-feature mapping will create a new dataset
                   of results that will remain usable until the app is closed.
                   Results can be displayed here as a table, and these results
                   can be exported as a CSV file using the 'Export' button."),
            uiOutput("selectPeaks"),
            uiOutput("selectFeatures"),
            actionButton(inputId = "mappingButton", label =
                           "Map peaks onto features"),
            textOutput("mappingMessage"),
            tags$p(),
            uiOutput("selectResult"),
            actionButton(inputId = "mappingDisplayButton",
                         label = "Display selected mapping results"),
            downloadButton("exportButton",
                         "Export selected mapping results as CSV file"),
            tags$p(),
            tags$p("(For proper file exporting, the filename you give this CSV
                   should end with '.csv')")
            ))),
      fluidRow(
        column(12, tableOutput("mappingTable"))
      )
    ),

    # This tab allows the user to visualize the results of mapping peaks onto
    # closest features
    tabPanel("Step 3: Visualize mapping results",
      sidebarLayout(
        sidebarPanel(
          tags$p("After mapping peaks to closest features in the previous step,
                 the generated results can be visualized here using 4 different
                 types of plots. The datasets used to create these plots can be
                 selected from the two menus below. After mapping results and
                 their corresponding peak file are selected, along with the type
                 of plot to be made, plots can be generated by clicking the
                 'Generate plot' button at the bottom of this panel. Generated
                 plots can also be saved as PNGs by clicking the 'Save' button."),
          uiOutput("selectResultsForPlotting"),
          uiOutput("selectPeaksForPlotting"),
          selectInput('selectPlotType',
                      'Select the type of plot you want to make:',
             c("1. Plot the number of closest features to each peak",
              "2. Plot locations of mapped peaks relative to start of closest features",
              "3. Plot distribution of overlap proportions between peaks and closest features",
              "4. Plot distribution of qualitative peak positions relative to mapped features")),

          tags$p("The following 5 parameters can be used to customize the
                 appearance of the generated plot. Selecting a new plot type
                 from the above menu will update the boxes for main title, x-axis
                 title, and y-axis title to the default titles for that plot
                 type."),
          textInput("plotColor", "Enter plot color:", "Red"),
          textInput("mainTitle", "Enter main plot title:"),
          textInput("xTitle", "Enter plot x-axis title:"),
          textInput("yTitle", "Enter plot y-axis title:"),
          selectInput("backgroundStyle", "Select style of plot background:",
                      c("blackAndWhite", "grey", "minimal")),

          tags$p("The following three parameters are only used for plotting
                 locations of mapped peaks relative to the start of closest
                 features (plot type #2 on the above list). The number of base
                 pairs plotted upstream or downstream of feature start points
                 (by default, 2500 bp) is the number of bins (default: 250)
                 times the number of bases per bin (default: 10). Fewer bases
                 per bin gives a higher resolution view of peak distribution"),

          numericInput("upstreamBins", "Enter the number of bins (plotted points)
                       to be used upstream of each feature start position:", 250),
          numericInput("downstreamBins", "Enter the number of bins (plotted points)
                       to be used downstream of each feature start position:", 250),
          numericInput("basesPerBin", "Enter the width of each bin, in base pairs:", 10),
          actionButton(inputId = "plottingButton", label = "Generate plot"),
          downloadButton("savePlot", "Save current plot as PNG"),
          tags$p(),
          tags$p("(For proper plot saving, the filename you give this PNG should
                   end with '.png')")
        ),
        mainPanel(plotOutput("mainPlot", height = "750px"))
      )
     )
    )
  )


# The use of the 'clientData' and 'session' parameters is based off of the
# following R Shiny tutorial used to update input fields based on user actions:
# https://shiny.rstudio.com/gallery/update-input-demo.html (full reference
# provided in the documentation for runPeakMapperApp())
server <- function(input, output, clientData, session) {

  # Define reactive lists to hold the datasets that the user has imported, and
  # the mapping results that the user has generated
  selectableData <- reactiveValues()

  mappingResults <- reactiveValues()


  # Drop-down menus that the user can use to select imported datasets

  # Use of renderUI to create drop-down menus that update as new datasets are
  # imported is based on code from the following RStudio Shiny tutorial:
  # https://shiny.rstudio.com/articles/dynamic-ui.html (full reference provided
  # in the documentation for runPeakMapperApp())

  output$selectInput <- renderUI({
    selectInput('previewData', 'Select an imported dataset to display:',
                names(reactiveValuesToList(selectableData)))
  })

  output$selectPeaks <- renderUI({
    selectInput('selectedPeak', 'Select an imported peak file for mapping:',
                names(reactiveValuesToList(selectableData)))
  })

  output$selectFeatures <- renderUI({
    selectInput('selectedFeature', 'Select an imported feature file for mapping:',
                names(reactiveValuesToList(selectableData)))
  })

  output$selectPeaksForPlotting <- renderUI({
    selectInput('selectedPeaksForPlotting',
                'Select the peak file used to generate these mapping results:',
                names(reactiveValuesToList(selectableData)))
  })


  # Drop-down menus that the user can use to select results generated by
  # mapping peaks onto features

  # Use of renderUI to create drop-down menus that update as new datasets are
  # imported is based on code from the following RStudio Shiny tutorial:
  # https://shiny.rstudio.com/articles/dynamic-ui.html (full reference provided
  # in the documentation for runPeakMapperApp())

  output$selectResult <- renderUI({
    selectInput('selectedResult', 'Select mapping results to display or export:',
                names(reactiveValuesToList(mappingResults)))
  })

  output$selectResultsForPlotting <- renderUI({
    selectInput('selectedResultsForPlotting',
                'Select mapping results to plot:',
                names(reactiveValuesToList(mappingResults)))
  })


  # When the user inputs a file, import it using importBED and add it to
  # selectableData
  observeEvent(input$file, {
    newName <- input$file$name
    selectableData[[newName]] <- PeakMapper::importBED(input$file$datapath)
  })

  # When the user presses the preview button in Step 1, update the table to
  # display the selected BED file
  selectedData <- eventReactive(input$button, {
    selectableData[[input$previewData]]
  })

  output$previewTable <- renderTable(selectedData())


  # When the user presses the mapping button in Step 2, map peaks onto features
  # using mapPeaks
  observeEvent(input$mappingButton, {
    peakName <- input$selectedPeak
    featureName <- input$selectedFeature
    resultName <- paste(
      "Peaks: ", peakName, ", Features: ", featureName, sep = "")

    mappingResults[[resultName]] <- PeakMapper::mapPeaks(
                                            selectableData[[peakName]],
                                            selectableData[[featureName]])

    output$mappingMessage <- renderText(paste("Peaks in ", peakName,
                                        " successfully mapped onto features in ",
                                        featureName, ". Results can be selected ",
                                        "in the menu below.", sep = ""))
  })

  # When the user presses the display button in Step 2, update the table to show
  # the currently-selected mapping results
  mappingResult <- eventReactive(input$mappingDisplayButton, {
     mappingResults[[input$selectedResult]]
  })

  output$mappingTable <- renderTable(mappingResult())


  # When the user presses the plot generation button in Step 3, determine the
  # type of plot to make based on the user's selection, and then create that
  # plot using the user's selected data
  mainPlot <- eventReactive(input$plottingButton, {
    if (input$selectPlotType ==
       "1. Plot the number of closest features to each peak") {
        PeakMapper::plotNumMappingsPerPeak(
                            selectableData[[input$selectedPeaksForPlotting]],
                            mappingResults[[input$selectedResultsForPlotting]],
                            plotColor = input$plotColor,
                            mainTitle = input$mainTitle,
                            xTitle = input$xTitle,
                            yTitle = input$yTitle,
                            backgroundStyle = input$backgroundStyle)

    } else if (input$selectPlotType ==
      "2. Plot locations of mapped peaks relative to start of closest features") {
        PeakMapper::plotPeakLocations(
                             mappingResults[[input$selectedResultsForPlotting]],
                             plotColor = input$plotColor,
                             mainTitle = input$mainTitle,
                             xTitle = input$xTitle,
                             yTitle = input$yTitle,
                             backgroundStyle = input$backgroundStyle,
                             upstreamBins = input$upstreamBins,
                             downstreamBins = input$downstreamBins ,
                             basesPerBin = input$basesPerBin)

    } else if (input$selectPlotType ==
      "3. Plot distribution of overlap proportions between peaks and closest features") {
        PeakMapper::plotOverlapDistribution(
                             mappingResults[[input$selectedResultsForPlotting]],
                             plotColor = input$plotColor,
                             mainTitle = input$mainTitle,
                             xTitle = input$xTitle,
                             yTitle = input$yTitle,
                             backgroundStyle = input$backgroundStyle)

    } else if (input$selectPlotType ==
        "4. Plot distribution of qualitative peak positions relative to mapped features") {
        PeakMapper::plotPeakOrientations(
                            mappingResults[[input$selectedResultsForPlotting]],
                            plotColor = input$plotColor,
                            mainTitle = input$mainTitle,
                            xTitle = input$xTitle,
                            yTitle = input$yTitle,
                            backgroundStyle = input$backgroundStyle)
    }

  })

  output$mainPlot <- renderPlot(mainPlot())


  # When the user selects the type of plot they want to create, update the text
  # fields for the main title, x-axis title, and y-axis title to hold the default
  # values for that plot type

  # Code to update these text fields using updateTextInput is based on code
  # provided in the following RStudio Shiny tutorial:
  # https://shiny.rstudio.com/gallery/update-input-demo.html
  # (full reference provided in the documentation for runPeakMapperApp())
  titles <- reactiveValues()

  observeEvent(input$selectPlotType, {

    if (input$selectPlotType ==
        "1. Plot the number of closest features to each peak") {
      titles[["mainTitle"]] <- "Number of features closest to each peak"
      titles[["xTitle"]] <- "Number of features per peak"
      titles[["yTitle"]] <- "Number of peaks"

    } else if (input$selectPlotType ==
        "2. Plot locations of mapped peaks relative to start of closest features") {
      titles[["mainTitle"]] <- "Peak positions relative to feature start"
      titles[["xTitle"]] <- "Position relative to feature start (bp)"
      titles[["yTitle"]] <- "Number of peaks"

    } else if (input$selectPlotType ==
        "3. Plot distribution of overlap proportions between peaks and closest features") {
      titles[["mainTitle"]] <- "Distribution of feature overlaps by mapped peaks"
      titles[["xTitle"]] <- "Proportion of feature overlapped by mapped peak"
      titles[["yTitle"]] <- "Number of peak-feature pairs"

    } else if (input$selectPlotType ==
      "4. Plot distribution of qualitative peak positions relative to mapped features") {
     titles[["mainTitle"]] <- "Position of peaks relative to closest features"
     titles[["xTitle"]] <- "Relative peak position"
     titles[["yTitle"]] <- "Number of peak-feature pairs"
    }

    updateTextInput(session, "mainTitle", value = titles[["mainTitle"]])
    updateTextInput(session, "xTitle", value = titles[["xTitle"]])
    updateTextInput(session, "yTitle", value = titles[["yTitle"]])

  })


  # The following 2 blocks of code that use the downloadHandler function to
  # download data from the app are based off of code from the following
  # RStudio Shiny tutorial: https://shiny.rstudio.com/articles/download.html
  # (full reference provided in the documentation for runPeakMapperApp())

  # Download the currently-selected mapping results when the user presses the
  # export button
  output$exportButton <- downloadHandler(
    filename = "mappingResult.csv",
    content = function(file) {
      write.csv(mappingResults[[input$selectedResult]], file)
    }
  )

  # Download the currently-displayed plot when the user presses the save button
  output$savePlot <- downloadHandler(
    filename = "plot.png",
    content = function(file) {
      ggplot2::ggsave(file, width = 8, height = 7)
    }
  )

}

shinyApp(ui = ui, server = server)

# [END]
fuscada2/PeakMapper documentation built on Dec. 8, 2019, 12:35 p.m.