historicalMAP

Build interactive maps in HTML format to assess changes in species distribution through time

⛶  Fullscreen ↓  Download

Projects

Living Herbarium
GLAMhack 2022 Prototype

(2+6) Connecting the Herbarium to Wikidata around playful experiences and storytelling

This was presented as Challenge 6 at GLAMhack 2022.

Challenge

Develop a plugin to compare the distribution of (mainly historical) observations from natural history collections with approximate (mainly contemporary) data from Swiss biodiversity portals.

Problem

Swiss biodiversity portals like InfoSpecies publish most of their georeferenced data as 5km square grids in GBIF (see for example here). In order to compare these contemporary data with georeferenced historical data from natural history collections, the latter must be ‘normalised’ to the square grid system used by Swiss biodiversity portals.

Benefit

Comparisons between historical and contemporary observational data allow the assessment of changes in species distributions over time. In the context of the current biodiversity crisis, facilitating such comparisons would tremendously facilitate the identification of threats to the Swiss indigenous flora, fauna and fungi and, by implication, the actions needed to preserve them.

Case study

The vascular plant collections of the United Herbaria Z+ZT of the University (Z) and ETH Zurich (ZT) encompass about 2.5 millions herbarium specimens (see this short movie for a brief presentation of the institution). Between 2019 and 2021, all (ca. 90,000) vouchers from the canton of Valais have been imaged and publicly released in GBIF. More than half (ca. 55’000) of the specimens were transcribed and georeferenced for a total of nearly 1,200 taxa occurring in Valais. These historical data could now be compared with ongoing vegetation surveys from the FloraVS project, which are being published on GBIF via InfoFlora.

Basic approach

  1. Specify taxon in search field
  2. Extract historical data of the given taxon from the United Herbaria Z+ZT
  3. Extract contemporary data of the given taxon from InfoFlora
  4. Normalise all data to 5km square grids
  5. Create distribution map for the time period before and after 1980

Jump start

I suggest to use the open-source JavaScript library called Leaflet to solve this challenge. I tried it using R (instead of Java, which I do not know how to program) and herewith briefly summarise what I have already been able to achieve with a few screenshots.

Trial dataset

Distributional data of Adonis aestivalis L. from canton Valais

Link to contemporary data from InfoFlora using following filters:

  • Scientific name = Adonis aestivalis L.
  • Basis of record = Human observation (to avoid including data from herbaria or literature)
  • Administrative areas = Valais - CHE.23_1
  • Dataset = Swiss National Databank of Vascular Plants

 Title

Link to (mainly) historical data from the United Herbaria Zurich Z+ZT using following filters:

  • Scientific name = Adonis aestivalis L.
  • Administrative areas = Valais - CHE.23_1
  • Dataset = United Herbaria of the University and ETH Zurich

 Title

R code

#############################################################################
## Script to assess changes in species distribution using interactive maps ##
#############################################################################

## Code by Alessia Guggisberg
## Created: 18.10.2022
## Last edited: 21.10.2022 by Alessia Guggisberg

## helpful links
## https://rpubs.com/huanfaChen/grid_from_polygon
## https://rstudio.github.io/leaflet/
## https://cran.r-project.org/web/packages/leaflet/leaflet.pdf
## https://leaflet-extras.github.io/leaflet-providers/preview/
## https://leanpub.com/leaflet-tips-and-tricks/read
## https://api3.geo.admin.ch/services/sdiservices.html
## https://leaflet-tilelayer-swiss.karavia.ch/layers.html#
## https://mikejohnson51.github.io/leaflet-intro/hospitals.html # to customised legends


## Initialize libraries and set working directory ######################################################


library(rgdal)
library(leaflet) # to create interactive maps
library(leaflegend)
library(htmlwidgets) # to save interactive maps as HTML

setwd("/Users/guggisberg/Documents/Alessia/Professional/Herbarium/Projekte/GLAMhack2022/Rscript/")

## Import historical Z+ZT and contemporary Infoflora data from GBIF #####################################


