Craig F. Barrett
April 07, 2021
Department of Biology
West Virginia University
Morgantown, WV USA 26506

# install packages if needed
# install.packages(c("rgbif", "ggplot2", "tidyverse", "gganimate", "ggthemes", "sf", "tools", "maps", "rnaturalearth", "rnaturalearthdata"))

# load libraries
library(shiny)
library(ggplot2)
library(tidyverse)
library(gganimate)
library(ggthemes)
library(sf)
library("tools")
library(maps)
library("rnaturalearth")
library("rnaturalearthdata")
#read in data, convert to data.frame, set "awn" as a factor
setwd(choose.dir())
awn <- read.csv("awndatanew.csv")
awn <- data.frame(awn)
# remove duplicate records based on latitude
awn <- awn %>% distinct(decimalLatitude, .keep_all = TRUE)
awns <- as.factor(awn$awn)
awn <- data.frame(awn)
world <- ne_countries(scale = "medium", returnclass = "sf")
## class(world)
states <- st_as_sf(map("state", plot = FALSE, fill = TRUE))
## head(states)
states <- cbind(states, st_coordinates(st_centroid(states)))
st_centroid assumes attributes are constant over geometries of xst_centroid does not give correct centroids for longitude/latitude data
#Generate the map and coordinate boundaries
usa <- ggplot(data = world) +
    #mutate(year = as.numeric(awn$year)) + 
    geom_sf(data = states, fill = NA) + 
    coord_sf(xlim = c(-96, -70), ylim = c(28, 45), expand = FALSE)
# map the occurrence data
JSmap <- usa +
  geom_point(data = awn, shape = 21, colour="black", aes(x=decimalLongitude, y=decimalLatitude, fill=awns), size=6, stroke=1) +
  #transition_states(year, transition_length = 1, state_length = 1) + 
  scale_fill_manual(values=c("red", "blue")) +
  transition_time(year) +
  labs(title = 'Digitized herbarium records of Microstegium vimineum', subtitle = 'Year: {round(frame_time,0)}') +
  shadow_mark()
#animate!
JSmap

Frame 1 (1%)
Frame 2 (2%)
Frame 3 (3%)
Frame 4 (4%)
Frame 5 (5%)
Frame 6 (6%)
Frame 7 (7%)
Frame 8 (8%)
Frame 9 (9%)
Frame 10 (10%)
Frame 11 (11%)
Frame 12 (12%)
Frame 13 (13%)
Frame 14 (14%)
Frame 15 (15%)
Frame 16 (16%)
Frame 17 (17%)
Frame 18 (18%)
Frame 19 (19%)
Frame 20 (20%)
Frame 21 (21%)
Frame 22 (22%)
Frame 23 (23%)
Frame 24 (24%)
Frame 25 (25%)
Frame 26 (26%)
Frame 27 (27%)
Frame 28 (28%)
Frame 29 (29%)
Frame 30 (30%)
Frame 31 (31%)
Frame 32 (32%)
Frame 33 (33%)
Frame 34 (34%)
Frame 35 (35%)
Frame 36 (36%)
Frame 37 (37%)
Frame 38 (38%)
Frame 39 (39%)
Frame 40 (40%)
Frame 41 (41%)
Frame 42 (42%)
Frame 43 (43%)
Frame 44 (44%)
Frame 45 (45%)
Frame 46 (46%)
Frame 47 (47%)
Frame 48 (48%)
Frame 49 (49%)
Frame 50 (50%)
Frame 51 (51%)
Frame 52 (52%)
Frame 53 (53%)
Frame 54 (54%)
Frame 55 (55%)
Frame 56 (56%)
Frame 57 (57%)
Frame 58 (58%)
Frame 59 (59%)
Frame 60 (60%)
Frame 61 (61%)
Frame 62 (62%)
Frame 63 (63%)
Frame 64 (64%)
Frame 65 (65%)
Frame 66 (66%)
Frame 67 (67%)
Frame 68 (68%)
Frame 69 (69%)
Frame 70 (70%)
Frame 71 (71%)
Frame 72 (72%)
Frame 73 (73%)
Frame 74 (74%)
Frame 75 (75%)
Frame 76 (76%)
Frame 77 (77%)
Frame 78 (78%)
Frame 79 (79%)
Frame 80 (80%)
Frame 81 (81%)
Frame 82 (82%)
Frame 83 (83%)
Frame 84 (84%)
Frame 85 (85%)
Frame 86 (86%)
Frame 87 (87%)
Frame 88 (88%)
Frame 89 (89%)
Frame 90 (90%)
Frame 91 (91%)
Frame 92 (92%)
Frame 93 (93%)
Frame 94 (94%)
Frame 95 (95%)
Frame 96 (96%)
Frame 97 (97%)
Frame 98 (98%)
Frame 99 (99%)
Frame 100 (100%)
Finalizing encoding... done!
#save animation as a .gif
  anim_save("Js_awn_map_red_blue.gif", JSmap)

