Skip to contents

This document introduces you to rminka basic set of tools, and shows you how to apply them to obtain your desired information. Once you’ve installed, read vignette(“rminka”) to learn more.

- Project Queries

Project-related functions will be illustrated using the project Biomarató Tarragona 2025.

mnk_proj_byname()

Initially, only the project name is known, so a search is performed to retrieve the corresponding project ID. This is done with mnk_proj_byname(). Here we use the query “biomarato 2025”.

prj_names <- mnk_proj_byname("biomarato 2025")

prj_names 
#> # A tibble: 5 × 8
#>      id title      place_id slug  created_at updated_at project_type description
#>   <int> <chr>         <int> <chr> <chr>      <chr>      <chr>        <chr>      
#> 1   417 BioMARató…      244 biom… 2025-03-2… 2026-05-0… collection   "La BioMAR…
#> 2   418 BioMARató…      245 biom… 2025-03-2… 2026-05-0… collection   "La BioMAR…
#> 3   419 BioMARató…      249 biom… 2025-03-2… 2026-05-0… collection   "La BioMAR…
#> 4   420 BioMARató…      248 biom… 2025-03-2… 2026-05-0… collection   "La BioMAR…
#> 5   424 BioMARato…      701 biom… 2025-04-0… 2025-12-1… collection   "Descobre …

# For this example 

prj_names[,c(1:2)]
#> # A tibble: 5 × 2
#>      id title                     
#>   <int> <chr>                     
#> 1   417 BioMARató 2025 (Catalunya)
#> 2   418 BioMARató 2025 (Girona)   
#> 3   419 BioMARató 2025 (Tarragona)
#> 4   420 BioMARató 2025 (Barcelona)
#> 5   424 BioMARatona 2025

mnk_proj_info()

Once the project ID is known, detailed information can be retrieved with mnk_proj_info(). For the Biomarató Tarragona 2025, the project ID is 419.

prj_info <- mnk_proj_info(419)

prj_info
#> # A tibble: 1 × 7
#>      id title               created_at subscrib_users place_id slug  description
#>   <int> <chr>               <chr>               <int>    <int> <chr> <chr>      
#> 1   419 BioMARató 2025 (Ta… 2025-03-2…             23      249 biom… La BioMARa…

mnk_proj_user()

Users explicitly subscribed to a project can be retrieved with mnk_proj_user() using the project ID

prj_user <- mnk_proj_user(419)

prj_user
#> # A tibble: 23 × 16
#>       id login          name              created_at          observations_count
#>    <int> <chr>          <chr>             <dttm>                           <int>
#>  1     4 xasalva        "xavi salvador c… 2021-04-16 10:44:11              83172
#>  2     6 ramonservitje  ""                2022-04-16 15:47:14               1259
#>  3    11 jaume-piera    "Jaume Piera"     2022-04-18 15:45:37              11332
#>  4    12 sonialinan      NA               2022-04-19 12:53:18                410
#>  5    13 adrisoacha     "Karen Soacha"    2022-04-21 09:40:57                592
#>  6    52 joselu_00      "José Luís Guijo… 2022-05-10 13:38:20                404
#>  7   159 jaumesaltiveri "Jaume Saltiveri" 2022-07-17 13:30:45                  2
#>  8   166 anomalia       "anomalia"        2022-07-19 07:56:08                 23
#>  9   197 peixderoca24   "Guillem Mayor S… 2022-08-08 12:58:18               4546
#> 10   219 ealcaniz       "Edu Alcaniz"     2022-08-27 15:52:52              27917
#> # ℹ 13 more rows
#> # ℹ 11 more variables: identifications_count <int>, species_count <int>,
#> #   activity_count <int>, journal_posts_count <int>, orcid <chr>,
#> #   icon_url <chr>, site_id <int>, roles <list>, spam <lgl>, suspended <lgl>,
#> #   universal_search_rank <int>

Note: The simplest way to retrieve all participants in a project — including those who are not subscribed — is to download the project’s observations with mnk_proj_obs() and then extract the unique values of user_login. Each observation includes the observer’s login, so the set of distinct logins corresponds to the project’s participants.

If the project lasts only one year, or if you are only interested in the participants from a single month, a single function call is sufficient. For example, project 419 ran in 2025, so all its observations can be obtained with mnk_proj_obs(419, year = 2025):

# 1. Download all observations for the project in may 2025

obs_2025 <- mnk_proj_obs(419,year=2025, mont=5, quiet = TRUE)

# 2. Extract the unique participants

participants_may_2025 <- obs_2025 %>%
  distinct(user_login) %>%
  arrange(user_login)

participants_may_2025
#> # A tibble: 30 × 1
#>    user_login          
#>    <chr>               
#>  1 aci                 
#>  2 albertvim           
#>  3 allbluediving       
#>  4 bertinhaco          
#>  5 biosub              
#>  6 elibonfill          
#>  7 elisenda_casabona   
#>  8 evararo             
#>  9 francesca           
#> 10 hectorserranocereijo
#> # ℹ 20 more rows

mnk_proj_obs()

