ggmap is unable to plot specific region - r

I am trying to plot a specific region in the arctic with ggmap. The center of the region is somewhere around lat. 80 lon. 0, unfortunately only a grey background and the latitude and longitude axis are shown. I tried to plot different regions and my code seems to work on every location, except regions which are beyond lat. 73.6. Here is an example of my code:
library(ggmap)
library(mapproj)
location <- get_map(location = c(lon = 0, lat = 80), zoom = 4, maptype = "hybrid")
ggmap(location)
So does anyone know, why ggmap is unable to plot this location?

I had the same problem (when trying to plot Antartica) and to get around it, I resorted to ggplot, but relying on a couple of functions from the ggmap package.
As #Henrik's link suggests, the map projection seems to be the problem.
The entire idea and code is courtesy of David Kahle
Here's what you can do to get it to work for your case:
location <- get_map(location = c(lon = 0, lat = 80), zoom = 4, maptype = "hybrid")
#Create a small data frame to pass to ggplot
fourCorners <- expand.grid(
lon = as.numeric(attr(location, "bb")[, c("ll.lon", "ur.lon")]),
lat = as.numeric(attr(location, "bb")[, c("ll.lat", "ur.lat")])
)
# The inset_raster function needs 4 data coordinates. Pull it out of your "location" that you got via get_map
xmin <- attr(location, "bb")$ll.lon
xmax <- attr(location, "bb")$ur.lon
ymin <- attr(location, "bb")$ll.lat
ymax <- attr(location, "bb")$ur.lat
# Now you are ready to plot it
mp <- ggplot(fourCorners, aes(x = lon, y = lat) ) +
inset_raster(location, xmin, xmax, ymin, ymax)
mp
Which gives you your "hybrid" map, centered at (Longitude=0, lat=80)

Related

Rayshader path has offset when plotting a ggplot2 object

I am trying to plot a world map and a path between Berlin and Cancun.
So far I managed to plot a world map using library rnaturalearth and convert it to a ggplot2 object which is plotted using rayshader plot_gg function.
After that I tried to add a rayshader path overlay from Berlin (52.5200° N, 13.4050° E) to Cancun (21.1619° N, -86.8515° E). Here is the result:
As you can see the path is up too high and also a bit too long. My best guess right now is that it has to do something with the offset of the rayshader plot and the ggplot object (The white border around the world map).
Here is my code:
#load libraries
library(sf)
library(rayshader)
library(rayrender)
library(rnaturalearth)
library(rnaturalearthdata)
library(tidyverse)
#get ggplot object of world
world <- ne_countries(scale = "medium", returnclass = "sf")
ggData = ggplot(data = world) + geom_sf()
#latitudes, longitudes of Berlin and Cancun as list for render_path()
lats = list()
lats[1] = 52.5
lats[2] = 21.1
longs = list()
longs[1] = 13.4
longs[2] = -86.8
#Altitude of the path
alts = list()
alts[1] = 100
alts[2] = 100
#plot with rayshader, width and height ratio of bbox of ggData
plot_gg(ggData, scale=10, width=3.6, height=1.7359854)
render_path(lat = unlist(lats), long = unlist(longs), zscale=50, color="red", antialias=TRUE, linewidth=3, extent=st_bbox(world), altitude=unlist(alts))
I tried it with different width and height ratios to match the ggplot object as I thought it might have to do something with wrong aspect ratios.
One option is to render the travel path not via rayshader but include it already in your ggplot object like so (required libraries as in OP):
create feature world:
world = ne_countries(scale = "medium", returnclass = "sf")
create feature my_path (the travel path) from linestring from point coordinates:
my_path <-
c(13.4, 52.5, ## longs
-86.6, 21.1 ## lats
) |>
matrix(ncol = 2, byrow = TRUE) |>
st_linestring() |>
st_sfc() |>
st_set_crs(4326) ## set CRS to geographic (degrees)
add features world and my_path as layers to ggplot object:
ggData <-
ggplot() +
geom_sf(data = world) +
geom_sf(data = my_path, col = 'red') +
coord_sf(
## see ?coord_sf to set map limits etc.
)
plot
plot_gg(ggData)
EDITED: The original plots were for a slightly modified ggData. This post corrects things.
I don't know what the cause is, but the extent argument in your render_path() call is wrong. You can see this if you try plotting straight lines at known latitudes or longitudes. The grid lines are at latitudes -45, 0, 45 and longitudes -120, -60, 0, 60, 120. If you plot paths along those lines, the latitude paths are okay, but the longitude paths are wrong. For example, using this path:
render_path(lat = c(-45, 45), long = c(-60, -60),
zscale=50, color="red", antialias=TRUE, linewidth=3,
extent=st_bbox(world), altitude=unlist(alts))
gives this on the map:
with the path drawn too far west, and too long. If I change the extent argument using
bbox <- st_bbox(world)
bbox[c(1,3)] <- 1.2*bbox[c(1,3)]
bbox[c(2,4)] <- 1.1*bbox[c(2,4)]
it looks better. I found those numbers by trial and error; you (or someone else) will have to do some research if you want to explain them. It's still not perfect, so your path still misses Berlin and Cancun, but not by as much.

