Thicket data repository for the EEG
1{
2 "id": "https://www.tunbury.org/2025/07/02/bon-in-a-box",
3 "title": "BON in a Box",
4 "link": "https://www.tunbury.org/2025/07/02/bon-in-a-box/",
5 "updated": "2025-07-02T00:00:00",
6 "published": "2025-07-02T00:00:00",
7 "summary": "On a suggestion from Michael, I have had a quick look at BON in a Box, which is a web-based biodiversity analysis platform using Docker containerised pipelines running R, Julia, and Python scripts.",
8 "content": "<p>On a suggestion from Michael, I have had a quick look at <a href=\"https://geo-bon.github.io/bon-in-a-box-pipeline-engine/\">BON in a Box</a>, which is a web-based biodiversity analysis platform using Docker containerised pipelines running R, Julia, and Python scripts.</p>\n\n<p>It couldn’t be easier to get started. Install Docker and Docker Compose, and make sure you can access GitHub via SSH using a public key. [Run <code>ssh-keygen -t ed25519</code> and then publish the resulting <code>~/.ssh/id_ed25519.pub</code> to your GitHub account.]</p>\n\n<div><div><pre><code>apt <span>install </span>docker.io docker-compose-v2\n</code></pre></div></div>\n\n<p>Clone the GEO-BON’s repository and make a working copy of the <code>runner.env</code> file. This file can be edit to add API keys of datasets, but I don’t have any so the default file is fine.</p>\n\n<div><div><pre><code>git clone git@github.com:GEO-BON/bon-in-a-box-pipelines.git\n<span>cd </span>bon-in-a-box\n<span>cp </span>runner-sample.env runner.env\n</code></pre></div></div>\n\n<p>To start the server run <code>./server-up.sh</code>. There is also <code>./server-down.sh</code> to stop the server.</p>\n\n<p>The first run downloads the required Docker containers so takes a few minutes. Once complete visit <a href=\"http://localhost\">http://localhost</a> to see the web GUI.</p>\n\n<p>I ran the “Get Country Polygon” script, creating a nice Colombia polygon.</p>\n\n<p>There is a drag and drop pipeline editor which felt a lot like Microsoft Access.</p>\n\n<p><img alt=\"\" src=\"https://www.tunbury.org/images/geobon-pipeline.png\"></p>\n\n<p>I followed along with the tutorial and created an R script and a YAML file of the same name in the <code>/scripts</code> directory. These appeared in the GUI, allowing me to run them and use them in the pipeline editor. Annoyingly, the dataset was not provided in the tutorial, so I couldn’t run the code.</p>\n\n<p><code>TestScript.R</code></p>\n\n<p>The <code>biab</code> functions are how the script interacts with the BON in a Box system.</p>\n\n<div><div><pre><code><span>library</span><span>(</span><span>rjson</span><span>)</span><span>\n</span><span>library</span><span>(</span><span>sf</span><span>)</span><span>\n</span><span>library</span><span>(</span><span>terra</span><span>)</span><span>\n</span><span>library</span><span>(</span><span>dplyr</span><span>)</span><span>\n</span><span>library</span><span>(</span><span>ggplot2</span><span>)</span><span>\n\n</span><span>input</span><span> </span><span><-</span><span> </span><span>biab_inputs</span><span>()</span><span>\n\n</span><span>dat</span><span> </span><span><-</span><span> </span><span>st_read</span><span>(</span><span>input</span><span>$</span><span>country_polygon</span><span>)</span><span>\n\n</span><span>if</span><span> </span><span>(</span><span>nrow</span><span>(</span><span>dat</span><span>)</span><span>==</span><span>0</span><span>)</span><span> </span><span>{</span><span>\n </span><span>biab_error_stop</span><span>(</span><span>\"Country polygon does not exist\"</span><span>)</span><span>\n</span><span>}</span><span> \n \n</span><span>dat.transformed</span><span> </span><span><-</span><span> </span><span>st_transform</span><span>(</span><span>dat</span><span>,</span><span> </span><span>crs</span><span>=</span><span>input</span><span>$</span><span>crs</span><span>)</span><span>\n\n</span><span>rasters</span><span> </span><span><-</span><span> </span><span>terra</span><span>::</span><span>rast</span><span>(</span><span>c</span><span>(</span><span>input</span><span>$</span><span>rasters</span><span>,</span><span> </span><span>crs</span><span>=</span><span>intput</span><span>$</span><span>crs</span><span>))</span><span>\n\n</span><span>country_vect</span><span> </span><span><-</span><span> </span><span>vect</span><span>(</span><span>dat.transformed</span><span>)</span><span>\n \n</span><span>raster.cropped</span><span> </span><span><-</span><span> </span><span>mask</span><span>(</span><span>rasters</span><span>,</span><span> </span><span>country_vect</span><span>)</span><span> \n \n</span><span>raster_change</span><span> </span><span><-</span><span> </span><span>rasters</span><span>[[</span><span>1</span><span>]]</span><span>-</span><span>rasters</span><span>[[</span><span>2</span><span>]]</span><span>\n\n</span><span>raster_change_path</span><span> </span><span><-</span><span> </span><span>file.path</span><span>(</span><span>outputFolder</span><span>,</span><span> </span><span>\"raster_change.tif\"</span><span>)</span><span>\n</span><span>writeRaster</span><span>(</span><span>raster_change</span><span>,</span><span> </span><span>raster_change_path</span><span>)</span><span>\n\n</span><span>biab_output</span><span>(</span><span>\"raster_change\"</span><span>,</span><span> </span><span>raster_change_path</span><span>)</span><span>\n\n</span><span>layer_means</span><span> </span><span><-</span><span> </span><span>global</span><span>(</span><span>rasters.cropped</span><span>,</span><span> </span><span>fun</span><span>=</span><span>\"mean\"</span><span>,</span><span> </span><span>na.rm</span><span>=</span><span>TRUE</span><span>)</span><span>\n</span><span>layer_means</span><span>$</span><span>name</span><span> </span><span><-</span><span> </span><span>names</span><span>(</span><span>rasters.cropped</span><span>)</span><span>\n \n</span><span>means_plot</span><span> </span><span><-</span><span> </span><span>ggplot</span><span>(</span><span>layer_means</span><span>,</span><span> </span><span>aes</span><span>(</span><span>x</span><span>=</span><span>name</span><span>,</span><span> </span><span>y</span><span>=</span><span>mean</span><span>))</span><span> </span><span>+</span><span> </span><span>geom_point</span><span>()</span><span>\n \n</span><span>means_plot_path</span><span> </span><span><-</span><span> </span><span>file.path</span><span>(</span><span>outputFolder</span><span>,</span><span> </span><span>\"means_plot.png\"</span><span>)</span><span>\n</span><span>ggsave</span><span>(</span><span>means_plot_path</span><span>,</span><span> </span><span>means_plot</span><span>)</span><span>\n \n</span><span>biab_output</span><span>(</span><span>\"means_plot\"</span><span>,</span><span> </span><span>means_plot_path</span><span>)</span><span>\n</span></code></pre></div></div>\n\n<p><code>TestScript.yaml</code></p>\n\n<p>The <code>inputs</code> and <code>outputs</code> section defines the inputs and outputs, where the names must match the names in the script above. The environment is set up using conda. A specific version can be specified like this: <code>r-terra=0.9-12</code></p>\n\n<div><div><pre><code><span>script</span><span>:</span> <span>TestScript.R</span>\n<span>name</span><span>:</span> <span>Test script</span>\n<span>description</span><span>:</span> <span>Demo script</span>\n<span>author</span><span>:</span>\n <span>-</span> <span>name</span><span>:</span> <span>ME</span>\n<span>inputs</span><span>:</span>\n <span>country_ploygon</span><span>:</span>\n <span>label</span><span>:</span> <span>Country Polygon</span>\n <span>description</span><span>:</span> <span>Polygon of the country of interest</span>\n <span>type</span><span>:</span> <span>application/geo+json</span>\n <span>example</span><span>:</span> <span>null</span>\n <span>crs</span><span>:</span>\n <span>label</span><span>:</span> <span>Coordinate reference system</span>\n <span>description</span><span>:</span> <span>Coordinate reference system</span>\n <span>type</span><span>:</span> <span>text</span>\n <span>example</span><span>:</span> <span>\"</span><span>EPSG:3857\"</span>\n <span>rasters</span><span>:</span>\n <span>label</span><span>:</span> <span>Rasters</span>\n <span>description</span><span>:</span> <span>Raster layers of variable of interest</span>\n <span>type</span><span>:</span> <span>image/tiff;application=geotiff[]</span>\n <span>example</span><span>:</span> <span>null</span> \n<span>outputs</span><span>:</span>\n <span>raster_change</span><span>:</span>\n <span>label</span><span>:</span> <span>Rasters</span>\n <span>description</span><span>:</span> <span>Differences between raster values</span>\n <span>type</span><span>:</span> <span>image/tiff;application=geotiff</span>\n <span>means_plot</span><span>:</span>\n <span>label</span><span>:</span> <span>Plot of raster means</span>\n <span>description</span><span>:</span> <span>Plot of means of raster layers</span>\n <span>type</span><span>:</span> <span>image/png</span>\n<span>conda</span><span>:</span>\n <span>channels</span><span>:</span>\n <span>-</span> <span>conda-forge</span>\n <span>-</span> <span>r</span>\n <span>dependencies</span><span>:</span>\n <span>-</span> <span>r-rjson</span>\n <span>-</span> <span>r-sf</span>\n <span>-</span> <span>r-dplyr</span>\n <span>-</span> <span>r-terra</span>\n <span>-</span> <span>r-ggplot2</span>\n</code></pre></div></div>\n\n<p>The architecture appears to be designed as a single-server instance without built-in job queuing or concurrent execution limits.</p>",
9 "content_type": "html",
10 "author": {
11 "name": "Mark Elvers",
12 "email": "mark.elvers@tunbury.org",
13 "uri": null
14 },
15 "categories": [
16 "geobon",
17 "tunbury.org"
18 ],
19 "source": "https://www.tunbury.org/atom.xml"
20}