Returns all observations for a project within a selected year, and optionally within a specific month. For the Biomarató Tarragona 2025 (project ID 419), observations for the entire year are obtained with mnk_proj_obs(419, year = 2025), while observations for a single month — for example, May — are obtained with mnk_proj_obs(419, year = 2025, month = 5).

prj_obs <- mnk_proj_obs(419, year= 2025, month=5)
#> --- STARTING DOWNLOAD FOR MONTH: May 2025 ---
#> 
#> --- Evaluating month: May 2025 ---
#> The month of May has 1173 records.
#>  -> Total <= 10,000. Downloading month in one go...
#> 
#> --- FINISHING... ---
#> Download complete! A total of 1,173 records were obtained.

prj_obs[2:14]
#> # A tibble: 1,173 × 13
#>    observed_on  year month  week   day  hour created_at      updated_at latitude
#>    <chr>       <int> <int> <int> <int> <int> <chr>           <chr>         <dbl>
#>  1 2025-05-23   2025     5    21    23    11 2026-03-14T12:… 2026-03-1…     41.1
#>  2 2025-05-24   2025     5    21    24    19 2025-10-22T21:… 2025-10-2…     41.2
#>  3 2025-05-24   2025     5    21    24    19 2025-10-22T21:… 2025-10-2…     41.2
#>  4 2025-05-24   2025     5    21    24    19 2025-10-22T21:… 2025-10-2…     41.2
#>  5 2025-05-24   2025     5    21    24    19 2025-10-22T21:… 2025-10-2…     41.2
#>  6 2025-05-24   2025     5    21    24    19 2025-10-22T21:… 2025-10-2…     41.2
#>  7 2025-05-24   2025     5    21    24    19 2025-10-22T21:… 2025-10-2…     41.2
#>  8 2025-05-24   2025     5    21    24    19 2025-10-22T21:… 2025-11-0…     41.2
#>  9 2025-05-24   2025     5    21    24    18 2025-10-22T21:… 2025-10-2…     41.2
#> 10 2025-05-24   2025     5    21    24    18 2025-10-22T21:… 2025-10-2…     41.2
#> # ℹ 1,163 more rows
#> # ℹ 4 more variables: longitude <dbl>, positional_accuracy <int>,
#> #   geoprivacy <chr>, obscured <lgl>

- User Queries

User-related functions will be illustrated using the user Xavier Salvador.

mnk_user_byname()

Initially, only an approximate name is known, so a search is performed to retrieve the corresponding user_login. For this example, we start with the query “xavi”, which returns a list of possible logins. The desired user — Xavier Salvador — is then selected from that list.

user_name <- mnk_user_byname("xavi")

user_name
#> # A tibble: 6 × 5
#>      id login             name            observations_count created_at         
#>   <int> <chr>             <chr>                        <int> <dttm>             
#> 1    47 xavi               NA                              6 2022-05-06 10:47:06
#> 2     4 xasalva           "xavi salvador…              83172 2021-04-16 10:44:11
#> 3  1178 xparellada        "Xavier Parell…                670 2023-10-31 09:07:52
#> 4   857 xavibou           "Xavi Bou"                    1087 2023-07-28 13:27:50
#> 5  1042 xavi-de-yzaguirre ""                             459 2023-09-26 13:18:42
#> 6 17242 xavisanjuan        NA                            390 2025-07-20 16:21:42

mnk_user_info()

Once the user ID is known, detailed information can be retrieved with mnk_user_info(). For Xavier Salvador (login “xasalva”), the user ID is 4

user_info <- mnk_user_info(4)

user_info
#> # A tibble: 1 × 16
#>      id login name  created_at          observations_count identifications_count
#>   <int> <chr> <chr> <dttm>                           <int>                 <int>
#> 1     4 xasa… xavi… 2021-04-16 10:44:11              83172                420171
#> # ℹ 10 more variables: species_count <int>, activity_count <int>,
#> #   journal_posts_count <int>, orcid <chr>, icon_url <chr>, site_id <int>,
#> #   roles <list>, spam <lgl>, suspended <lgl>, universal_search_rank <int>

mnk_user_proj()

mnk_user_proj() returns the projects to which a user is explicitly subscribed, given the user ID. For Xavier Salvador (user ID 4), the list is obtained with mnk_user_proj(4).

Note: Projects in which a user has contributed observations but is not formally subscribed cannot be retrieved directly in R.

user_project <- mnk_user_proj(4)

user_project
#> # A tibble: 10 × 7
#>       id title                       description slug  icon  place_id created_at
#>    <int> <chr>                       <chr>       <chr> <chr>    <int> <chr>     
#>  1   522 (Principal) Biodiverciutat… "Aquest pr… prin… http…       NA 2025-10-2…
#>  2   144 ANERIS - Biodiversitat D.C… "El projec… aner… http…      337 2023-06-0…
#>  3   312 ANERIS - Biodiversitat Llo… "El projec… aner… http…      416 2024-06-1…
#>  4   146 ANERIS - Biodiversitat UNI… "El projec… aner… http…      338 2023-06-0…
#>  5   177 ANERIS - Biodiversitat al … "El projec… aner… http…      361 2023-08-2…
#>  6   183 ANERIS - SASBA (Seguiment … "Descobrim… aner… http…      251 2023-10-0…
#>  7    44 Anthozoos del Barcelonés    "Estudi de… anth… http…       NA 2022-09-2…
#>  8   444 Arees verdes marines - Emp… "Aquest pr… aree… http…      712 2025-05-2…
#>  9   448 BIODIVERSIDAD MARINA BADIA… "Estudio d… biod… http…      714 2025-05-2…
#> 10   181 BM-PortSalvi                "El projec… bm-p… http…      367 2023-09-1…

