I am attempting to plot several shapefiles on top of a map generated through ggmap. This is working well, however I want to constrain the view area to the shapefile (and not rely on the zoom argument in ggmaps). I've done this by getting the bounding box and passing it as an argument in ggplot's coord_cartesian While this works, I am getting some tearing issues on the edges of the map - most specifically on the western portion. I've tried adjusting the x-y coordinates manually but it seems to only severely distort the picture.
My thoughts are to zoom out slightly to allow the entire shapefile to be plotted in the area, but I can't seem to figure it out. It's also possible I am going about this entirely in the wrong way.
Here's the code I used to generate the map. The shapefile can be downloaded here
library(dplyr)
library(ggmap)
library(rgdal)
library(broom)
# Read in shapefile, project into long-lat
# Create 'tbox' which is a minimum bounding box around the shapefile
tracts <- readOGR(dsn = ".", layer = "CensusTracts2010") %>%
spTransform("+proj=longlat +ellps=WGS84")
tbox <- bbox(tracts)
# Plot data
tract_plot <- tidy(tracts)
DetroitMap <- qmap("Detroit", zoom = 11)
DetroitMap + geom_polygon(data = tract_plot, aes(x = long, y = lat, group = id), color = "black", fill = NA) +
coord_cartesian(xlim = c(tbox[1,1], tbox[1,2]),
ylim = c(tbox[2,1], tbox[2,2]))
I followed your workflow, which resulted in the same problem as you mentioned above. Then I changed the zoom on the qmap option from 11 to 10 and it resulted in a much better picture, although you do lose some of the place names but you can add those in manually yourself with annotate:
DetroitMap <- qmap("Detroit", zoom = 10)
DetroitMap + geom_polygon(data = tract_plot, aes(x = long, y = lat, group = id), color = "black", fill = NA) +
coord_cartesian(xlim = c(tbox[1,1], tbox[1,2]),
ylim = c(tbox[2,1], tbox[2,2]))
Related
I am trying to create a simple map using ggplot2 to display a series of river basins and data points.
The data points are stored in a .csv file and look like this:
I've projected the points and created a SpatialPointDataFrame and then stored them in a dataframe:
points <- read.csv(c(paste0("./Points/", "points.csv")), header = T, sep = ",")
coordinates(points) <- ~Lat + Lon
cord <- "+init=epsg:2163"
proj4string(points) <- CRS(cord)
points_df <- as.data.frame(points, region = "id")
The basin files are all stored in the same directory and have been read into a single spatialpolygonsdataframe which looks like this:
then are converted to a dataframe using this:
basins_TX$id <- rownames(basins#data)
basins_TX_df <- tidy(basins, region = "id")
Then I try to create a map using this code:
ggplot() +
geom_polygon(data = basins_df,
aes(x = long, y = lat, group = group),
fill = "white", color = "black") +
geom_point(data = points_df,
aes(x = Lon, y = Lat),
color = "red")
this creates a map with polygons and one point at 0,0:
This happens no matter what subsection of the data I am looking at. I want to accurately display the points and the polygons.
The issue was in fact related to the projection of my data. I had defined the projection of the points file rather than projecting it, so it was incorrect. I was able to correct this by redefining it correctly and the projecting the points file using spTransform
leaving this up in case other run into similar issues, although the error was not in the ggplot2 script.
A few lines of code to expose my problem. When I work with a map of
the world and I introduce a projection, I always end up with some
weird looking horizontal lines.
Please have a look at
https://www.rdocumentation.org/packages/ggplot2/versions/1.0.0/topics/coord_map
from where I take the example for New Zeland
library(ggplot2)
nz <- map_data("nz")
# Prepare a map of NZ
nzmap <- ggplot(nz, aes(x = long, y = lat, group = group)) +
geom_polygon(fill = "white", colour = "black")
# Plot it in cartesian coordinates
nzmap
# With correct mercator projection
nzmap + coord_map()
which works beautifully. Now let us do the same with the world
world <- map_data("world")
# Prepare a map of the world
worldmap <- ggplot(world, aes(x = long, y = lat, group = group)) +
geom_polygon(fill = "white", colour = "black")
# Plot it in cartesian coordinates
worldmap
##but the following is a disaster!
# With correct mercator projection
worldmap + coord_map()
I see this issue of the horizontal lines with a projection has been
going on for quite a while, but I was able to find only seasoned posts
and I had assumed this was fixed long ago.
Please find below my sessionInfo.
Is there any solution to this? Is it still an open bug?
This is a pretty common problem in ggplot, but happily it is easily fixed:
worldmap + coord_map(xlim=c(-180,180))
produces
solution from: Why does coord_map produce a weird output?
So I am plotting crime levels onto London, however the sizes and colours are not proportionate to the figures I am using. I will leave the code and graphic below, if anyone can give me an explanation as to why this is happening i would really appreciate it. Thanks
London <- c(lon=-0.1278, lat=51.5074)
London_map <- get_googlemap(center = London, zoom = 10)
plot(London_map)
LondonRob <- read.csv("Rob London.csv")
Areas <- as.character(LondonRob$ï..Borough)
locs_geo <- geocode(Areas)
df <- cbind(LondonRob, locs_geo)
Map <- ggmap(London_map) +
geom_point(data = df,
aes(x = lon, y = lat,
size = LondonRob$X2012.13,
color = LondonRob$X2012.13))
Map1 <- Map + labs(color="Robbery")
Map1 <- Map1 + labs(size="Figures")
Map1
scale_size_continuous, which is the default when mapping a continuous variable to size, uses the range argument to determine the scale. By default, this is c(1, 6), and ggplot will translate the variation in the X2012.13 variable to a range from size 1 to size 6.
In many scenarios, with points, you don't want this default. Instead you want to able to compare the sizes directly, with 0 being no point at all. Then you can use scale_size_area instead.
This has the added benefit the area is scaled instead of the radius, which performs better visually.
In addition, you very likely should not be doing
aes(x = lon, y = lat, size = LondonRob$X2012.13, color = LondonRob$X2012.13)
but rather rather
aes(x = lon, y = lat, size = X2012.13, color = X2012.13)
As a general rule, never use $ inside aes, things can go horribly wrong, without any errors or warnings.
I am trying to figure out how to display a map including the legend with ggmap/ggplot.
I have gotten so far:
library(ggmap)
library(RColorBrewer)
bbox <- c(8.437526,47.328268,8.605915,47.462160)
map.base <- get_map(maptype='toner',source = 'stamen',location = bbox)
ggmap(map.base) +
geom_blank() +
ggtitle("2015-09-21 06:00:00 CEST") +
scale_colour_manual(values = rev(brewer.pal(7,"Spectral")), drop = FALSE)+
scale_size_manual(values=c(1:7), drop = FALSE) +
guides(color=guide_legend(title='Mean Delay [s]'), size = guide_legend(title='Mean Delay [s]'))+
ggsave(file=paste("map_","2015-09-21 060000",".png",sep=""),dpi = 100)
dev.off()
This generates the correct map in the correct bounding box. But even thought I have specified: "scale_colour_manual" and "scale_size_manual" with "drop = FALSE", no legend is appearing. How can I have the legend shown when no data is to be displayed?
The overall intention is to create a single map of a given interval in a time series. Now the problem is that some intervals have no data and so the map is displayed without a scale. If the map does not have a scale the dimensions of the map are different making it impossible to create a movie out of the different maps. That is why I need to be able to create a map WITHOUT data but WITH the legend showing.
Thank you.
Taking Jaap's comment into account, that I have to call a legend in aes I have been able to achieve what I want with following code:
library(ggmap)
library(RColorBrewer)
bbox <- c(8.437526,47.328268,8.605915,47.462160)
map.base <- get_map(maptype='toner',source = 'stamen',location = bbox)
ggmap(map.base) +
geom_point(aes(x=0,y=0, color=cut(0,breaks = c(-Inf,0,60,120,240,300,360,Inf),right = FALSE), size=cut(0,breaks = c(-Inf,0,60,120,240,300,360,Inf),right = FALSE))) +
ggtitle("2015-09-21 06:00:00 CEST") +
scale_colour_manual(values = rev(brewer.pal(7,"Spectral")), drop = FALSE)+
scale_size_manual(values=c(1:7), drop = FALSE) +
guides(color=guide_legend(title='Mean Delay [s]'), size = guide_legend(title='Mean Delay [s]')) +
ggsave(file=paste("map_","2015-09-21 060000",".png",sep=""),dpi = 100)
dev.off()
I know this is not the most elegant way, but it works for now.
I basically make a dummy point outside of the bounding box to be displayed. I then give the point a value, which is cut according to the breaks I want and then colored and sized accordingly. Just remember to put the values of x and y in aes outside of the bounding box.
Better solutions are welcome.
I am having clipping problems when I try to combine ggmap with shape files. The example in Kahle and Wickham (2013: 158) works fine because the raster image from ggmap covers the entire shape file. Below is an example of what happens when I try to plot the shape file for U.S. states on a ggmap plot that covers a smaller area. The ggmap shows New York City and I want to overlay it with the borders for U.S. states (just as an example). The resulting map doesn't make any sense. The problem is that the shape file gets clipped and ggplot connects the unclipped points. Below is the code. The shape file is from here. I am just showing the last plot here.
How can I solve this problem?
path <- "PATH TO SHAPEFILE"
library("ggmap")
library("rgdal")
# shapefile
states <- readOGR(dsn = path, layer = "states")
states_df <- fortify(states)
# plot shapefile
plot(states, lwd = 0.1)
ggplot(states_df, aes(long, lat, group = group)) +
geom_polygon(colour = "black", fill = NA, size = 0.1)
# combine ggmap with shapefile
map <- get_map("new york city", zoom = 10, source = "stamen")
ggmap(map, extent = "device")
ggmap(map, extent = "device") +
geom_polygon(aes(long, lat, group=group), data = states_df, colour = "red", fill = NA, size = 1)
Kahle, David and Hadley Wickham. 2013. “Ggmap: Spatial Visualization with ggplot2.” The R Journal 5(1):144–61.
Here is my attempt. I often use GADM shapefiles, which you can directly import using the raster package. I subsetted the shape file for NY, NJ and CT. You may not have to do this in the end, but it is probably better to reduce the amount of data. When I drew the map, ggplot automatically removed data points which stay outside of the bbox of the ggmap image. Therefore, I did not have to do any additional work. I am not sure which shapefile you used. But, GADM's data seem to work well with ggmap images. Hope this helps you.
library(raster)
library(rgdal)
library(rgeos)
library(ggplot2)
### Get data (shapefile)
us <- getData("GADM", country = "US", level = 1)
### Select NY and NJ
states <- subset(us, NAME_1 %in% c("New York", "New Jersey", "Connecticut"))
### SPDF to DF
map <- fortify(states)
## Get a map
mymap <- get_map("new york city", zoom = 10, source = "stamen")
ggmap(mymap) +
geom_map(data = map, map = map, aes(x = long, y = lat, map_id = id, group = group))
If you just want lines, the following would be what you are after.
ggmap(mymap) +
geom_path(data = map, aes(x = long, y = lat, group = group))
I would check out this answer, it seems that ggmap as you expected doesn't handle polygon's in an ideal way when you zoom in, namely items not on the plot get truncated causing 'interesting' results with respect to the shape files.
Polygons nicely cropping ggplot2/ggmap at different zoom levels
# transform for good measure
states <- spTransform(states,CRS("+datum=WGS84 +proj=longlat") )
# combine ggmap with shapefile
states_df <- fortify(states)
# get your map
map <-get_map("new york city", zoom = 10, source = "stamen")
a <- ggmap(map, # this is where we get our raster
base_layer=ggplot(aes(x=long, y=lat), data=states_df), # this defines the region where things are plotted
extent = "normal", # this won't work with device, you need normal (see examples in ggmap documentation)
maprange=FALSE
) +
coord_map( # use map's bounding box to setup the 'viewport' we want to see
projection="mercator",
xlim= c(attr(map, "bb")$ll.lon, attr(map, "bb")$ur.lon),
ylim=c(attr(map, "bb")$ll.lat, attr(map, "bb")$ur.lat)
) +
geom_polygon( # plot the polygon
aes(x=long, y=lat,group=group), data =states_df, color = "red", fill=NA, size = 1)
print(a)
With output:
As a side note you might want to check out using the U.S. Census data for state maps, they seem to be of higher quality than the ESRI data set.
ftp://ftp2.census.gov/geo/pvs/tiger2010st/tl_2010_us_state10.zip
As a final note, there are issues with ggmap near the poles so I would also subset your data by the states you are interested in.