Species distribution mapping

I have a data frame that consists of the columns Species, Latitude, and Longitude, with 1200 rows. I wish to map this data onto a map of Europe, for a publishable ecology paper. However, since there are so many plot points, I need to plot the data as shaded areas or solid lines of distribution, instead of individual points.
The outcome needs to be presentable enough for publication.

Any help would be extremely appreciated!

Here is a reprex of the data:

tibble::tribble(
           ~Species, ~Latitude, ~Longitude,
  "Patella vulgata",  6744266L, -522088.41,
  "Patella vulgata",  6439351L, -706878.77,
  "Patella vulgata",  6679876L,  156960.48,
  "Patella vulgata",  7369633L, -159186.87,
  "Patella vulgata",  8056069L,  -567729.4,
  "Patella vulgata",  8575482L,  -91281.98,
  "Patella vulgata",  8184711L, -374033.49,
  "Patella vulgata",  6533322L, -390731.41,
  "Patella vulgata",  6845456L, -474221.03,
  "Patella vulgata",  7198616L,   -21150.7,
  "Patella vulgata",  6559615L, -163639.65,
  "Patella vulgata",  7276935L, -399636.97,
  "Patella vulgata",  8437684L, -145828.53,
  "Patella vulgata",  7330875L, -148054.92,
  "Patella vulgata",  8388614L, -149168.12,
  "Patella vulgata",  7367691L, -159186.87,
  "Patella vulgata",  7578349L, -541012.73,
  "Patella vulgata",  7408585L, -170318.82,
  "Patella vulgata",  8228090L, -279411.92,
  "Patella vulgata",  7020079L, -468655.06
  )
1 Like

The latitude and longitude are different orders of magnitude from each other ?

It looks like admin on this site has edited the reprex for some reason, not sure why.

Here's my reprex:

Species Latitude Longitude
Patella vulgata 59.629463 5.310413
Patella vulgata 48.693563 -4.119703
Patella vulgata 50.81394 -0.345862
Patella vulgata 45.998708 -1.107789
Patella vulgata 51.75 3.85
Patella vulgata 37.345038 -8.850797
Patella vulgata 51.6 3.65
Patella vulgata 40.607026 0.584646
Patella vulgata 51.55 3.7
Patella vulgata 51.4 3.55
Patella vulgata 36.746375 -3.655908
Patella vulgata 51.5 3.9
Patella vulgata 51.058509 2.431043
Patella vulgata 51.5 3.95
Patella vulgata 51.3 3.75
Patella vulgata 51.65 3.7
Patella vulgata 58.706916 9.213822
Patella vulgata 68.854241 17.008064
Patella vulgata 53.013593 -9.405609
Patella vulgata 51.198008 -4.17878
Patella vulgata 47.5379 -2.895345
Patella vulgata 50.871931 1.588353
Patella vulgata 37.09634 -8.471687
Patella vulgata 53.36531 -6.088289
Patella vulgata 50.870911 1.591342
Patella vulgata 51.35 3.35
Patella vulgata 54.079773 -0.194446
Patella vulgata 50.824707 1.588169
Patella vulgata 58.875792 5.590926
Patella vulgata 58.95639 5.609036
Patella vulgata 58.876391 5.595327
Patella vulgata 58.261631 8.515104
Patella vulgata 38.688802 -9.36183
Patella vulgata 52.08542 -7.54733

The reason is that it was on its face, not a reprex, a reprex is runnable code. You have provided mere text...
This text can be read by using read_tsv, and I reexport this for the convenience of others as a tribble that can be copied and pasted and directly interpreted by forum users who have library(tidyverse) loaded.

tibble::tribble(
  ~Species, ~Latitude, ~Longitude,
  "Patella vulgata", 59.629463,   5.310413,
  "Patella vulgata", 48.693563,  -4.119703,
  "Patella vulgata",  50.81394,  -0.345862,
  "Patella vulgata", 45.998708,  -1.107789,
  "Patella vulgata",     51.75,       3.85,
  "Patella vulgata", 37.345038,  -8.850797,
  "Patella vulgata",      51.6,       3.65,
  "Patella vulgata", 40.607026,   0.584646,
  "Patella vulgata",     51.55,        3.7,
  "Patella vulgata",      51.4,       3.55,
  "Patella vulgata", 36.746375,  -3.655908,
  "Patella vulgata",      51.5,        3.9,
  "Patella vulgata", 51.058509,   2.431043,
  "Patella vulgata",      51.5,       3.95,
  "Patella vulgata",      51.3,       3.75,
  "Patella vulgata",     51.65,        3.7,
  "Patella vulgata", 58.706916,   9.213822,
  "Patella vulgata", 68.854241,  17.008064,
  "Patella vulgata", 53.013593,  -9.405609,
  "Patella vulgata", 51.198008,   -4.17878,
  "Patella vulgata",   47.5379,  -2.895345,
  "Patella vulgata", 50.871931,   1.588353,
  "Patella vulgata",  37.09634,  -8.471687,
  "Patella vulgata",  53.36531,  -6.088289,
  "Patella vulgata", 50.870911,   1.591342,
  "Patella vulgata",     51.35,       3.35,
  "Patella vulgata", 54.079773,  -0.194446,
  "Patella vulgata", 50.824707,   1.588169,
  "Patella vulgata", 58.875792,   5.590926,
  "Patella vulgata",  58.95639,   5.609036,
  "Patella vulgata", 58.876391,   5.595327,
  "Patella vulgata", 58.261631,   8.515104,
  "Patella vulgata", 38.688802,   -9.36183,
  "Patella vulgata",  52.08542,   -7.54733
)
1 Like