mnk_user_obs()

Returns all observations for a user within a selected year, and optionally within a specific month. For Xavier Salvador (user ID 4), observations for the entire year are obtained with mnk_user_obs(4, year = 2025), while observations for a single month, for example May, are obtained with mnk_user_obs(4, year = 2025, month = 5).

user_obs <- mnk_user_obs(user_id= 4, year = 2025, month = 8)
#> --- STARTING DOWNLOAD FOR MONTH: August 2025 ---
#> 
#> --- Evaluating month: August 2025 ---
#> The month of August has 2347 records.
#>  -> Total <= 10,000. Downloading month in one go...
#> 
#> --- FINISHING... ---
#> Download complete! A total of 2,347 records were obtained.

user_obs
#> # A tibble: 2,347 × 27
#>        id observed_on  year month  week   day  hour created_at        updated_at
#>     <int> <chr>       <int> <int> <int> <int> <int> <chr>             <chr>     
#>  1 596411 2025-08-30   2025     8    35    30    12 2025-10-17T10:40… 2025-10-1…
#>  2 596410 2025-08-30   2025     8    35    30    12 2025-10-17T10:40… 2025-10-1…
#>  3 596409 2025-08-30   2025     8    35    30    12 2025-10-17T10:40… 2025-10-1…
#>  4 596408 2025-08-30   2025     8    35    30    12 2025-10-17T10:40… 2025-10-1…
#>  5 596407 2025-08-30   2025     8    35    30    12 2025-10-17T10:40… 2025-10-1…
#>  6 596406 2025-08-30   2025     8    35    30    12 2025-10-17T10:40… 2025-10-1…
#>  7 596405 2025-08-30   2025     8    35    30    12 2025-10-17T10:40… 2025-10-1…
#>  8 596404 2025-08-30   2025     8    35    30    12 2025-10-17T10:40… 2025-10-1…
#>  9 596403 2025-08-30   2025     8    35    30    12 2025-10-17T10:40… 2025-10-1…
#> 10 596402 2025-08-30   2025     8    35    30    12 2025-10-17T10:40… 2025-10-1…
#> # ℹ 2,337 more rows
#> # ℹ 18 more variables: latitude <dbl>, longitude <dbl>,
#> #   positional_accuracy <int>, geoprivacy <chr>, obscured <lgl>, uri <chr>,
#> #   url_picture <chr>, quality_grade <chr>, taxon_id <int>, taxon_name <chr>,
#> #   taxon_rank <chr>, taxon_min_ancestry <chr>, taxon_endemic <lgl>,
#> #   taxon_threatened <lgl>, taxon_introduced <lgl>, taxon_native <lgl>,
#> #   user_id <int>, user_login <chr>

- Place Queries

Place-related functions will be illustrated using the place Piscines del Fòrum

mnk_place_byname()

Initially, only an approximate place name is known, so a search is performed to retrieve the corresponding place ID. For this example, we start with the query “forum”, which returns a list of possible places. The project actually uses “Piscinas del Forum”, which does not match the initial assumption, so it is recommended to try several searches with different terms until the desired place is identified.

places <- mnk_place_byname("Forum")

places[,1:6]
#> # A tibble: 2 × 6
#>   place_id slug                      name     area display_name location_latitud
#>      <int> <chr>                     <chr>   <dbl> <chr>                   <dbl>
#> 1      253 piscinas-del-forum-fecdas Pisc… 4.28e-5 Piscinas de…             41.4
#> 2      257 platja-banys-del-forum    Plat… 1.28e-5 Platja Bany…             41.4

mnk_place_sf()

Returns the sf geometry for a place given its place ID. Once the place ID is known, the geometry can be retrieved with mnk_place_sf(). For Piscinas del Forum, the geometry is obtained with mnk_place_sf(253).

In the example the geometry is visualized with the leaflet package using the function’s default projection, WGS84 (EPSG:4326), the standard used by Google Maps. Note that the returned sf object already includes the place name as an attribute.

# 1. Downloading the geometry 

place_forum <- mnk_place_sf(253)

# 2. Drawing the map

 forum_sf <-leaflet(place_forum) %>%
                addProviderTiles("OpenStreetMap", group = "OSM") %>%
                addProviderTiles("Esri.WorldImagery", group = "Satélite") %>%
                addPolygons(
                  color = "#2c4fb8",
                  weight = 2,
                  opacity = 1,
                  fillOpacity = 0.4,
                  label = ~name, # information added from previous function
                  highlightOptions = highlightOptions(weight = 3, bringToFront = TRUE)
                ) %>%
                addLayersControl(baseGroups = c("Satélite", "OSM")) 
 forum_sf