INFOFLORA <-read.table("Adonis_aestivalis_0101032-220831081235567/occurrence.txt", header=T, sep = "\t", quote = "\"'")

## Build raster #########################################################################################

grid_size <- 0.055
r <- raster(xmn=5.956601, ymn=45.81914, xmx=10.49347, ymx=47.80988, res=grid_size)
r[] <- 0
crs(r) <- CRS("+init=epsg:4326") # add CRS to raster
rZZT <- rasterize(ZZT[,c("decimalLongitude","decimalLatitude")], r, fun = "count")
rINFOFLORA <- rasterize(INFOFLORA[,c("decimalLongitude","decimalLatitude")], r, fun = "count")

# Retain only grids that contain a count and 'normalise' to centroids of 5km-by-5km grids ###############

rZZT[rZZT < 1] <- NA
rINFOFLORA[rINFOFLORA < 1] <- NA
cellsZZT <- which(is.na(rZZT@data@values)==F)
cellsINFOFLORA <- which(is.na(rINFOFLORA@data@values)==F)
centroidsZZT <- data.frame(xyFromCell(rZZT, cellsZZT))
centroidsINFOFLORA <- data.frame(xyFromCell(rINFOFLORA, cellsINFOFLORA))

# URLs to various Swiss maps ############################################################################

url1 <- 'https://wmts.geo.admin.ch/1.0.0/ch.swisstopo.pixelkarte-farbe/default/current/3857/{z}/{x}/{y}.jpeg' # swisstopo colour
url2 <- 'https://wmts.geo.admin.ch/1.0.0/ch.swisstopo.pixelkarte-grau/default/current/3857/{z}/{x}/{y}.jpeg' # swisstopo greyscale#
url3 <- 'https://wmts.geo.admin.ch/1.0.0/ch.swisstopo.swissimage/default/current/3857/{z}/{x}/{y}.jpeg' # aerial view
url4 <- 'https://wmts.geo.admin.ch/1.0.0/ch.swisstopo.swisstlm3d-karte-grau.3d/default/current/3857/{z}/{x}/{y}.jpeg' # topographic greyscale map


# Create leaflet map using centroids #####################################################################

addLegendCustom <- function(map, color, labels, sizes, opacity = 1, stroke, title = "Observations per grid", position = "bottomright"){ # setup customised legend to plot circles (instead of squares)
  colorAdditions <- paste0(color, "; border-radius: 50%; width:", sizes, "px; height:", sizes, "px")
  labelAdditions <- paste0("<div style='display: inline-block;height: ", 
                           sizes, "px;margin-top: 4px;line-height: ", sizes, "px;'>", 
                           labels, "</div>")
  return(addLegend(map, 
                   colors = colorAdditions, 
                   labels = labelAdditions, 
                   opacity = opacity, 
                   title = title,
                   position = position))
}

m <- leaflet(centroidsZZT) %>% 
  addTiles(urlTemplate = url1, group ="Swisstopo colour")  %>%
  addTiles(urlTemplate = url2, group ="Swisstopo greyscale") %>%
  addTiles(urlTemplate = url3, group ="Aerial view") %>%
  addTiles(urlTemplate = url4, group ="Topographic view") %>%
  addCircleMarkers(~ZZT$decimalLongitude, ~ZZT$decimalLatitude, fillOpacity = 1, col = c("red"), weight = 2, radius=5, group = "ZZT - precise") %>%
  addCircleMarkers(~centroidsZZT$x, ~centroidsZZT$y, fillOpacity = 0.5, col = c("red"), weight = 2, radius=5, group = "ZZT - presence in grid") %>%
  addCircleMarkers(~centroidsZZT$x, ~centroidsZZT$y, fillOpacity = 0.5, col = c("red"), weight = 2, radius=levels(cut(rZZT@data@values, c(1,5,10,20,100,1000), labels=c(1,3,5,10,20))), group = "ZZT - observations per grid") %>% #plot data points proportionate to number of counts per grid
  addCircleMarkers(~centroidsINFOFLORA$x, ~centroidsINFOFLORA$y, fillOpacity = 0.5, col = c("blue"), weight = 2, radius=5, group = "InfoFlora - presence in grid") %>%
  addCircleMarkers(~centroidsINFOFLORA$x, ~centroidsINFOFLORA$y, fillOpacity = 0.5, col = c("blue"), weight = 2, radius=levels(cut(rINFOFLORA@data@values, c(1,5,10,20,100,1000), labels=c(1,3,5,10,20))), group = "InfoFlora - observations per grid") %>%#plot data points proportionate to number of counts per grid
  addLayersControl(baseGroups = c("Swisstopo colour", "Swisstopo greyscale", "Aerial view", "Topographic view"), overlayGroups = c("ZZT - precise", "ZZT - presence in grid", "ZZT - observations per grid", "InfoFlora - presence in grid", "InfoFlora - observations per grid"), options = layersControlOptions(collapsed = FALSE)) %>%
  addLegend(colors = c("red","blue"), position = "bottomright", labels = c("ZZT", "InfoFlora"), opacity = 1, title = c("Dataset")) %>%
  addLegendCustom(color = "grey", labels = c("1-5","6-10","11-20","21-100","101-1000"), sizes = c(3,5,10,20,40), stroke = FALSE)