eqscplot equivalent in ggplot2 for maps (geom_sf & coord_sf)

I'm looking for a ggplot2 equivalent for eqscplot (package MASS) that can fill the entire plot window while maintaining the scale within a map in a ggplot.
There is coord_fixed where cartesian coordinates can be set with fixed ratios (argument aspect =), but it doesn't work if I'm using geom_sf(); geom_sf() requires coord_sf for a plot to be drawn and can't be a ggplot layer if coord_sf is a layer.
There is this answer (ggplot2: Flip axes and maintain aspect ratio of data) but it's not automatic, which is a problem when there are a lot of maps to be made and where automated map making is the ideal workflow. It also doesn't zoom into the points like eqscplot does.
eqscplot is able to maintain an aspect ratio when the range of x and y values are widely different, which is helpful for plotting maps.
library(rnaturalearth)
library(rnaturalearthdata)
library(MASS)
library(ggplot2)
world <- ne_countries(scale = 'medium', returnclass = 'sf',
country = c('India', 'Sri Lanka', 'Pakistan', 'Myanmar',
'Malaysia', 'Indonesia', 'Thailand',
'Nepal', 'Bhutan', 'Laos', 'China', 'Iran',
'Maldives'))
RNGkind('Mers')
set.seed(42)
lonlat <- data.frame(lon = rnorm(10, 80, 5),
lat = rnorm(10, 12, 5))
apply(lonlat, 2, range)
lon lat
[1,] 77.17651 9.343545
[2,] 90.09212 14.286645
eqscplot(lonlat$lon, lonlat$lat, type = 'n')
plot(world, add = T, col = NA)
points(lonlat$lon, lonlat$lat, col = 'red', pch = 16)
#Compare the lon/lat ranges on the map with the ranges of the data's longitude and latitude
I was was looking to see if there was a way to do it ggplot2, but if I don't specify the limits of the longitude and latitude, it takes the extent of the countries I specified in the world object.
ggplot(data = world) +
geom_sf() +
geom_point(data = lonlat, aes(x = lon, y = lat))
I've got a few hundred maps to make so automating the process with apply family of functions or for loops is preferred. But I need something that can automatically zoom into the points that I'm plotting while maintaining the the aspect ratio. I'm looking to do it in ggplot2 because my collaborators aren't as experienced with R and were taught how to use the tidyverse, so anything I can do to make the code easier for them to understand can go a long way.
My question is: Is there an eqscplot equivalent in ggplot2? Something where the aspect ratio can be set automatically, zooms into where the points are instead of the extent of the world polygon, and fills the entire plot window while maintaining the scales?
Using geom_sf it looks like the plots will adjust to show the extent of the largest object. You can use st_crop to adjust the data that is too large. It might help to make all of your objects sf collections, rather than using both geom_sf and geom_point.
Below is a quick example that you can adjust to change the bounding box of the maps. A good post by #Jindra-Lacko on adjusting bounding boxes can be found here: https://www.jla-data.net/eng/adjusting-bounding-box-of-a-tmap-map/
Though most of it is using tmap, it should apply to ggplot2 as well.
library(rnaturalearth)
library(rnaturalearthdata)
library(MASS)
library(ggplot2)
library(tidyverse)
library(sf)
#> Linking to GEOS 3.6.2, GDAL 2.2.3, PROJ 4.9.3
world <- ne_countries(scale = 'medium', returnclass = 'sf',
country = c('India', 'Sri Lanka', 'Pakistan', 'Myanmar',
'Malaysia', 'Indonesia', 'Thailand',
'Nepal', 'Bhutan', 'Laos', 'China', 'Iran',
'Maldives'))
RNGkind('Mers')
set.seed(42)
lonlat <- data.frame(lon = rnorm(10, 80, 5),
lat = rnorm(10, 12, 5))
#apply(lonlat, 2, range)
# lon lat
# [1,] 77.17651 9.343545
# [2,] 90.09212 14.286645
# eqscplot(lonlat$lon, lonlat$lat, type = 'n')
# plot(world, add = T, col = NA)
#points(lonlat$lon, lonlat$lat, col = 'red', pch = 16)
#Compare the lon/lat ranges on the map with the ranges of the data's longitude and latitude
#make lonlat an sf object
lonlat_sf <- st_as_sf(lonlat,
coords = c('lon', 'lat')) %>%
st_set_crs(st_crs(world))
ggplot() +
geom_sf(data = lonlat_sf, color = 'red') +
geom_sf(data = world %>% st_crop(lonlat_sf, world ), fill = NA) +
coord_sf()
#> although coordinates are longitude/latitude, st_intersection assumes that they are planar
#> Warning: attribute variables are assumed to be spatially constant throughout all
#> geometries
Created on 2020-04-14 by the reprex package (v0.3.0)
When I have this issue with different aspect ratios. I create a buffer for the output dimension i want and save using the same scales similar to below..
library(buffers)
#Find bbox and bbox_centroid
bbox <- st_as_sfc(st_bbox(df))
bbox_centroid<-st_centroid(bbox)
#Calculate Max distance from centroid
max_dist=as.numeric(max(st_distance(bbox_centroid,bbox)))
#Create buffer (In this case I wanted a rectangular image output)
#Notice I'm using a 1.5 ratio for x_length to y_length, 3/2=1.5)
rectangle_buf=buffer_rectangle(bbox_centroid,x_length=max_dist*2,y_length=max_dist*3)
#Find bbox minumum and maximum X and Y values
bbox_rect<-st_as_sf(rectangle_buf)
bbox<-st_bbox(bbox_rect)
#Create ggplot2 image and save with correct dimensions
ggplot(df)+geom_sf()+
#Important part of code
coord_sf(xlim = c(bbox[1], bbox[3]), ylim = c(bbox[2], bbox[4]), expand = T)
##Save ggplot with correct dimensions (In my case this is would be a 1 to 1.5 ratio)
ggsave(plot=last_plot(),width=3,height= 3* 1.5)