mnk_places_obs()

Returns all observations recorded within a place within a selected year, and optionally within a specific month. The result is returned as a data frame without geometry. For Piscines del Fòrum, observations for february 2025 are obtained with mnk_places_obs(place_id, year = 2025, month =2).

Although the output has no geometry, it can be converted to an sf object with the helper function mnk_obs_sf(). The resulting points can then be mapped with the leaflet package.

obs_place <- mnk_place_obs(place_id = 253, year = 2025, month = 2, quiet = TRUE)

obs_place
#> # A tibble: 529 × 27
#>        id observed_on  year month  week   day  hour created_at        updated_at
#>     <int> <chr>       <int> <int> <int> <int> <int> <chr>             <chr>     
#>  1 427205 2025-02-19   2025     2     8    19    11 2025-03-19T12:44… 2025-03-1…
#>  2 427204 2025-02-19   2025     2     8    19    12 2025-03-19T12:44… 2025-03-1…
#>  3 427203 2025-02-19   2025     2     8    19    12 2025-03-19T12:44… 2026-01-2…
#>  4 427202 2025-02-19   2025     2     8    19    12 2025-03-19T12:44… 2025-03-1…
#>  5 427201 2025-02-19   2025     2     8    19    12 2025-03-19T12:44… 2025-03-1…
#>  6 427200 2025-02-19   2025     2     8    19    12 2025-03-19T12:44… 2025-03-1…
#>  7 427199 2025-02-19   2025     2     8    19    12 2025-03-19T12:44… 2025-03-1…
#>  8 427198 2025-02-19   2025     2     8    19    12 2025-03-19T12:44… 2025-03-1…
#>  9 427197 2025-02-19   2025     2     8    19    11 2025-03-19T12:44… 2025-03-1…
#> 10 427196 2025-02-19   2025     2     8    19    11 2025-03-19T12:44… 2025-03-1…
#> # ℹ 519 more rows
#> # ℹ 18 more variables: latitude <dbl>, longitude <dbl>,
#> #   positional_accuracy <int>, geoprivacy <chr>, obscured <lgl>, uri <chr>,
#> #   url_picture <chr>, quality_grade <chr>, taxon_id <int>, taxon_name <chr>,
#> #   taxon_rank <chr>, taxon_min_ancestry <chr>, taxon_endemic <lgl>,
#> #   taxon_threatened <lgl>, taxon_introduced <lgl>, taxon_native <lgl>,
#> #   user_id <int>, user_login <chr>

#Turning the dataframe into an sf object with the mnk_obs_sf() function

obs_sf <- mnk_obs_sf(obs_place,"id","taxon_name","observed_on", "url_picture", "uri", "user_login")

# Preparing the obtained data for later display in the marker popup

popup_final <-  paste0( "ID: <a href='", obs_sf$uri, 
                        "' target='_blank'>",obs_sf$id , "</a><br>",
                        "Specie:", obs_sf$taxon_name, "<br>",
                        "Observer: ", obs_sf$user_login,"<br>",
                        "Date:", obs_sf$observed_on, "<br>",
                        "<a href= '", obs_sf$url_picture, "' target='_blank'><img src='", 
                        obs_sf$url_picture,
                        "' style='margin-top:2px;border-radius:4px;'> </a> ")
#finally plotins

# Only the observations:

leaflet(obs_sf) %>%
                addProviderTiles("OpenStreetMap", group = "OSM") %>%
                addProviderTiles("Esri.WorldImagery", group = "Satélite") %>%
                addMarkers(lng = ~longitude,
                           lat = ~latitude,
                           popup = popup_final) %>%
                addLayersControl(baseGroups = c("Satélite", "OSM")) 

#  Observations plus place maping

 forum_sf %>%
  addMarkers(data = obs_sf, popup = ~popup_final)

- Observation Queries

mnk_obs_id()

Returns a single observation given its observation ID. This function is seldom used in practice, as observation IDs are not usually known beforehand.

obs_id <- mnk_obs_id(id = 553028)

obs_id
#> # A tibble: 1 × 166
#>   quality_grade time_observed_at       taxon_geoprivacy annotations uuid      id
#>   <chr>         <chr>                  <lgl>            <list>      <chr>  <int>
#> 1 research      2025-08-22T12:22:00+0… NA               <list [0]>  b106… 553028
#> # ℹ 160 more variables: cached_votes_total <int>,
#> #   identifications_most_agree <lgl>, species_guess <chr>,
#> #   identifications_most_disagree <lgl>, tags <list>,
#> #   positional_accuracy <int>, comments_count <int>, site_id <int>,
#> #   created_time_zone <chr>, license_code <chr>, observed_time_zone <chr>,
#> #   quality_metrics <list>, public_positional_accuracy <int>,
#> #   reviewed_by <list>, oauth_application_id <lgl>, flags <list>, …

mnk_obs()

This is the core function of the package. It is highly versatile and allows searches by project, user, place, date, week number, taxon, or geographic area. Two important notes apply to all mnk_obs() calls:

