Plotting Airbnb data over NASA's "black marble" geotiff - r

I am trying to create an image similar to that presented by Ricardo Bion of Airbnb but I would like to plot the visualization over the NASA "black marble" image to give more context as I don't have nearly the data density of the Airbnb dataset.
I downloaded the Nasa black marble image here using the global map 13500x6750 (3km) GeoTIFF 39 MB option.
This issue I keep running into is most of the options and explanations available online have been depreciated in the past few years. I tried using EBImage as shown here but ebimageGrob has been removed from gridExtra. I also tried to use the rasterVis package as shown here but the code breaks at the colorable step.
Here is as far as I have made it trying to layer the tiff behind the plot using the ggplot2 annotation_raster option (this gives the lines between the destinations but only a white background):
library(ggplot2)
library(ggmap)
library(sp)
library(grid)
library(geosphere)
library(plyr)
library(tiff)
# source the theme_map for ggplot2
# source("https://dl.dropboxusercontent.com/u/2364714/theme_map.R")
# in the original post I had a data.frame with 500k rows of top origin destination pairs
trips <- data.frame(origin = c("San Francisco", "Sydney", "Chicago"),
destination = c("Paris", "Tokyo", "Boston"),
stringsAsFactors = FALSE)
# get lat and lon of cities
trips$geocode_origin <- suppressMessages(geocode(trips$origin))
trips$geocode_destination <- suppressMessages(geocode(trips$destination))
# get intermediate points between the two locations
arch <- gcIntermediate(trips$geocode_origin,
trips$geocode_destination,
n=100,
breakAtDateLine=FALSE,
addStartEnd=TRUE, sp=TRUE)
# http://docs.ggplot2.org/0.9.3.1/fortify.map.html
arch_fortified <- ldply(arch#lines, fortify)
earth <- readTIFF("~/Downloads/dnb_land_ocean_ice.2012.13500x6750_geo.tif")
theme_map <- function(base_size = 12) {
require(grid)
theme_grey(base_size) %+replace%
theme(
axis.title = element_blank(),
axis.text = element_blank(),
panel.grid = element_blank(),
axis.ticks.length = unit(0,"cm"),
panel.margin = unit(0,"lines"),
plot.margin = unit(c(0,0,0,0),"lines"),
complete = TRUE,
panel.background = element_rect(fill = NA, colour=NA)
)
}
# a few lines of ggplot2 code
ggplot() +
geom_line(aes(long,lat,group=group), data=arch_fortified, alpha=0.1,size=1, colour="skyblue1") +
coord_cartesian(ylim =c(-45, 70), xlim=c(-165, 165)) +
theme_map() +
geom_point(aes(lon, lat),data=trips$geocode_origin, alpha = 0.8, size = 1, colour = "white") +
geom_point(aes(lon, lat),data=trips$geocode_destination, alpha = 0.8, size = 1, colour = "white") +
annotation_raster(earth, -180, 180, -90, 90)
Thanks!

I just had to slightly modify your plotting code to get it work:
ggplot(arch_fortified) +
coord_cartesian(ylim =c(-45, 70), xlim=c(-165, 165)) +
theme_map() +
annotation_raster(earth, -180, 180, -90, 90) +
geom_line(aes(long,lat,group=group), alpha=0.1,size=1, colour="skyblue1") +
geom_point(aes(lon, lat),data=trips$geocode_origin, alpha = 0.8, size = 1, colour = "white") +
geom_point(aes(lon, lat),data=trips$geocode_destination, alpha = 0.8, size = 1, colour = "white")
Note that you should first draw the background and only then the lines and points, otherwise the image will cover other plot elements.

Related

How to relocate the position on the map (ggplot/sf/geometry)

I am using R to draw a map of China and the USA. I would like to map the "holistic thinking" across these two countries. Could you help me with the followings?
relocate these two countries? There is too much space between the two countries. I would like to narrow the space between the two countries and zoom the size of the two countries.
smaller the size of "Alaska"? I think the size of Alaska is too big relative to the size of the USA.
This is my code.
holistic_10_plot1 <- st_read(" two_triad_current ") %>%
ggplot() +
geom_sf(aes(fill = holistic_10_province)) +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
geom_sf_text(aes(label = province_current), color = "grey", family = "sans", angle = 0) +
theme_void() +
labs(title = "The number of holistic groups(10 focal groups) \n current states/provinces") +
theme(legend.position = "bottom", plot.title = element_text(hjust = 0.5), title = element_text(size = 14, face = "bold", color = "black")) +
guides(fill = guide_colorbar(title = "The number of the holistic groups", title.position = "top"))
This is the plot.
enter image description here
to relocate the positon of China or the USA to make them closer.
You can shift an sf object simply by recalculating its geometry column like so:
library(sf)
## create example sf object "nc":
nc <- st_read(system.file("shape/nc.shp", package="sf"))
add another geometry column (or change it in place):
library(sf)
## shift geometry .1 degrees east and .1 degrees north:
nc$shifted_geometry <- nc |> st_geometry() + c(.1, -.1)
plot original and shifted geometry:
nc |> st_geometry() |> plot()
nc$shifted_geometry |> plot(add = TRUE, border = 'red')
Concerning the area, you could set an equal-area projection with st_transform().

Limit Simple Features Plot based on geojson Polygon

I'd like to limit a plot based on a polygon defined in geojson, so that it only shows the area shaded blue here.
i.e. just plot the features inside and including the ring road.
The geojson is available here.
It would also be great to add a buffer around the edge to include the ring road.
My code to draw all the features (unlimited by the geojson is below).
library(tidyverse)
library(osmdata)
bounding_box <- getbb("Birmingham", featuretype = "city")
streets <- bounding_box %>%
opq()%>%
add_osm_feature(key = "highway",
value = c("motorway", "trunk", "primary", "secondary", "tertiary")) %>%
osmdata_sf()
ggplot() +
geom_sf(data = streets$osm_lines,
inherit.aes = FALSE,
color = "grey",
size = 1) +
theme_void() +
theme(
plot.background = element_rect(fill = "white"),
legend.position = "none"
) +
coord_sf(xlim = c(-1.933, -1.869),
ylim = c(52.46, 52.496),
expand = FALSE)
I assume in the following that the object streets has already been defined by running the first few lines of the code in the question. The next step is then to read the polygon using read_sf() from the sf package. The next line converts to a more suitable coordinate system (OSGB 1936 / British National Grid) because adding a buffer in meters is not possible in lon/lat-coordinates. A buffer of 40 meters is added using st_buffer() and finally the coordinates are transformed back to WGS84:
library(sf)
area <- read_sf("~/Birmingham CAZ 2020.GeoJSON") %>%
st_transform(27700) %>%
st_buffer(units::set_units(40, m)) %>%
st_transform(4326)
Of course, you need to adapt the path to where you have actually stored the file. Then I use st_intersection() to extract the part of streets$osm_lines that lies inside the polygon:
streets_area <- st_intersection(poly, streets$osm_lines)
And finally I produce the plot using the code from your question. Note that I have added a layer with the polygon in the second line in order to demonstrate that the streets indeed lie inside the polygon:
ggplot() +
geom_sf(data = area) +
geom_sf(data = streets_area,
inherit.aes = FALSE,
color = "grey",
size = 1) +
theme_void() +
theme(
plot.background = element_rect(fill = "white"),
legend.position = "none"
) +
coord_sf(xlim = c(-1.933, -1.869),
ylim = c(52.46, 52.496),
expand = FALSE)

Plot lat/lon points on OpenStreetMap Using R

I am trouble wrapping my head around projection. My points for a place in Northern Europe ends up in Mid Africa.
My code goes as follow.
#Loading packages
library(OpenStreetMap)
library(rgdal)
library(ggplot2)
#defining world map
map <- openmap(c(70,-179), c(-70,179))
plot(map)
#Finding my work place in Northern Europe (Ørbækvej 100, Odense, Denmark from here: https://www.latlong.net/convert-address-to-lat-long.html)
subscr<-data.frame(lat=c(55.381640),
lon=c(10.433600))
#I am not sure what this does, but found on the web for a map in Germany: (https://gis.stackexchange.com/questions/209166/plotting-bubbles-on-top-of-openstreetmap-in-r)
coordinates(subscr)<-~lat+lon
proj4string(subscr)<-CRS("+init=epsg:4326")
points(spTransform(subscr,osm()))
#as can be seen using this method the dot turns up in Eastern Africa
symbols(y = subscr$lon, x = subscr$lat, circles = 1, add = TRUE,
inches = 0.0001, bg = "darkgreen")
#as can be seen using the method the dot turns up in Western/Mid Africa
Can anyone explain or even help me to get the dot placed in Denmark, Northern Europe?
I do not know what kind of map you want, but for plotting lat-lon points, leaflet is my default weapon of choice..
library( leaflet )
library( magrittr )
subscr<-data.frame(lat=c(55.381640),
lon=c(10.433600))
leaflet() %>% addTiles() %>%
addCircleMarkers(data = subscr,
lat = ~lat, lng = ~lon,
color = "blue")
Are you bound to using open street maps? You might consider using the ggmap package which interacts pretty well with ggplot2. However, I sometimes have troubles with downloading an open street map with ggmap, but google-maps should work.
Consider the following example. Note that I removed unnecessary text in the map in the download command.
# download
map <- get_googlemap(center = "Europe", zoom = 3,
style = paste0("feature:administrative.country|",
"element:labels|visibility:off"),
filename = "Map",
language = "en-EN") # you might want to adjust the language settings
# see what you've got
ggmap(map)
# edit map
ggmap(map)+
# do some scaling (make it smaller)
scale_x_continuous(limits = c(-12, 42), expand = c(0, 0)) +
scale_y_continuous(limits = c(35, 70), expand = c(0, 0))+
# remove unwanted information
theme(axis.title.x = element_blank(),
axis.title.y = element_blank(),
axis.line = element_blank(),
axis.ticks = element_blank(),
axis.text = element_blank(),
plot.title = element_blank(),
plot.background = element_blank())+
# add whatever you like based on your coordinates using annotate()
annotate("text", x = 10.433600, y = 55.381640,
label = "You are here",
size = 2.4, color = "black", fontface = "bold",
na.rm = TRUE, hjust = 0.5)
Does this solve your problem?

How to add lines of longitude and latitude on a map using ggplot2?

I am now plotting the map of Canada using ggplot2. Because the default projection method is "aea"(Albers Equal Area), so the longitude and latitude are straight lines on the map. I wonder how I can display the longitude and latitude in the form of "110W, 100W, 90W" and "50N, 60N, 70N" on the map. They should be curves. Thanks a lot.
The arcgis shapfile is downloaded from https://www.arcgis.com/home/item.html?id=dcbcdf86939548af81efbd2d732336db
library(ggplot2)
library(rgdal)
countries<-readOGR("Canada.shp", layer="Canada")
ggplot()+geom_polygon(data=countries,aes(x=long,y=lat,group=group),fill='white',color = "black")
The final result should be like this.
You can do this with the coord_map argument of ggplot documented here
This uses projections to alter the coordinate grid. Curved lines would include equal-distance projections, but you should look here for a list of all projections allowed. Which one you choose is a manner of preference.
Using azequidistant (I think this is the Azimuthal equidistant projection), and adding labels manually:
axis_labels <- rbind(
data.frame(long = rep(-140,5),lat = seq(40,80,10), labels = seq(40,80,10)), # x axis labels
data.frame(long = seq(-140,-60,40),lat = rep(85,3), labels = seq(140,60,-40)) # y axis labels
)
ggplot() +
geom_polygon(data=countries,aes(x=long,y=lat,group=group),fill='white',color = "black") +
coord_map("azequidistant") +
scale_x_continuous(breaks = seq(-140,60, by = 20))+
scale_y_continuous(breaks = seq(40,80, by = 10)) +
geom_text(data = axis_labels, aes(x = long, y = lat, label = labels)) +
theme_bw() +
theme(panel.grid.major = element_line(colour = "grey"),
panel.border = element_blank(),
axis.text = element_blank())
You can use a separate graticule layer of spatial data, which you then project based on your Canada layer.
You can find free graticule layers for download at NaturalEarthData.
countries<-readOGR("Canada.shp", layer="Canada")
grat <- readOGR("graticule.shp", layer="graticule")
grat_prj <- spTransform(grat, CRS(countries))
ggplot() +
geom_polygon(data=countries, aes(x=long,y=lat,group=group),fill='white',color = "black") +
geom_path(data=grat_prj, aes(long, lat, group=group, fill=NULL), linetype="solid", color="grey50")

How can I add labels to a choropleth map created using ggplot2?

I am attempting and currently failing to add text annotations to a choropleth map I created in ggplot2. I am seeking to label each of the polygons (a local government area) with its name.
Before I go on, I know that a similar questions have been asked both on SO and detailed in a (very good) tutorial here. However, I have tried several methods unsuccessfully and think I may have stumbled across a different issue. I suspect that one of the reasons that my code is failing is that I am trying to annotate a geom_polygon() whereas other methods I have seen have detailed how to annotate a geom_map object. That said, I can't think why this shouldn't be possible with a geom_polygon.
I have included my code below. You can download my data from here. The data frame includes my data joined to a fortified shapefile. The labels I am attempting to append are in the column "LGA_NAME11".
## LOAD PACKAGES
require(ggplot2)
require(rgdal)
require(dplyr)
## SET GGPLOT THEME
theme_clean <- function(base_size = 12) {
require(grid)
theme_grey(base_size) %+replace%
theme(
axis.title = element_blank(),
axis.text = element_blank(),
panel.background = element_blank(),
panel.grid = element_blank(),
axis.ticks.length = unit(0,"cm"),
axis.ticks.margin = unit(0,"cm"),
panel.margin = unit(0,"lines"),
plot.margin = unit(c(0, 0, 0, 0), "lines"),
complete = TRUE
)}
## SET COLOUR PALETTES
palette1 <- c("#f2f0f7", "#dadaeb", "#bcbddc", "#9e9ac8", "#756bb1", "#54278f")
## SET LABEL NAMES
lgaNamesSydney <- aggregate(cbind(long, lat) ~ LGA_NAME11, data=sydneyMapData, FUN = function(x) mean(range(x)))
lgaNamesSydney <- lgaNamesSydney %>% rename(lga = LGA_NAME11)
lgaNamesSydney$angle <- 0
## ATTEMPT TO PLOT MAP WITH LABELS
ggplot(sydneyMapData) +
aes(long, lat, group=group, fill=Factor1) +
geom_polygon() +
geom_text(data=lgaNamesSydney, aes(long, lat, label = LGA_NAME11, angle=angle, map_id =NULL), size=2.5) +
scale_fill_manual(values = palette1) +
labs(fill="Drop Bears \nper 1000 population") +
coord_map(projection = "mercator") +
theme_clean()
If anyone has any suggestions, I would be extremely grateful if anyone could point where I am going wrong. Thanks in advance.
As an aside, I recognise that labeling choropleths can detract from the visual experience, but the boss has requested them specially!
You could add labels like this
# http://stackoverflow.com/questions/9441778/improve-centering-county-names-ggplot-maps
centroids <- setNames(do.call("rbind.data.frame", by(sydneyMapData, sydneyMapData$group, function(x) {Polygon(x[c('long', 'lat')])#labpt})), c('long', 'lat'))
centroids$label <- sydneyMapData$LGA_NAME11[match(rownames(centroids), sydneyMapData$group)]
ggplot(sydneyMapData, aes(long, lat, group=group, fill=Factor1)) +
geom_polygon(colour = "white") +
with(centroids, annotate(geom="text", x = long, y=lat, label = label, size = 2.5))

Resources