Issue plotting (simple) polygons with leaflet in r

I'd like to use leaflet to help me plot heatmap polygons over the UK, but leaflet()%>%addPolygons(...) is giving me weird output, even for a very simple case.
I'm using the following code to plot a simple triangle in leaflet and then ggplot2. ggplot2 has no issues, but leaflet will sometimes just give me a straight line instead of a triangle.
The leaflet code:
l <- leaflet()
shapes <- unique(df$shape)
for (shape in shapes) {
d <- df[df$shape == shape, , drop = FALSE]
l <- l %>% addPolygons(lat = d$lat, lng = d$lng,noClip=TRUE)
}
l
The geom_polygon code:
qplot(lng, lat, data=df, geom="polygon", group = shape)
If I use this input, then I get sensible output from both pacakges:
df <- data.frame(lat = c(1, 2, 3), lng = c(2, 3, 1), shape = c("triangle", "triangle", "triangle"))
However, using even a simple modification leads to a simple horizontal line in leaflet (but a correct triangle in ggplot2):
df <- data.frame(lat = c(100, 200, 300), lng = c(200, 300, 100), shape = c("triangle", "triangle", "triangle"))
It sounds to me like I'm missing a parameter or something, but I can't for the life of my figure out what's going on. Any help appreciated.
Thanks,
Tom
Apparently leaflet only deals with long/lat, even if there is no base maps.
Starting with a shape file (with Eastings & Northings), the following will read in a shapefile, convert to longlat and then successfully plot the polygons using leaflet:
shapefile <- readOGR("filepath_here", "shapefile_here")
shapeData <- spTransform(shapefile, CRS("+proj=longlat +datum=WGS84 +no_defs"))
leaflet() %>%
addPolygons(data=subset(shapeData, POSTAREA %in% c('SW')),weight=2)

How to change the colour of raster data when combining raster layer and ggmap

I'm currently learning something on raster layer and I could put a raster layer on a ggmap using inset_raster(). Here is a sample code below:
library(ggmap)
library(animation)
auckland <- get_map(location = "Auckland",
zoom = 14,
scale = 2, ## 1280*1280 pixels
maptype = "roadmap",
color = "color")
auckland_vis <- ggmap(ggmap = auckland)
auckland_vis
rainbow <- matrix(seq(360, 2000, length = 50 * 50), nrow = 50)
rainbow[sample(1:length(rainbow), 2400, replace = FALSE)] <- NA
rainbow.r <- raster(rainbow)
## inset_raster is used to put a raster layer on a ggmap
auckland_vis +
inset_raster(rainbow.r, xmin = attributes(auckland)$bb$ll.lon,
xmax = attributes(auckland)$bb$ur.lon,
ymin = attributes(auckland)$bb$ll.lat,
ymax = attributes(auckland)$bb$ur.lat
)
Please ignore whether the points on the map is reasonable or not. I know how to change the colour of a raster data when just using plot(). I would also like to know how to change the colour of the points in ggmap. For now the colours are always white, pink, yellow and green(what if I want the colours to be blue yellow and red). Is there a function that can specify colour just like the one in ggplot?
Thanks in advance