When the query is executed, a message reports the total number of observations found. If this number exceeds 10,000, only the first 10,000 are downloaded by default. To retrieve all records, set limit_download = FALSE.

To retrieve only validated records, use quality = "research".Previous examples have already covered searches by project, user and place, so here we focus on two cases not yet shown: a) search by taxon and b) search by bounding box.

  1. Taxon search. Observations for the genus Raja and for the species Raja brachyura during 2025 are obtained with taxon_name = "Raja" and taxon_name = "Raja brachyura" respectively.
#In this example don´t show messages in console (quiet= TRUE)

# Get observations for genus Raja in 2025 by user Xavier Salvador ( user_id=4)

obs <- mnk_obs(taxon_name = "Raja", year = 2025, user_id = 4, quiet = TRUE, 
               quality = "research")

obs # show full dataframe
#> # A tibble: 38 × 27
#>        id observed_on  year month  week   day  hour created_at        updated_at
#>     <int> <chr>       <int> <int> <int> <int> <int> <chr>             <chr>     
#>  1 414056 2025-01-24   2025     1     4    24     0 2025-01-29T10:04… 2025-01-2…
#>  2 427152 2025-02-19   2025     2     8    19    20 2025-03-19T10:19… 2025-03-1…
#>  3 427148 2025-02-19   2025     2     8    19    20 2025-03-19T10:19… 2025-03-2…
#>  4 427142 2025-02-19   2025     2     8    19    20 2025-03-19T10:19… 2025-03-1…
#>  5 427141 2025-02-19   2025     2     8    19    20 2025-03-19T10:19… 2025-03-1…
#>  6 427140 2025-02-19   2025     2     8    19    20 2025-03-19T10:19… 2025-03-1…
#>  7 422169 2025-02-27   2025     2     9    27    20 2025-02-28T11:17… 2025-02-2…
#>  8 420087 2025-02-21   2025     2     8    21    15 2025-02-21T15:14… 2025-02-2…
#>  9 429445 2025-03-27   2025     3    13    27    21 2025-03-30T18:52… 2025-03-3…
#> 10 429443 2025-03-26   2025     3    13    26    21 2025-03-30T18:52… 2025-03-3…
#> # ℹ 28 more rows
#> # ℹ 18 more variables: latitude <dbl>, longitude <dbl>,
#> #   positional_accuracy <int>, geoprivacy <chr>, obscured <lgl>, uri <chr>,
#> #   url_picture <chr>, quality_grade <chr>, taxon_id <int>, taxon_name <chr>,
#> #   taxon_rank <chr>, taxon_min_ancestry <chr>, taxon_endemic <lgl>,
#> #   taxon_threatened <lgl>, taxon_introduced <lgl>, taxon_native <lgl>,
#> #   user_id <int>, user_login <chr>

# Get observations for species Raja brachyura in 2023 by user Xasalva (user_id= 4)

obs_brachyura <- mnk_obs(taxon_name = "Raja brachyura", year = 2023, user_id = 4, 
                         quiet = TRUE, quality = "research")

obs_brachyura[, c(1,2, 10, 11, 16, 27)]  # show selected columns
#> # A tibble: 8 × 6
#>       id observed_on latitude longitude url_picture                   user_login
#>    <int> <chr>          <dbl>     <dbl> <chr>                         <chr>     
#> 1 107825 2023-01-20      41.9      3.21 https://minka-sdg.org/attach… xasalva   
#> 2 110045 2023-02-17      41.7      2.94 https://minka-sdg.org/attach… xasalva   
#> 3 108780 2023-02-01      41.7      2.94 https://minka-sdg.org/attach… xasalva   
#> 4 207525 2023-11-25      41.9      3.21 https://minka-sdg.org/attach… xasalva   
#> 5 211384 2023-12-21      41.7      2.94 https://minka-sdg.org/attach… xasalva   
#> 6 211381 2023-12-20      41.7      2.94 https://minka-sdg.org/attach… xasalva   
#> 7 210709 2023-12-16      41.7      2.94 https://minka-sdg.org/attach… xasalva   
#> 8 210705 2023-12-16      41.7      2.94 https://minka-sdg.org/attach… xasalva

Images from observations can be visualized and printed using the magick package and the observation’s url_picture as follow.

# Get observations for specie Raja undulata in 2022 by user Xasalva (user_id= 4)

obs_undulata <- mnk_obs(taxon_name = "Raja undulata", year = 2022, user_id = 4, 
                         quiet = TRUE, quality = "research")

# select 3rd observation and get picture URL (url_picture)

url_pic_undulata <- as.character(obs_undulata$url_picture[3])

# read image directly from URL

img_undulata <- image_read(url_pic_undulata )

  # add copyright caption at top
  img_undulata <- image_annotate(
    img_undulata,
    "© Xavier Salvador",
    size = 10, 
    gravity = "northwest", 
    color = "white", 
  ) 

# display image in the document

img_undulata

Raja undulata observed by Xavier Salvador

  1. Bounding-box search. Observations within a rectangular area are retrieved by supplying the coordinates of the southwest and northeast corners. For example, to search around Platja de Sant Sebastià in Barcelona, define the bounding box and pass it to mnk_obs().
