Search code examples
rplotlycontourr-plotlysurface

Find the exact coordinates of a contour on a surface and plot it manually in R plotly


I am drawing a surface plot and would like to "manually" draw a contour line using plotly. In the code below I:

  • simulate the data for drawing the surface plot
  • calculate the coordinates of the contour line at a specific z level using the contoureR package
  • draw the surface plot and contour line
# Load packages
library(plotly) # for interactive visualizations
library(contoureR) # for calculating contour coordinates

# Simulate the data for plotting
x <- y <- seq(from = 0, to = 100, by = 1)
z1 <- outer(X = x, Y = y, FUN = function(x, y) x^0.2 * y^0.3) # data for surface plot

# Obtain coordinates of contour for z = 5
z_level <- 5
r <- contourLines(x = x, y = y, z = z1, levels = z_level)

plot_ly(
  type = "surface",
  x = x,
  y = y,
  z = z1,
) %>%
  add_trace(
    type = "scatter3d",
    x = r[[1]]$x,
    y = r[[1]]$y,
    z = z_level
  )

contour_line1

I am aware that these are all approximations, so I also tried to pass the x and y coordinates produced by contourLines() to the formula used to create z1above and use the corresponding values to plot my contour line (instead of using z_level = 5, but I still do not obtain the desired result:

plot_ly(
  x = x,
  y = y,
  z = z1,
  type = "surface"
) %>%
  add_trace(
    type = "scatter3d",
    x = r[[1]]$x,
    y = r[[1]]$y,
    z = r[[1]]$x^0.2*r[[1]]$y^0.3
  )

contour_line2

I alo know that plotly enables me to draw specific contour lines (see my question and answer here: Add a permanent contour line to a surface plot in R plotly). However, I would like to draw my contour line myself (after getting their coordinates) so it can "pull" by cursor and show me the tooltip info whenever I hover over it. Ideally, if there was a way to obtain the contour lines coordinates as computed by plotly itself, that would be great.

Thank you for your help.


Solution

  • I was able to find two solutions to this problem.

    Solution 1: transpose the z1 matrix

    The first solution was given me by @nirgrahamuk and it consists in transposing the z1 matrix:

    library(plotly) # for interactive visualizations
    
    # Simulate the data for plotting
    x <- y <- seq(from = 0, to = 100, by = 1)
    z1 <- outer(X = x, Y = y, FUN = function(x, y) x^0.2 * y^0.3) # data for surface plot
    
    # Obtain coordinates of contour for z = 5
    z_level <- 6
    r <- contourLines(x = x, 
                      y = y, 
                      z = z1, 
                      levels = z_level)
    
    plot_ly(
      type = "surface",
      z = t(z1), # *** WE TRANSPOSE THE MATRIX HERE! ***
    ) %>%
      add_trace(
        type = "scatter3d",
        x = r[[1]]$x,
        y = r[[1]]$y,
        z = z_level
      )
    

    Solution 2: use the isoband package

    The second solution is to compute the contour lines coordinates with the isoband::isolines() function:

    library(plotly) # for interactive visualizations
    library(isoband) # for find contour lines coordinates
    
    # Simulate the data for plotting
    x <- y <- seq(from = 0, to = 100, by = 1)
    z1 <- outer(X = x, Y = y, FUN = function(x, y) x^0.2 * y^0.3) # data for surface plot
    
    # Obtain coordinates of contour for z = 5
    z_level <- 6
    r <- isolines(x = x, # *** WE USE THE isolines() FUNCTION HERE ***
                  y = y, 
                  z = z1, 
                  levels = z_level)
    
    plot_ly(
      type = "surface",
      z = z1, 
    ) %>%
      add_trace(
        type = "scatter3d",
        x = r[[1]]$x,
        y = r[[1]]$y,
        z = z_level
      )