m

# Save the leaflet maps as HTML objects ##################################################################
saveWidget(m, file="historicalMAP_example_Adonis_aestivalis.html")

Here's what you get:

a) Mapping of (precise) herbarium locations  Title

b) Mapping of grids where the species is/was present (red, herbarium data; blue, InfoFlora data)  Title

c) Mapping of grids where the species is/was present (red, herbarium data; blue, InfoFlora data); the sizes of the circles are proportionate to the number of observations recorded per grid (see legend at the bottom right)  Title

To be implemented or solved

  • The InfoFlora database entails observations dating back to the 1970s and the United Herbaria Zurich Z+ZT also possess vouchers from this millennium. Accordingly, one should implement a time slider, to facilitate temporal comparisons and accommodate data from other databases (e.g. iNaturalist).
  • To better assess the reasons for putative distributional changes through time (e.g. habitat loss, urbanisation), one should integrate the historical Dufour and Siegfried swisstopo maps. Further maps (e.g. land use, vegetation type, road network) are, of course, welcome! Having the opportunity to visualise two maps (from two different time periods) side-by-side might also be useful, but maybe it would be more elegant to establish a hypothetical distribution based upon all the observations and let the internet users choose which data they want to plot against it (e.g. only data from a given dataset or a given time period).
  • In order to identify putative wrong determinations, one should be able to access any images (if available) pertaining to the mapped datapoints (see examples from the United Herbaria Zurich Z+ZT or iNaturalist). Generally, implementing dynamic popups with important information pertaining to the datapoints would be a nice feature!
  • Logically, the code should be setup in such a way that data are being automatically retrieved from GBIF, so that one always constructs the most current map.
  • I herewith compared data of only one species (Adonis aestivalis L.), but ideally internet users should be able to customise their map based on following criteria: plant species, administrative areas (cantons), data sources (national database, herbaria, citizen science platforms), etc.
  • Herbaria should be able to integrate the code into their websites.
  • Export functions (with copyrights) are needed.

This content is a preview from an external site.
 

Event finished

We met with the expert, formed a team, and brainstormed possible designs. We are super motivated to build on this data.

04.11.2022 16:14 ~ loleg

Project

Event started

Edited (version 186)

03.11.2022 13:45 ~ jonaslendenmann

Edited (version 183)

03.11.2022 08:37 ~ loleg

Edited (version 179)

26.10.2022 06:18 ~ AlessiaGuggisberg

Edited (version 177)

26.10.2022 06:14 ~ AlessiaGuggisberg

Edited (version 175)

26.10.2022 05:31 ~ AlessiaGuggisberg

Edited (version 173)

26.10.2022 05:30 ~ AlessiaGuggisberg

Edited (version 171)

26.10.2022 05:30 ~ AlessiaGuggisberg

Edited (version 169)

26.10.2022 05:29 ~ AlessiaGuggisberg