# read shapefile of  study area using relative path 

shp <- "../inst/extdata/espigo_w.shp"

espigo <- sf::st_read(shp, quiet = TRUE)

# ensure WGS84 for leaflet (standard for web maps)

bounds <- st_transform(espigo, 4326)

Similarly, the area can also be defined by a vector from two points, Pmax and Pmin, where the area is defined as:

bounds <- c(ymax, xmax, ymin, xmin)

Applied to the Espigo del W example, this would be:

bounds <-c(41.371239, 2.193971, 41.368182, 2.189147)

The two points, pmax and pmin, will be displayed together with the study area of the Espigo del W in leaflet to help understand how the bounds parameter works.

#Pmin
xmin <- 2.189147
ymin <- 41.368182

#Pmax
xmax <- 2.193971
ymax <- 41.371239

pts <- data.frame(
  id = c("Pmin", "Pmax"),
  lng = c(xmin, xmax),
  lat = c(ymin, ymax)
)

# create interactive map for visualising the study area and the two points

bound <- leaflet() %>%
                addProviderTiles("Esri.WorldImagery", group = "Satélite") %>%
                addProviderTiles("OpenStreetMap", group = "OSM") %>%
                addCircleMarkers(             
                         data = pts,
                          radius = 20,
                          color = "cyan", 
                          weight = 2, 
                          fillOpacity = 0.7,
                          label = ~id,          
                          popup = ~paste0("<b>", id, "</b><br>",
                                          "lon: ", round(lng, 6), "<br>",
                                          "lat: ", round(lat, 6))) %>%
                addPolygons(data =    bounds,
                          color = "#2c4fb8",
                          weight = 2,
                          opacity = 1,
                          fillOpacity = 0.4,
                          label = "Bound Espigo W",
                          popup = "Bound Espigo W",
                          highlightOptions = highlightOptions(weight = 3, 
                                                              bringToFront =TRUE)) %>%
                addLayersControl(baseGroups = c("Satélite", "OSM"))
#> Assuming "lng" and "lat" are longitude and latitude, respectively

bound  # display map in document

#Obtaining the observations within the study area

obs_torpedo_bounds <- mnk_obs(taxon_name = "Torpedo", year = 2024,
                      bounds = bounds , quality = "research", quiet = TRUE)

obs_torpedo_bounds
#> # A tibble: 7 × 27
#>       id observed_on  year month  week   day  hour created_at         updated_at
#>    <int> <chr>       <int> <int> <int> <int> <int> <chr>              <chr>     
#> 1 244605 2024-03-22   2024     3    12    22    20 2024-03-24T18:34:… 2024-03-3…
#> 2 244604 2024-03-22   2024     3    12    22    20 2024-03-24T18:34:… 2024-03-3…
#> 3 244594 2024-03-22   2024     3    12    22    20 2024-03-24T18:34:… 2024-03-3…
#> 4 304290 2024-07-20   2024     7    29    20    18 2024-07-20T18:36:… 2024-07-2…
#> 5 301613 2024-07-16   2024     7    29    16    22 2024-07-17T09:30:… 2024-07-2…
#> 6 315533 2024-08-01   2024     8    31     1     9 2024-08-03T09:08:… 2024-08-0…
#> 7 315527 2024-08-01   2024     8    31     1     9 2024-08-03T09:08:… 2024-08-0…
#> # ℹ 18 more variables: latitude <dbl>, longitude <dbl>,
#> #   positional_accuracy <int>, geoprivacy <chr>, obscured <lgl>, uri <chr>,
#> #   url_picture <chr>, quality_grade <chr>, taxon_id <int>, taxon_name <chr>,
#> #   taxon_rank <chr>, taxon_min_ancestry <chr>, taxon_endemic <lgl>,
#> #   taxon_threatened <lgl>, taxon_introduced <lgl>, taxon_native <lgl>,
#> #   user_id <int>, user_login <chr>

#Turning the dataframe into an sf object with the mnk_obs_sf() function and 
#selecting some fields ("id","taxon_name","observed_on",.....) from the original
#data frame for to show later in leaflet popups.

obs_bounds_sf <- mnk_obs_sf(obs_torpedo_bounds,"id","taxon_name","observed_on", "url_picture", "uri", "user_login")

# Preparing the obtained data for later display in the marker popup

popup_final_torpedo <-  paste0( "ID: <a href='", obs_bounds_sf$uri,
                        "' target='_blank'>",obs_bounds_sf$id , "</a><br>",
                        "Specie:", obs_bounds_sf$taxon_name, "<br>",
                        "Observer: ", obs_bounds_sf$user_login,"<br>",
                        "Date:", obs_bounds_sf$observed_on, "<br>",
                        "<a href= '", obs_bounds_sf$url_picture, 
                        "' target='_blank'><img src='", 
                        obs_bounds_sf$url_picture, 
                        "' style='margin-top:2px;border-radius:4px;'> </a> ")

#final plot

bound %>%
  addMarkers(data = obs_bounds_sf, popup = ~popup_final_torpedo)