Frame 1 (1%)
Frame 2 (2%)
Frame 3 (3%)
Frame 4 (4%)
Frame 5 (5%)
Frame 6 (6%)
Frame 7 (7%)
Frame 8 (8%)
Frame 9 (9%)
Frame 10 (10%)
Frame 11 (11%)
Frame 12 (12%)
Frame 13 (13%)
Frame 14 (14%)
Frame 15 (15%)
Frame 16 (16%)
Frame 17 (17%)
Frame 18 (18%)
Frame 19 (19%)
Frame 20 (20%)
Frame 21 (21%)
Frame 22 (22%)
Frame 23 (23%)
Frame 24 (24%)
Frame 25 (25%)
Frame 26 (26%)
Frame 27 (27%)
Frame 28 (28%)
Frame 29 (29%)
Frame 30 (30%)
Frame 31 (31%)
Frame 32 (32%)
Frame 33 (33%)
Frame 34 (34%)
Frame 35 (35%)
Frame 36 (36%)
Frame 37 (37%)
Frame 38 (38%)
Frame 39 (39%)
Frame 40 (40%)
Frame 41 (41%)
Frame 42 (42%)
Frame 43 (43%)
Frame 44 (44%)
Frame 45 (45%)
Frame 46 (46%)
Frame 47 (47%)
Frame 48 (48%)
Frame 49 (49%)
Frame 50 (50%)
Frame 51 (51%)
Frame 52 (52%)
Frame 53 (53%)
Frame 54 (54%)
Frame 55 (55%)
Frame 56 (56%)
Frame 57 (57%)
Frame 58 (58%)
Frame 59 (59%)
Frame 60 (60%)
Frame 61 (61%)
Frame 62 (62%)
Frame 63 (63%)
Frame 64 (64%)
Frame 65 (65%)
Frame 66 (66%)
Frame 67 (67%)
Frame 68 (68%)
Frame 69 (69%)
Frame 70 (70%)
Frame 71 (71%)
Frame 72 (72%)
Frame 73 (73%)
Frame 74 (74%)
Frame 75 (75%)
Frame 76 (76%)
Frame 77 (77%)
Frame 78 (78%)
Frame 79 (79%)
Frame 80 (80%)
Frame 81 (81%)
Frame 82 (82%)
Frame 83 (83%)
Frame 84 (84%)
Frame 85 (85%)
Frame 86 (86%)
Frame 87 (87%)
Frame 88 (88%)
Frame 89 (89%)
Frame 90 (90%)
Frame 91 (91%)
Frame 92 (92%)
Frame 93 (93%)
Frame 94 (94%)
Frame 95 (95%)
Frame 96 (96%)
Frame 97 (97%)
Frame 98 (98%)
Frame 99 (99%)
Frame 100 (100%)
Finalizing encoding... done!
#################################
### Interactive leaflet map #####
#################################

library(leaflet)
library(dplyr)

# read in data
awn<-read.csv("awndatanew.csv")

# add interactive popup information from data file
awn<-awn%>%mutate(popup_info=paste(Collection,"<br/>",year,"<br/>","Awn?",awn,"<br/>","Elevation",elev,"<br/>",County))

# map the points based on lat/lon
leaflet()%>%addTiles()%>%addCircleMarkers(data=awn, lat=~decimalLatitude,lng=~decimalLongitude,radius=4,color= ~awn,popup = ~popup_info)
Data contains 1 rows with either missing or invalid lat/lon values and will be ignored

#create color pallete for awned/awnless records
colors<-c("red","blue")
pal<-colorFactor(colors, awn$awn)