Plot coordinates on map

I am trying to plot my coordinates using R. I have tried already to follow different post (R: Plot grouped coordinates on world map ; Plotting coordinates of multiple points at google map in R) but I am not having much success with my data.
I am trying to achieve a flat map of the world with my gps coordinate as colored dots (each area a specific color):
area lat long
Agullhas -38,31 40,96
Polar -57,59 76,51
Tasmanian -39,47 108,93
library(RgoogleMaps)
lat <- c(-38.31, -35.50) #define our map's ylim
lon <- c(40.96,37.50) #define our map's xlim
center = c(mean(lat), mean(lon)) #tell what point to center on
zoom <- 2 #zoom: 1 = furthest out (entire globe), larger numbers = closer in
terrmap <- GetMap(center=center, zoom=zoom, maptype= "satallite", destfile = "satallite.png")
problem that now I don't know how to add my points and I will like one color for each region.
Could anyone help me going forward with it?
the other option I have tried is :
library(maps)
library(mapdata)
library(maptools)
map(database= "world", ylim=c(-38.31, -35.5), xlim=c(40.96, 37.5), col="grey80", fill=TRUE, projection="gilbert", orientation= c(90,0,225))
lon <- c(-38.31, -35.5) #fake longitude vector
lat <- c(40.96, 37.5) #fake latitude vector
coord <- mapproject(lon, lat, proj="gilbert", orientation=c(90, 0, 225)) #convert points to projected lat/long
points(coord, pch=20, cex=1.2, col="red") #plot converted points
but the coordinates ends in a wrong position and I am not sure why
Hope someone can help
As an alternative to RgoogleMaps, you can also use the combination ggplot2 with ggmap.
With this code:
# loading the required packages
library(ggplot2)
library(ggmap)
# creating a sample data.frame with your lat/lon points
lon <- c(-38.31,-35.5)
lat <- c(40.96, 37.5)
df <- as.data.frame(cbind(lon,lat))
# getting the map
mapgilbert <- get_map(location = c(lon = mean(df$lon), lat = mean(df$lat)), zoom = 4,
maptype = "satellite", scale = 2)
# plotting the map with some points on it
ggmap(mapgilbert) +
geom_point(data = df, aes(x = lon, y = lat, fill = "red", alpha = 0.8), size = 5, shape = 21) +
guides(fill=FALSE, alpha=FALSE, size=FALSE)
you get this result:
Another option is using the leaflet package (as suggested here). Unlike Google Maps it does not require any API key.
install.packages(c("leaflet", "sp"))
library(sp)
library(leaflet)
df <- data.frame(longitude = runif(10, -97.365268, -97.356546),
latitude = runif(10, 32.706071, 32.712210))
coordinates(df) <- ~longitude+latitude
leaflet(df) %>% addMarkers() %>% addTiles()
An other alternative, is the plotGoogleMaps package which allows to plot in a navigator, allowing to zoom in and out etc. You can then make a screenshot of your picture to save it (though remember google maps are legally supposed to be used for the internet).
library("plotGoogleMaps")
lat <- c(-38.31, -35.50) #define our map's ylim
lon <- c(40.96,37.50) #define our map's xlim
# make your coordinates a data frame
coords <- as.data.frame(cbind(lon=lon,lat=lat))
# make it a spatial object by defining its coordinates in a reference system
coordinates(coords) <- ~lat+lon
# you also need a reference system, the following should be a fine default
proj4string(coords) <- CRS("+init=epsg:4326")
# Note: it is a short for:
CRS("+init=epsg:4326")
> CRS arguments:
> +init=epsg:4326 +proj=longlat +datum=WGS84 +no_defs +ellps=WGS84 +towgs84=0,0,0
# then just plot
a <- plotGoogleMaps(coords)
# here `a <-` avoids that you get flooded by the html version of what you plot
And you get :
Here is a solution using only Rgooglemaps, as requested by the user.
# get map (from askers OP, except changed map type = "Satallite" to type = "Satellite")
library(RgoogleMaps)
lat <- c(-38.31, -35.50) #define our map's ylim
lon <- c(40.96,37.50) #define our map's xlim
center = c(mean(lat), mean(lon)) #tell what point to center on
zoom <- 2 #zoom: 1 = furthest out (entire globe), larger numbers = closer in
terrmap <- GetMap(center=center, zoom=zoom, type= "satellite", destfile = "satellite.png")
# plot points and save image
lat <- c(-38.31, -57.59, -39.47)
lon <- c(40.96, 76.51, 108.93)
png('map.png')
PlotOnStaticMap(terrmap, lat = lat, lon = lon, pch = 20, col = c('red', 'blue', 'green'))
dev.off()

Resources