mnk_obs_byday()

Works like mnk_obs() but uses an explicit date interval instead of year or month parameters. The interval is defined by:

d1: start date in ‘yyyy-mm-dd’ format d2: end date in ‘yyyy-mm-dd’ format

#Retrieves observations for specie Raja undulata by user Xasalva (user_id= 4) the observations between 01-01-2024 and  25-05-2024.

obs_undulata_2024 <- mnk_obs_byday(taxon_name = "Raja undulata", d1 = "2024-01-01", 
                             d2= "2024-05-25", quiet = TRUE, quality = "research")

obs_undulata_2024
#> # A tibble: 10 × 29
#>        id observed_on  year month  week   day  hour created_at        updated_at
#>     <int> <chr>       <int> <int> <int> <int> <int> <chr>             <chr>     
#>  1 457257 2024-02-17   2024     2     7    17    10 2025-05-20T10:44… 2025-05-2…
#>  2 271165 2024-05-12   2024     5    19    12    11 2024-05-22T20:31… 2025-03-1…
#>  3 270617 2024-05-11   2024     5    19    11    22 2024-05-21T22:52… 2024-05-2…
#>  4 264653 2024-05-08   2024     5    19     8    21 2024-05-09T14:08… 2025-03-1…
#>  5 253086 2024-04-15   2024     4    16    15    19 2024-04-16T18:19… 2024-04-1…
#>  6 232230 2024-02-18   2024     2     7    18    10 2024-02-24T10:38… 2025-03-1…
#>  7 230658 2024-02-18   2024     2     7    18    11 2024-02-19T19:25… 2024-02-2…
#>  8 230538 2024-02-18   2024     2     7    18    10 2024-02-19T14:36… 2024-02-2…
#>  9 229797 2024-02-18   2024     2     7    18    10 2024-02-18T21:41… 2024-02-1…
#> 10 222442 2024-02-03   2024     2     5     3    11 2024-02-06T18:34… 2025-03-1…
#> # ℹ 20 more variables: latitude <dbl>, longitude <dbl>,
#> #   positional_accuracy <int>, geoprivacy <lgl>, obscured <lgl>, uri <chr>,
#> #   photo_url_square <chr>, photo_url_medium <chr>, quality_grade <chr>,
#> #   species_guess <chr>, taxon_id <int>, taxon_name <chr>, taxon_rank <chr>,
#> #   taxon_min_ancestry <chr>, taxon_endemic <lgl>, taxon_threatened <lgl>,
#> #   taxon_introduced <lgl>, taxon_native <lgl>, user_id <int>, user_login <chr>

- Auxiliary functions

The examples below show how the helper functions work. The first two use the Torpedo torpedo observations at the Espigó Hotel W from the previous sections. The last two use separate, specific examples.

mnk_obs_sf()

This function converts an observations data frame into a spatial layer in sf format. The function takes the latitude/longitude columns returned by mnk_obs or by any other function in the rminka package that returns an observations data frame — such as mnk_proj_obs or mnk_user_obs — and converts them into an sf POINT layer. By default the geometry is returned in CRS EPSG:4326 (WGS84), but you can specify any CRS you need via the crs argument. Its basic usage has already been shown briefly in previous sections. Here we develop a detailed example using the Torpedo torpedo observations at the espigó Hotel W.

obs_utm <- mnk_obs_sf(obs_torpedo_bounds, id, taxon_name,
                      observed_on, user_login, quality_grade, url_picture,
                      uri, crs = 25831)

# The input obs_torpedo_bounds is a data frame with the full set of observation records. The
#function keeps only the selected attribute columns (id, taxon_name, observed_on, user_login,
#quality_grade, url_picture, uri); these are preserved in the output sf object together with
#the generated geometry in obs_utm.

#The crs = 25831 parameter overrides the default EPSG:4326. 
#Here we request the output directly in EPSG:25831 – ETRS89 / UTM zone 31N, 
#which is the official projected CRS for Catalonia. 
#This is useful for distance calculations and for matching local cartography


# Leaflet natively works in Web Mercator (EPSG:3857). To visualize data in UTM, 
#you must change the map's base CRS:

crs25831 <- leafletCRS(
  crsClass = "L.Proj.CRS",
  code = "EPSG:25831",
  proj4def = "+proj=utm +zone=31 +ellps=GRS80 +units=m +no_defs",
  resolutions = c(8192,4096,2048,1024,512,256,128,64,32,16,8,4,2,1,0.5),
  origin = c(0, 9000000)
)

# Because the map is no longer in 3857, you cannot use standard OSM tiles. 
#You need a WMS that serves imagery in the same CRS. 
#Here we use the Institut Cartogràfic i Geològic de Catalunya (ICGC) service.