# Now, map with colors indicating awned/awnless
leaflet()%>%addTiles()%>%addCircleMarkers(data=awn, lat=~decimalLatitude,lng=~decimalLongitude,radius=4,popup = ~popup_info,color=~pal(awn))
Data contains 1 rows with either missing or invalid lat/lon values and will be ignored
LS0tDQp0aXRsZTogIkFuaW1hdGlvbiAoZ2dhbmltYXRlKSBhbmQgaW50ZXJhY3RpdmUgbGVhZmxldCBtYXAgb2YgSmFwYW5lc2Ugc3RpbHRncmFzcyBpbnZhc2lvbiBoaXN0b3J5IGluIFIiDQpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sNCmVkaXRvcl9vcHRpb25zOiANCiAgY2h1bmtfb3V0cHV0X3R5cGU6IGlubGluZQ0KLS0tDQpDcmFpZyBGLiBCYXJyZXR0PGJyPg0KQXByaWwgMDcsIDIwMjE8YnI+DQpEZXBhcnRtZW50IG9mIEJpb2xvZ3k8YnI+DQpXZXN0IFZpcmdpbmlhIFVuaXZlcnNpdHk8YnI+DQpNb3JnYW50b3duLCBXViBVU0EgMjY1MDY8YnI+DQpjZmIwMDAxQG1haWwud3Z1LmVkdTxicj4NCg0KYGBge3J9DQojIGluc3RhbGwgcGFja2FnZXMgaWYgbmVlZGVkDQojIGluc3RhbGwucGFja2FnZXMoYygicmdiaWYiLCAiZ2dwbG90MiIsICJ0aWR5dmVyc2UiLCAiZ2dhbmltYXRlIiwgImdndGhlbWVzIiwgInNmIiwgInRvb2xzIiwgIm1hcHMiLCAicm5hdHVyYWxlYXJ0aCIsICJybmF0dXJhbGVhcnRoZGF0YSIpKQ0KDQojIGxvYWQgbGlicmFyaWVzDQpsaWJyYXJ5KHNoaW55KQ0KbGlicmFyeShnZ3Bsb3QyKQ0KbGlicmFyeSh0aWR5dmVyc2UpDQpsaWJyYXJ5KGdnYW5pbWF0ZSkNCmxpYnJhcnkoZ2d0aGVtZXMpDQpsaWJyYXJ5KHNmKQ0KbGlicmFyeSgidG9vbHMiKQ0KbGlicmFyeShtYXBzKQ0KbGlicmFyeSgicm5hdHVyYWxlYXJ0aCIpDQpsaWJyYXJ5KCJybmF0dXJhbGVhcnRoZGF0YSIpDQoNCmBgYA0KDQoNCg0KYGBge3J9DQojcmVhZCBpbiBkYXRhLCBjb252ZXJ0IHRvIGRhdGEuZnJhbWUsIHNldCAiYXduIiBhcyBhIGZhY3Rvcg0Kc2V0d2QoY2hvb3NlLmRpcigpKQ0KYXduIDwtIHJlYWQuY3N2KCJhd25kYXRhbmV3LmNzdiIpDQphd24gPC0gZGF0YS5mcmFtZShhd24pDQpgYGANCg0KYGBge3J9DQojIHJlbW92ZSBkdXBsaWNhdGUgcmVjb3JkcyBiYXNlZCBvbiBsYXRpdHVkZQ0KYXduIDwtIGF3biAlPiUgZGlzdGluY3QoZGVjaW1hbExhdGl0dWRlLCAua2VlcF9hbGwgPSBUUlVFKQ0KYXducyA8LSBhcy5mYWN0b3IoYXduJGF3bikNCmF3biA8LSBkYXRhLmZyYW1lKGF3bikNCg0KYGBgDQpgYGB7cn0NCndvcmxkIDwtIG5lX2NvdW50cmllcyhzY2FsZSA9ICJtZWRpdW0iLCByZXR1cm5jbGFzcyA9ICJzZiIpDQojIyBjbGFzcyh3b3JsZCkNCnN0YXRlcyA8LSBzdF9hc19zZihtYXAoInN0YXRlIiwgcGxvdCA9IEZBTFNFLCBmaWxsID0gVFJVRSkpDQojIyBoZWFkKHN0YXRlcykNCnN0YXRlcyA8LSBjYmluZChzdGF0ZXMsIHN0X2Nvb3JkaW5hdGVzKHN0X2NlbnRyb2lkKHN0YXRlcykpKQ0KDQojR2VuZXJhdGUgdGhlIG1hcCBhbmQgY29vcmRpbmF0ZSBib3VuZGFyaWVzDQp1c2EgPC0gZ2dwbG90KGRhdGEgPSB3b3JsZCkgKw0KICAgICNtdXRhdGUoeWVhciA9IGFzLm51bWVyaWMoYXduJHllYXIpKSArIA0KICAgIGdlb21fc2YoZGF0YSA9IHN0YXRlcywgZmlsbCA9IE5BKSArIA0KICAgIGNvb3JkX3NmKHhsaW0gPSBjKC05NiwgLTcwKSwgeWxpbSA9IGMoMjgsIDQ1KSwgZXhwYW5kID0gRkFMU0UpDQoNCg0KYGBgDQpgYGB7cn0NCiMgbWFwIHRoZSBvY2N1cnJlbmNlIGRhdGENCkpTbWFwIDwtIHVzYSArDQogIGdlb21fcG9pbnQoZGF0YSA9IGF3biwgc2hhcGUgPSAyMSwgY29sb3VyPSJibGFjayIsIGFlcyh4PWRlY2ltYWxMb25naXR1ZGUsIHk9ZGVjaW1hbExhdGl0dWRlLCBmaWxsPWF3bnMpLCBzaXplPTYsIHN0cm9rZT0xKSArDQogICN0cmFuc2l0aW9uX3N0YXRlcyh5ZWFyLCB0cmFuc2l0aW9uX2xlbmd0aCA9IDEsIHN0YXRlX2xlbmd0aCA9IDEpICsgDQogIHNjYWxlX2ZpbGxfbWFudWFsKHZhbHVlcz1jKCJyZWQiLCAiYmx1ZSIpKSArDQogIHRyYW5zaXRpb25fdGltZSh5ZWFyKSArDQogIGxhYnModGl0bGUgPSAnRGlnaXRpemVkIGhlcmJhcml1bSByZWNvcmRzIG9mIE1pY3Jvc3RlZ2l1bSB2aW1pbmV1bScsIHN1YnRpdGxlID0gJ1llYXI6IHtyb3VuZChmcmFtZV90aW1lLDApfScpICsNCiAgc2hhZG93X21hcmsoKQ0KDQpgYGANCmBgYHtyfQ0KI2FuaW1hdGUhDQpKU21hcA0KDQpgYGANCmBgYHtyfQ0KI3NhdmUgYW5pbWF0aW9uIGFzIGEgLmdpZg0KICBhbmltX3NhdmUoIkpzX2F3bl9tYXBfcmVkX2JsdWUuZ2lmIiwgSlNtYXApDQoNCmBgYA0KDQpgYGB7cn0NCiMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIw0KIyMjIEludGVyYWN0aXZlIGxlYWZsZXQgbWFwICMjIyMjDQojIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMNCg0KbGlicmFyeShsZWFmbGV0KQ0KbGlicmFyeShkcGx5cikNCg0KIyByZWFkIGluIGRhdGENCmF3bjwtcmVhZC5jc3YoImF3bmRhdGFuZXcuY3N2IikNCg0KIyBhZGQgaW50ZXJhY3RpdmUgcG9wdXAgaW5mb3JtYXRpb24gZnJvbSBkYXRhIGZpbGUNCmF3bjwtYXduJT4lbXV0YXRlKHBvcHVwX2luZm89cGFzdGUoQ29sbGVjdGlvbiwiPGJyLz4iLHllYXIsIjxici8+IiwiQXduPyIsYXduLCI8YnIvPiIsIkVsZXZhdGlvbiIsZWxldiwiPGJyLz4iLENvdW50eSkpDQoNCiMgbWFwIHRoZSBwb2ludHMgYmFzZWQgb24gbGF0L2xvbg0KbGVhZmxldCgpJT4lYWRkVGlsZXMoKSU+JWFkZENpcmNsZU1hcmtlcnMoZGF0YT1hd24sIGxhdD1+ZGVjaW1hbExhdGl0dWRlLGxuZz1+ZGVjaW1hbExvbmdpdHVkZSxyYWRpdXM9NCxjb2xvcj0gfmF3bixwb3B1cCA9IH5wb3B1cF9pbmZvKQ0KDQojY3JlYXRlIGNvbG9yIHBhbGxldGUgZm9yIGF3bmVkL2F3bmxlc3MgcmVjb3Jkcw0KY29sb3JzPC1jKCJyZWQiLCJibHVlIikNCnBhbDwtY29sb3JGYWN0b3IoY29sb3JzLCBhd24kYXduKQ0KDQojIE5vdywgbWFwIHdpdGggY29sb3JzIGluZGljYXRpbmcgYXduZWQvYXdubGVzcw0KbGVhZmxldCgpJT4lYWRkVGlsZXMoKSU+JWFkZENpcmNsZU1hcmtlcnMoZGF0YT1hd24sIGxhdD1+ZGVjaW1hbExhdGl0dWRlLGxuZz1+ZGVjaW1hbExvbmdpdHVkZSxyYWRpdXM9NCxwb3B1cCA9IH5wb3B1cF9pbmZvLGNvbG9yPX5wYWwoYXduKSkNCg0KDQpgYGANCg==