Edited (version 167)

26.10.2022 05:29 ~ AlessiaGuggisberg

Edited (version 165)

26.10.2022 05:28 ~ AlessiaGuggisberg

Edited (version 163)

26.10.2022 05:27 ~ AlessiaGuggisberg

Edited (version 161)

26.10.2022 05:26 ~ AlessiaGuggisberg

Edited (version 159)

26.10.2022 05:26 ~ AlessiaGuggisberg

Edited (version 157)

26.10.2022 05:25 ~ AlessiaGuggisberg

Edited (version 155)

26.10.2022 05:25 ~ AlessiaGuggisberg

Edited (version 153)

26.10.2022 05:25 ~ AlessiaGuggisberg

Edited (version 151)

26.10.2022 05:23 ~ AlessiaGuggisberg

Edited (version 149)

26.10.2022 05:22 ~ AlessiaGuggisberg

Edited (version 147)

26.10.2022 05:22 ~ AlessiaGuggisberg

Edited (version 145)

26.10.2022 05:20 ~ AlessiaGuggisberg

Edited (version 143)

26.10.2022 05:20 ~ AlessiaGuggisberg

Edited (version 141)

26.10.2022 05:19 ~ AlessiaGuggisberg

Edited (version 139)

26.10.2022 05:18 ~ AlessiaGuggisberg

Edited (version 137)

21.10.2022 09:02 ~ AlessiaGuggisberg

Edited (version 135)

21.10.2022 09:00 ~ AlessiaGuggisberg

Edited (version 133)

21.10.2022 08:56 ~ AlessiaGuggisberg

Edited (version 131)

21.10.2022 08:47 ~ AlessiaGuggisberg

Edited (version 129)

21.10.2022 08:41 ~ AlessiaGuggisberg

Edited (version 127)

21.10.2022 05:12 ~ AlessiaGuggisberg

Edited (version 125)

21.10.2022 04:54 ~ AlessiaGuggisberg

Edited (version 123)

20.10.2022 05:34 ~ AlessiaGuggisberg

Edited (version 120)

20.10.2022 05:33 ~ AlessiaGuggisberg

Edited (version 118)

20.10.2022 05:32 ~ AlessiaGuggisberg

Edited (version 116)

20.10.2022 05:30 ~ AlessiaGuggisberg

Edited (version 114)

20.10.2022 05:29 ~ AlessiaGuggisberg

Edited (version 112)

19.10.2022 11:45 ~ AlessiaGuggisberg

Edited (version 110)

19.10.2022 11:42 ~ AlessiaGuggisberg

Edited (version 108)

19.10.2022 11:33 ~ AlessiaGuggisberg

Edited (version 106)

19.10.2022 11:26 ~ AlessiaGuggisberg

Edited (version 104)

19.10.2022 11:06 ~ AlessiaGuggisberg

Edited (version 102)

19.10.2022 11:06 ~ AlessiaGuggisberg

Edited (version 100)

19.10.2022 11:04 ~ AlessiaGuggisberg

Edited (version 98)

19.10.2022 10:00 ~ AlessiaGuggisberg

Edited (version 96)

19.10.2022 10:00 ~ AlessiaGuggisberg

Edited (version 91)

19.10.2022 09:55 ~ AlessiaGuggisberg

Edited (version 89)

19.10.2022 09:52 ~ AlessiaGuggisberg

Edited (version 87)

19.10.2022 09:43 ~ AlessiaGuggisberg

Edited (version 85)

19.10.2022 09:39 ~ AlessiaGuggisberg

Edited (version 83)

19.10.2022 09:36 ~ AlessiaGuggisberg

Challenge

 
Contributed 1 year ago by AlessiaGuggisberg for GLAMhack 2022
All attendees, sponsors, partners, volunteers and staff at our hackathon are required to agree with the Hack Code of Conduct. Organisers will enforce this code throughout the event. We expect cooperation from all participants to ensure a safe environment for everybody.

Creative Commons LicenceThe contents of this website, unless otherwise stated, are licensed under a Creative Commons Attribution 4.0 International License.