4

I have never worked with APIs so this is my first try, I don't even know if what I'm trying to do is possible.

I'm trying to obtain pictures of cells from the SwissBioPics API (https://www.npmjs.com/package/%40swissprot/swissbiopics%2Dvisualizer) and have them in my R session.

res <-  httr::GET('https://www.swissbiopics.org/static/swissbiopics.js',
                     query = list(taxisid = '9606', sls= 'SL0073',gos = '0005641'))
result <- httr::content(res$content)


but I'm getting this error:

Error in httr::content(res$content) : is.response(x) is not TRUE

Any clues?

CodingBiology
  • 262
  • 4
  • 13
  • Thank goodness, I think it's finally done! Let me know if [my solution](https://stackoverflow.com/a/68447794) works in your RStudio. – Greg Jul 20 '21 at 02:59

3 Answers3

7

After many misadventures, I have your answer as promised!

Since it involves interactive imagery, courtesy of JavaScript and HTML, this solution must be run as a .Rmd file within RStudio. The interactive imagery can also be accessed in the eponymous .html file, output by knitr when you click the Knit button in RStudio.

Step 1: Project Setup

Create a new R project my_pics in RStudio, under a new directory. From within this project, create a new R Notebook (here my_book.Rmd), which should end up right next to my_pics.Rproj under the aforementioned directory.

Step 2: Supporting Files

Under that same directory, create a ./snippets subdirectory. The latter should contain the following two .txt files, copied (and corrected) from the swissbiopics-visualizer documentation:

  • templates.txt: the first code block given in the documentation. Reproduced here with necessary EOF and syntactically corrected comments:
<template id="sibSwissBioPicsStyle">
    <style>
        ul > li > a {
            font-style:oblique;
        }
        ul.notpresent li > .subcell_description {
            display:none;
        }
    </style>
</template>
<template id="sibSwissBioPicsSlLiItem">
    <li class="subcellular_location">
         <a class="subcell_name"></a> <!-- the class name is required and textContent will be set on it -->
         <span class="subcell_description"></span> <!-- the class name is required and textContent will be set on it -->
    </li>
</template>

  • custom_element.txt: the third code block given in the documentation. Reproduced here with necessary EOF:
<script type="module" src="https://www.swissbiopics.org/static/swissbiopics.js"></script>
<script defer>
    if (! window.customElements.get("sib-swissbiopics-sl"))
        window.customElements.define("sib-swissbiopics-sl", SwissBioPicsSL);
</script>

Mind you, these .txt files can just as easily be saved as .html files. Only the file extensions would need to be refactored, in the default values for the templates and custom_element parameters, for the make_html() function in the my_book.Rmd code below.

Step 3: Interact in RStudio

Now we are ready! In my_book.Rmd, write the following:

---
title: "R Notebook"
output: html_document
---


```{r}
library(htmltools)
library(readr)
library(rlang)
```



# Functions #

