Q:

Shiny + ggplot2 - plotOutput - zoom - floating geom_text position

I have a plotOutput. The user selects a subarea, then double clicks, then the range of the plot (via a call to ggplot's coord_cartesian) adapts so that the graph is now zoomed on the subarea. It works ok: going from img1 to img2. 
The issue is that the position of the geom_text labels, because it is currently specified in absolute terms (x=Score - .75) does not adapt to the change in scale. The result is a messy graph (img2).
I have tried replacing x=Score - .15 with Score-((ranges$x[2]-ranges$x[1])*.2); the latter expression is a double, the value of which depends on the current level of zoom. 
But R does not like it when I make that replacement (here is the error I get) : 
Listening on http://127.0.0.1:7310
Warning: Error in : Aesthetics must be either length 1 or the same as the data (4): x, label, y
Stack trace (innermost first):
    110: check_aesthetics
    109: f
    108: l$compute_aesthetics
    107: f
    106: by_layer
    105: ggplot2::ggplot_build
    104: print.ggplot
    103: print
     92: <reactive:plotObj>
     81: plotObj
     80: origRenderFunc
     79: output$XassetOverview
      4: <Anonymous>
      3: do.call
      2: print.shiny.appobj
      1: print

Img1:

Img2: 

Full code (problematic line commented out): 
server = function (input, output){
  # store range in a reactiveValues pair
  ranges <- reactiveValues(x = NULL, y = NULL)
  # generate the data
  XassetOverviewData <- reactive({
    dataCrossAsset <- data.frame(c("point1", "point2", "point3"), c(50,33,45), c(49,50,53))
    dataCrossAsset <-
      setNames(dataCrossAsset,
               c("Name", "Correlation", "Score"))
    return(dataCrossAsset)
  })
  # generate the plot
  output$XassetOverview <- renderPlot({
    ggplot(XassetOverviewData(), aes(x = Score, y = Correlation)) + 
      geom_point(size = 5) + 
      coord_cartesian(xlim = ranges$x, ylim = ranges$y) +  
      geom_text(aes(x = Score - .15, label = Name), size = 3) 
      # solution... causing a bug: 
      # geom_text(aes(x = Score -  ((ranges$x[2]-ranges$x[1])*.2), label = Name), size = 2) 
  })
  # observeEvent
  observeEvent(input$plot1_dblclick, {
    brush <- input$plot1_brush
    if (!is.null(brush)) {
      ranges$x <- c(brush$xmin, brush$xmax)
      ranges$y <- c(brush$ymin, brush$ymax)

    } else {
      ranges$x <- NULL
      ranges$y <- NULL
    }
    adjustment <- ((ranges$x[2]-ranges$x[1])*.2)
    cat(adjustment, file = stderr())
   })
}

ui = basicPage(plotOutput(click = "plot_click",
                          outputId = "XassetOverview",
                          dblclick = "plot1_dblclick",
                          brush = brushOpts(id = "plot1_brush", resetOnNew = TRUE)
))

shinyApp(server=server, ui=ui)

A:

The reason is that initially ranges$x is null. So you pass a null to geom_text. You should make a simple check if a double click occured already by checking the length of ranges$x: if(length(ranges$x)).
server = function (input, output){
  # store range in a reactiveValues pair
  ranges <- reactiveValues(x = NULL, y = NULL)
  # generate the data
  XassetOverviewData <- reactive({
    dataCrossAsset <- data.frame(c("point1", "point2", "point3"), c(50,33,45), c(49,50,53))
    dataCrossAsset <-
      setNames(dataCrossAsset,
               c("Name", "Correlation", "Score"))
    return(dataCrossAsset)
  })
  # generate the plot
  output$XassetOverview <- renderPlot({
      plot <- ggplot(XassetOverviewData(), aes(x = Score, y = Correlation)) + 
              geom_point(size = 5) + 
              coord_cartesian(xlim = ranges$x, ylim = ranges$y) +  
              geom_text(aes(x = Score - .15, label = Name), size = 3)
              if(length(ranges$x)){
                plot <- plot + geom_text(aes(x = Score -  ((ranges$x[2]-ranges$x[1]) *.1), label = Name), size = 3)
              } else{
                plot <- plot +  geom_text(aes(x = Score - .15, label = Name), size = 3) 
              }
      plot
  })
  # observeEvent
  observeEvent(input$plot1_dblclick, {
    brush <- input$plot1_brush
    if (!is.null(brush)) {
      ranges$x <- c(brush$xmin, brush$xmax)
      ranges$y <- c(brush$ymin, brush$ymax)
    } else {
      ranges$x <- NULL
      ranges$y <- NULL
    }
    adjustment <- ((ranges$x[2]-ranges$x[1])*.2)
    cat(adjustment, file = stderr())
  })
}

ui = basicPage(plotOutput(click = "plot_click",
                          outputId = "XassetOverview",
                          dblclick = "plot1_dblclick",
                          brush = brushOpts(id = "plot1_brush", resetOnNew = TRUE)
))

shinyApp(server=server, ui=ui)