maybe something like this

library(tidyverse)
library(sf)
library(rnaturalearth)
library(rnaturalearthdata)
library(rgeos)
world <- ne_countries(scale = "medium", returnclass = "sf")
Europe <- world[which(world$continent == "Europe"), ]


(example_data <- tibble::tribble(
  ~Species, ~Latitude, ~Longitude,
  "Patella vulgata", 59.629463, 5.310413,
  "Patella vulgata", 48.693563, -4.119703,
  "Patella vulgata", 50.81394, -0.345862,
  "Patella vulgata", 45.998708, -1.107789,
  "Patella vulgata", 51.75, 3.85,
  "Patella vulgata", 37.345038, -8.850797,
  "Patella vulgata", 51.6, 3.65,
  "Patella vulgata", 40.607026, 0.584646,
  "Patella vulgata", 51.55, 3.7,
  "Patella vulgata", 51.4, 3.55,
  "Patella vulgata", 36.746375, -3.655908,
  "Patella vulgata", 51.5, 3.9,
  "Patella vulgata", 51.058509, 2.431043,
  "Patella vulgata", 51.5, 3.95,
  "Patella vulgata", 51.3, 3.75,
  "Patella vulgata", 51.65, 3.7,
  "Patella vulgata", 58.706916, 9.213822,
  "Patella vulgata", 68.854241, 17.008064,
  "Patella vulgata", 53.013593, -9.405609,
  "Patella vulgata", 51.198008, -4.17878,
  "Patella vulgata", 47.5379, -2.895345,
  "Patella vulgata", 50.871931, 1.588353,
  "Patella vulgata", 37.09634, -8.471687,
  "Patella vulgata", 53.36531, -6.088289,
  "Patella vulgata", 50.870911, 1.591342,
  "Patella vulgata", 51.35, 3.35,
  "Patella vulgata", 54.079773, -0.194446,
  "Patella vulgata", 50.824707, 1.588169,
  "Patella vulgata", 58.875792, 5.590926,
  "Patella vulgata", 58.95639, 5.609036,
  "Patella vulgata", 58.876391, 5.595327,
  "Patella vulgata", 58.261631, 8.515104,
  "Patella vulgata", 38.688802, -9.36183,
  "Patella vulgata", 52.08542, -7.54733
))


ggplot(Europe) +
  geom_sf() +
  coord_sf(
    expand = FALSE, xlim = c(-13, 20),
    ylim = c(34, 70)
  ) +
  geom_density_2d_filled(
    data = example_data,
    mapping = aes(
      x = Longitude,
      y = Latitude,
      alpha=(ifelse(as.numeric(after_stat(level)) <= 1, 0, 1)),
      fill=after_stat(level)
    )
  )+theme(legend.position = "none")

image

Another example, using maps and ggpointdensity:

library(maps)
library(ggpointdensity)

worldmap = map_data("world")

ggplot() + 
  # show the countries
  geom_polygon(data= worldmap, 
               aes(x=long, y=lat, group=group),
               color="black", fill="lightblue", alpha = 0.2) + 
   # limit the map to the mapped data, you may adjust the borders manually as well
  coord_cartesian(xlim = c(min(geodata$Longitude), max(geodata$Longitude)), 
                  ylim = c(min(geodata$Latitude), max(geodata$Latitude))) + 
   # show the locations
  geom_pointdensity(data = example_data,
             aes(x = Longitude,
                 y = Latitude),
             size = 3) + 
  theme_minimal()

(axes are nicer in the previous example)

When using geom_density it might be better to draw the land area on top of the density plots:

ggplot() + 
  coord_cartesian(xlim = c(min(geodata$Longitude), max(geodata$Longitude)), 
                  ylim = c(min(geodata$Latitude), max(geodata$Latitude))) + 
  theme_minimal() + 
  geom_density_2d_filled(
   data = example_data,
   mapping = aes(x = Longitude,
                 y = Latitude,
                 alpha=(ifelse(as.numeric(after_stat(level)) <= 1, 0, 1)),
                 fill=after_stat(level) )) +
  # show the countries
  geom_polygon(data= worldmap, 
               aes(x=long, y=lat, group=group),
               color="black", fill="grey80") + 
  theme(legend.position = "none")