Here are the functions that do the trick.  The snippets used by `make_html()` are copied from the [documentation](https://www.npmjs.com/package/%40swissprot/swissbiopics-visualizer#usage) for `swissbiopics-visualizer`, and (after fixing the HTML comments) pasted into `.txt` files (`templates.txt` and `custom_element.txt`) under the `./snippets` subdirectory, which lies within the directory containing this `.Rproj`.

```{r}
# Create comma-separated list from vectorized (or listed) items, safely escaped.
csl <- function(items) {
  return(paste("\"", paste(htmltools::htmlEscape(unlist(items)), collapse = ",", sep = ""), "\"", sep = ""))
}



# Create the HTML for the interactive imagery given by the parameters. Assembly
# process is as described the documentation for 'swissbiopics-visualizer':
#   https://www.npmjs.com/package/%40swissprot/swissbiopics-visualizer#usage
make_html <- function(# The NCBI taxonomy ID.
                      tax_id,
                      
                      # The IDs of the cellular elements to highlight.
                      sl_ids,
                      
                      # The filepath to (or raw HTML text of) the templates
                      # snippet.
                      templates = "./snippets/templates.txt",
                      
                      # The filepath to (or raw HTML text of) the custom element
                      # snippet.
                      custom_element = "./snippets/custom_element.txt",
                      
                      # Further arguments to 'readr::read_file()', which might
                      # be useful to process snippet encodings across platforms.
                      ...) {
  # Escape any strings supplied.
  tax_id <- csl(tax_id[1])
  sl_ids <- csl(sl_ids)
  
  
  # Compile all the HTML snippets into a list:
  elements <- list()
  
  
  # Include the templates (as read)...
  elements$templates <- readr::read_file(file = templates, ...)
  
  
  # ...then include the line (created here) to target the right picture...
  elements$identifier <- "<sib-swissbiopics-sl taxid=%s sls=%s></sib-swissbiopics-sl>"
  elements$identifier <- sprintf(fmt = elements$identifier, tax_id, sl_ids)
  
  
  # ...and finally include the definition (as read) for the custom element.
  elements$custom_element <- readr::read_file(file = custom_element, ...)
  
  
  # Append these snippets together, into the full HTML code.
  return(paste(unlist(elements), collapse = "\n"))
}



# Display the interactive imagery given by the parameters, visible in both
# RStudio (crowded) and the R Markdown file (well laid out).
visualize <- function(# The NCBI taxonomy ID.
                      taxid = "9606",
                      
                      # A list (or vector) of the UniProtKB subcellular location
                      # (SL) IDs for the cellular elements to highlight.
                      sls = list("SL0073"),
                      
                      # Further arguments to 'make_html()'.
                      ...
                      ) {
  # Embed the HTML text where this function is called.
  return(htmltools::HTML(make_html(tax_id = taxid, sl_ids = sls, ...)))
}
```



# Results #

Here we `visualize()` the **interactive** image, also accessible on [SwissBioPics](https://www.swissbiopics.org):

```{r}
visualize(sls = list("SL0073", "SL0138"))
```

Note

Observe how (in this case) we "lazily" use the default value ("9606") for taxid, without having to specify it. Observe also how we can simultaneously highlight not one but multiple separate components, namely the Contractile vacuole ("SL0073") and the Cell cortex ("SL0138").


Now below that last chunk where visualize() is called

```{r}
visualize(sls = list("SL0073", "SL0138"))
```

you will see interactive output that looks like this:

enter image description here

Sadly, it appears extremely crowded in RStudio, and an HTML wizard might be needed to alter the supporting .txt (or .html) files, to achieve properly formatted HTML within this IDE.

Step 4: Embed in Reports

As with any .Rmd file, RStudio gives you the option to Knit the Markdown results into a .html file, which can be easily accessed and beautifully formatted as a report!

With my_book.Rmd open in RStudio, click the Knit button, and my_book.html should appear within that same directory. You can open this .html file in a web browser (I used Chrome) to see it in all its glory!

enter image description here

In Conclusion

With either of these two interactive images, you can hover to highlight the various components and layers of the diagram. Furthermore, clicking on any definition will take you by hyperlink to its profile on UnitProt.

Many of the remaining limitations are due to the swissbiopics-visualizer API itself. For example, there seems to be a malfunction in its mapping from GO IDs to SL IDs, via this dataset. As such, you should provide only SL codes to visualize().

That said, if you can wrangle that HTML and bend its layout to your will, the sky is the limit!

Enjoy!


Bonus

Here's a demo of the same interactive output, embedded right here in Stack Overflow! Unfortunately, it's unstable and horribly unwieldy in this environment, so I've left it as a mere "footnote":

<template id="sibSwissBioPicsStyle">
    <style>
        ul > li > a {
            font-style:oblique;
        }
        ul.notpresent li > .subcell_description {
            display:none;
        }
    </style>
</template>
<template id="sibSwissBioPicsSlLiItem">
    <li class="subcellular_location">
         <a class="subcell_name"></a> <!-- the class name is required and textContent will be set on it -->
         <span class="subcell_description"></span> <!-- the class name is required and textContent will be set on it -->
    </li>
</template>


<sib-swissbiopics-sl taxid="9606" sls="SL0073,SL0138" ></sib-swissbiopics-sl>


<script type="module" src="https://www.swissbiopics.org/static/swissbiopics.js"></script>
<script defer>
    if (! window.customElements.get("sib-swissbiopics-sl"))
        window.customElements.define("sib-swissbiopics-sl", SwissBioPicsSL);
</script>
Greg
  • 3,054
  • 6
  • 27
  • It works wonderfully, also with different taxIds and different subcell types. Just a follow-up question (and last I promise) before accepting it. If I would like to save **only** the cell picture as a static image, do I have to mess with the JavaScript code? Or there's a more straightforward way of doing that? – CodingBiology Jul 20 '21 at 09:39
  • @CodingBiology Hmmm...let me check that out. The strength of this solution lies in its modularity. The JS script is automatically invoked by the HTML, in that last snippet; and since the script is never explicitly written out, but rather sourced (`src="https://www.swissbiopics.org/static/swissbiopics.js"`) from the site, we never really get to fiddle with the literal JS text itself. It keeps things clean and compartmentalized, though at the (generally minor) cost of abstraction. – Greg Jul 20 '21 at 14:01
  • @CodingBiology Can I ask how you manage to single out certain images (like the one titled "[**Animal cell**](https://www.swissbiopics.org/name/Animal_cell)") by their `taxid` (like `"9606"`)? Unfortunately, the API seems to provide no way of associating a given `taxid` with its title...and this title appears to be the only way to access the corresponding [static image](https://www.swissbiopics.org/api/image/Animal_cell.svg): `"https://www.swissbiopics.org/api/image/` **`Animal_cell`** `.svg"`. – Greg Jul 20 '21 at 14:31
  • Indeed, after removing a bunch of code of that JS code, I am able to obtain only the picture of the cell that I'm looking for. However, it is still in an HTML document, and I haven't found yet a way to make it just a static image. – CodingBiology Jul 20 '21 at 14:32
  • I am not able either to go from the `taxid` like `"9095"` to `Animal_cell` either. I believe the API checks the NCBI database. Right now, I'm getting the cell with certain organeles colored in an HTML which is awesome. However, it'll give us much flexibility to have it as an object, that can be combined, stored, etc. – CodingBiology Jul 20 '21 at 15:00
  • 1
    I _think_ I have a solution, though it could get a little messy. – Greg Jul 20 '21 at 15:03
  • @CodingBiology It involves using a modified `.js` script, saved as `./snippets/script.txt` (or `.js`), as the `src=""` url within the HTML. Then it would use `htmlwidgets::saveWidget()` to save each interactive image as its own `.html` file under `./images`, and finally use `webshot::webshot()` to screenshot that `.html` image. However, certain components might still remain highlighted. – Greg Jul 20 '21 at 15:09
  • @CodingBiology **Question**: will the [image library](https://www.swissbiopics.org/) ever change? With the addition of new images, the alteration of existing images, or the removal of existing images? If not, we could try to create a manual mapping file (a `.csv` or something) that would convert each `taxid` to the appropriate title, which we could splice into `"https://www.swissbiopics.org/api/image/`...`.svg"`. This would be a **lot** simpler than my [suggestion above](https://stackoverflow.com/posts/comments/120985309). – Greg Jul 20 '21 at 15:12
  • It will probably change indeed since new species are discovered daily. I have tried the webshot::webshot(cell_picture.html) and webshot::rmdshot(Rnotebook.rmd) but both pngs are empty, I'm not sure why though. – CodingBiology Jul 20 '21 at 15:37
  • @CodingBiology I think we're in the same boat: _"but both pngs are empty"_. – Greg Jul 20 '21 at 15:43
  • @CodingBiology I'm currently investigating `webshot2`, to see if it can provide any better alternatives. I suspect the issue with `webshot` is that the `.js` script itself is being invoked in the `.html` file, rather than the `.html` file including the **results** of the `.js` as HTML. **UPDATE:** my `webshot2` attempt also failed. I think we somehow need to get the HTML _results_ of the `.js` — that is. the rendered HTML for the inline preview within RStudio — if we want to effectively `webshot()` the image. – Greg Jul 20 '21 at 15:53
  • @CodingBiology I have the solution! **When calling `webshot::webshot()`, increase the `delay` argument, so the HTML has enough time to load and render.** – Greg Jul 20 '21 at 15:59
  • Ahh I'm almost there, I must be doing something wrong. I've tried up to delay = 100, but still no luck. Which one did you use? – CodingBiology Jul 20 '21 at 17:34
  • Hmmm...I might have to get back to you on that. Now it's no longer working, likely due to file encoding or some other garbage. Frankly, this might be a topic for an entirely new question. – Greg Jul 20 '21 at 18:25
  • Okay @Greg, thanks for the immense help! I'll let you know if I find out how to obtain those tricky `.png` – CodingBiology Jul 20 '21 at 18:48
  • @CodingBiology My pleasure! Given time, I might be able to get you what you need, so I'll either update this answer here or I'll post in answer to your new question...if no one beats me to the punch. Good luck! – Greg Jul 20 '21 at 18:56
  • 1
    webshot2 with one second delay is working nicely for me :) – CodingBiology Jul 21 '21 at 08:19
  • 1
    Btw @Greg, I'm creating an R package out of this, so if you want you're more than welcome to collaborate :) – CodingBiology Jul 21 '21 at 10:12
  • @CodingBiology That's awesome, and thank you! I actually had the same inclination myself (though I never managed to finish the last package I began). I can't promise much in the immediate present — life is happening fast on my end — but feel free to send me a link to (I presume) the GitHub repo? I'll just have to dust off my old GitHub account or make a new one. What are you calling it, by the way? Perhaps `swissbiopics_visualizer`? `visualizeR` is taken on GitHub ([here](https://github.com/SantanderMetGroup/visualizeR) and [here](https://github.com/jwist/visualizeR)), though not yet on CRAN. – Greg Jul 21 '21 at 14:01
  • Awesome, even if you don't collaborate much, you deserve to be put as one of the authors. So far I called it drawCell (but we can discuss it too). Here's the link, https://github.com/svalvaro/drawCell – CodingBiology Jul 21 '21 at 14:17
  • @CodingBiology That's extremely kind of you! And I like what I've seen of it so far! I'll reach out once I get the chance for a deeper look. :) – Greg Jul 21 '21 at 14:21
  • Thanks! Absolutely, see you in Github :) – CodingBiology Jul 21 '21 at 14:36
1

You have to call the content-function on res, not res$content. Then you get raw content which needs to be converted e.g. via

base::rawToChar(content(res))

which results in a string containing some JS-code

base::rawToChar(content(res))
[1] "var SwissBioPics;SwissBioPics=(()=>....
Jonas
  • 1,760
  • 1
  • 3
  • 12
  • Thanks, @Jonas, It works, but I'm not sure how to proceed now from that JS code to the picture that I'm trying to obtain? – CodingBiology Jun 28 '21 at 14:43
  • 1
    @CodingBiology What are you visualizing, when you say _"I'm trying to obtain pictures of cells...and have them in my R session"_? Do you want the **images themselves** to render in RStudio? Or do you want data in RStudio that can be saved to your computer as image files? – Greg Jul 19 '21 at 14:19
  • I do want the images themselves. For example, Uniprot uses the API of swiss biopics to render those images : https://www.uniprot.org/uniprot/P04637 However the second option might be a workaround that I might be able to make it work – CodingBiology Jul 19 '21 at 14:34
  • 1
    @CodingBiology Did you want these images to be rendered in an R Markdown report? If so, you could save them into an `/images` directory in your `.Rproj`, and have `knitr` render them in the report. – Greg Jul 19 '21 at 14:39
  • @Greg yes that sounds good. However what I'm mainly struggling to achieve is to go from the JavaScript code that it is being generated after `base::rawToChar(content(res))` to the final image. – CodingBiology Jul 19 '21 at 14:42
  • 1
    @CodingBiology I think I have your solution, but I'll have to put 2 and 2 together... – Greg Jul 19 '21 at 14:54
  • @Greg I haven't seen any other example of this kind of task so that's why I'm not sure of how to approach and that is why I came to SO to ask. Sometimes putting 2 and 2 together might be a second order differential equation for users with not as much experience. – CodingBiology Jul 19 '21 at 15:12
  • @CodingBiology I definitely have a **huge** lead here. Might take a couple days to get back to you with a working version, though. You good with a `.Rproj` file that knits to HTML? – Greg Jul 19 '21 at 15:34
  • Thanks @Greg, yeah that would be perfect! No problem, whatever it takes. – CodingBiology Jul 19 '21 at 19:06
  • @CodingBiology How confident are we in this API? I followed the [documentation](https://www.npmjs.com/package/%40swissprot/swissbiopics-visualizer), and while I got the HTML working as an interactive element in RStudio (within an `.Rmd` file), it always defaulted to the [first cell](https://www.swissbiopics.org/name/Animal_cell) on [this page](https://www.swissbiopics.org) — with "Contractile vacuole" highlighted — rather than altering the image to match the new `taxid`. Take that with a massive grain of salt, as I am inexperienced in HTML. Do you have any alternative `taxid`s I could test? – Greg Jul 19 '21 at 19:22
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/235073/discussion-between-greg-and-codingbiology). – Greg Jul 19 '21 at 19:30
0

I have only quickly looked at the website, but what about just downloading the files? It also goes through the API.

qurl = "https://www.swissbiopics.org/api/image/Chlamydomona_cells.svg"
fl = file.path(tempdir(), basename(qurl))

download.file(qurl, fl)

Once on disk, you can load the image in R, e.g. through the magick-package:

require(magick)

img = image_read_svg(fl)
print(img)
andschar
  • 3,504
  • 2
  • 27
  • 35