leaflet(options = leafletOptions(crs25831, minZoom = 0, maxZoom = 14)) %>%
  
      addWMSTiles(
        "https://geoserveis.icgc.cat/servei/catalunya/mapa-base/wms",
        layers = "orto",
        options = WMSTileOptions(format = "image/png", 
                                 transparent = TRUE, version = "1.3.0")) %>%
    
      addCircleMarkers(
        data = obs_utm, 
        radius = 6,
        color = "red",
        fillOpacity = 0.9,
        popup =  paste0( "ID: <a href='", obs_utm$uri,
                            "' target='_blank'>",obs_utm$id , "</a><br>",
                            "Specie:", obs_utm$taxon_name, "<br>",
                            "Observer: ", obs_utm$user_login,"<br>",
                            "Quality:", obs_utm$quality_grade,"<br>",
                            "Date:", obs_utm$observed_on, "<br>",
                            "<a href= '", obs_utm$url_picture, 
                            "' target='_blank'><img src='", 
                            obs_utm$url_picture, 
                            "' style='margin-top:2px;border-radius:4px;'> </a> "))

export_mnk_qgis()

This function writes one or more sf objects to a GeoPackage (.gpkg), creating one layer per object. It is designed for a smooth GIS workflow ( specialy QGIS): it ensures valid geometries, drops Z/M dimensions, removes list-columns that GDAL cannot write, and transforms features to the target CRS.

We will apply it to the two examples from the previous section.

export_mnk_qgis(observations =obs_utm ,file="shape")

#In this first example we export the observations layer from the previous step (obs_utm)
#as a single point layer named "observations". The output file is named "shape" — the .gpkg
#extension is added automatically.Because no path is specified, the file shape.gpkg is saved
#in the current R working directory of the project.

export_mnk_qgis(zone = places_forum , observations = obs_sf,
                file = "C:/examples/forum.gpkg")

#In the second example export several layers at once. Here we write two layers 
#into the same GeoPackage:

# *  places_forum — a polygon defining the Forum pools area, saved as layer "zone"

# * obs_sf — point observations from the Forum pools, saved as layer "observations"
#Both layers are stored in forum.gpkg inside the "examples" folder.

get_wrm_tax()

This function downloads taxonomic information from the World Register of Marine Species (WoRMS) for a given scientific name. Two examples are provided to illustrate its practical use, particularly when working with tibbles returned by observation functions. In these cases, the goal is to obtain the observations tibble enriched with the complete taxonomy for each recorded species, including its marine,its freshwater, brackish, or extinct status.

#First example – single species

get_wrm_tax("Diplodus sargus")
#> # A tibble: 1 × 14
#>   valid_AphiaID valid_name      rank    kingdom  phylum class order family genus
#>           <int> <chr>           <chr>   <chr>    <chr>  <chr> <chr> <chr>  <chr>
#> 1        127053 Diplodus sargus Species Animalia Chord… Tele… Eupe… Spari… Dipl…
#> # ℹ 5 more variables: isMarine <lgl>, isBrackish <lgl>, isFreshwater <lgl>,
#> #   isTerrestrial <lgl>, isExtinct <lgl>

#The taxonomy and metadata are retrieved for a single species, Diplodus sargus.

#Second example – community taxonomy
#The taxonomy is retrieved for all species observed in the previous `mnk_place_ob` 
#call — all species recorded in the Forum pools area during February 2025 (obs_place).

taxon_unic <- obs_place %>%
  filter(!is.na(taxon_name)) %>%
  distinct(taxon_name) %>%
  pull(taxon_name)

taxonomy_df <- map_dfr(
  setNames(taxon_unic, taxon_unic),  
  ~ get_wrm_tax(.x),
  .id = "taxon_name"
)
#> No taxon found for the scientific name: 'Life'.
#> No taxon found for the scientific name: 'Ichthyaetus audouinii'.

#The complete observations tibble for the Forum pools can be rebuilt with taxonomy #information by performing a left_join() between obs_place and the taxonomy dataframe.

obs_place_tax <- obs_place %>%
  left_join(taxonomy_df, by = "taxon_name")

shrt_name()

This function creates a standardized abbreviation from a scientific name. In the following example it is used to generate a bar plot of observation frequencies for the species in the “obs_place” tibble, with the abbreviated names making the chart easier to read.

#Create standardized abbreviations for scientific names

obs_place <- obs_place %>%
  mutate(
    shrt_tax_name = map_chr(taxon_name, ~ {
      if (is.na(.x) || .x == "") NA_character_ else shrt_name(.x)
    })
  )
  
#Count observations per abbreviated species and keep top 10

freq_top10 <- obs_place %>%
  filter(!is.na(shrt_tax_name)) %>%          # remove missing names
  count(shrt_tax_name, name = "n_obs") %>%    # count occurrences
  slice_max(n_obs, n = 10) %>%                # keep the 10 most frequent
  arrange(n_obs)   

#Plot horizontal bar chart of observation frequency

ggplot(freq_top10, aes(x = n_obs, y = reorder(shrt_tax_name, n_obs))) +
        geom_col(fill = "steelblue") +
        geom_text(aes(label = n_obs), hjust = -0.2, size = 2) +  # add n as label
        scale_x_continuous(expand = expansion(mult = c(0, 0.15))) +  # make room for labels
        labs(
              x = "Number of observations",
              y = "Species (abbreviated)",
              title = "Observation frequency",
              subtitle = " Forum February 2025"
            ) +
        theme_minimal(base_size = 12)