Plotting OpenStreetMap with ggmap - r

I'm trying to get districts of Warsaw and draw them on google map. Using this code, where 2536107 is relation code for OpenStreetMap single Warsaw district, gives me almost what I want but with a few bugs. There is general outline but also lines between points which shouldn't be connected. What am I doing wrong?
map <- get_googlemap('warsaw', zoom =10)
warszawa <- get_osm(relation(2536107), full = T)
warszawa.sp <- as_sp(warszawa, what='lines')
warsawfort <- fortify(warszawa.sp)
mapa_polski <- ggmap(map, extent='device', legend="bottomleft")
warsawfort2 <- geom_polygon(aes(x = long, y = lat),
data = warsawfort, fill="blue", colour="black",
alpha=0.0, size = 0.3)
base <- mapa_polski + warsawfort2
base
Edit: I figured it must be somehow connected with order of plotting every point/line but I have no idea how to fix this.

There is a way to generate your map without using external packages: don't use osmar...
This link, to the excellent Mapzen website, provides a set of shapefiles of administrative areas in Poland. If you download and unzip it, you will see a shapfile set called warsaw.osm-admin.*. This is a polygon shapefile of all the districts in Poland, conveniantly indexed by osm_id(!!). The code below assumes you have downloaded the file and unzipped it into the "directory with your shapefiles".
library(ggmap)
library(ggplot2)
library(rgdal)
setwd(" <directory with your shapefiles> ")
pol <- readOGR(dsn=".",layer="warsaw.osm-admin")
spp <- pol[pol$osm_id==-2536107,]
wgs.84 <- "+proj=longlat +datum=WGS84"
spp <- spTransform(spp,CRS(wgs.84))
map <- get_googlemap('warsaw', zoom =10)
spp.df <- fortify(spp)
ggmap(map, extent='device', legend="bottomleft") +
geom_polygon(data = spp.df, aes(x = long, y=lat, group=group),
fill="blue", alpha=0.2) +
geom_path(data=spp.df, aes(x=long, y=lat, group=group),
color="gray50", size=0.3)
Two nuances: (1) The osm IDs are stored as negative numbers, so you have to use, e.g.,
spp <- pol[pol$osm_id==-2536107,]
to extract the relevant district, and (2) the shapefile is not projected in WGS84 (long/lat). So we have to reproject it using:
spp <- spTransform(spp,CRS(wgs.84))
The reason osmar doesn't work is that the paths are in the wrong order. Your warszawa.sp is a SpatialLinesDataframe, made up of a set of paths (12 in your case), each of which is made up of a set of line segments. When you use fortify(...) on this, ggplot tries to combine them into a single sequence of points. But since the paths are not in convex order, ggplot tries, for example, to connect a path that ends in the northeast, to a path the begins in the southwest. This is why you're getting all the extra lines. You can see this by coloring the segments:
xx=coordinates(warszawa.sp)
colors=rainbow(11)
plot(t(bbox(warszawa.sp)))
lapply(1:11,function(i)lines(xx[[i]][[1]],col=colors[i],lwd=2))
The colors are in "rainbow" order (red, orange, yellow, green, etc.). Clearly, the lines are not in that order.
EDIT Response to #ako's comment.
There is a way to "fix" the SpatialLines object, but it's not trivial. The function gPolygonize(...) in the rgeos package will take a list of SpatialLines and convert to a SpatialPolygons object, which can be used in ggplot with fortify(...). One huge problem (which I don't understand, frankly), is that OP's warszaw.sp object has 12 lines, two of which seem to be duplicates - this causes gPolygonize(...) to fail. So if you create a SpatialLines list with just the first 11 paths, you can convert warszawa.sp to a polygon. This is not general however, as I can't predict how or if it would work with other SpatialLines objects converted from osm. Here's the code, which leads to the same map as above.
library(rgeos)
coords <- coordinates(warszawa.sp)
sll <- lapply(coords[1:11],function(x) SpatialLines(list(Lines(list(Line(x[[1]])),ID=1))))
spp <- gPolygonize(sll)
spp.df <- fortify(spp)
ggmap(map, extent='device', legend="bottomleft") +
geom_polygon(data = spp.df, aes(x = long, y=lat, group=group),
fill="blue", alpha=0.2) +
geom_path(data=spp.df, aes(x=long, y=lat, group=group),
color="gray50", size=0.3)

I am not sure this is a general hangup--I can reproduce your example and see the issue. My first thought was that you didn't supply group=id which are typically used for polygons with many lines, but you have lines, so that should not be needed.
The only way I could get it to display properly was by changing your lines into a polygon off script. Qgis' line to polygon didn't get this "right", getting a large donut hole, so I used ArcMap, which produced a full polygon. If this is a one off that may work for your workflow. Odds are it is not. In that case, perhaps RGDAL can transform lines to polygons, assuming that is indeed a general problem.
Upon reading the polygon shapefile and fortifying that, your code ran without problems.

Related

Heat Map in ggplot(), getting all the layers plotted

I am making a heat map of flooding incidents in the UK. I am following the example listed here. However, I am using a different base map from the example and it won't show up on the map. For my base map I use shapefiles provided by the UK gov, found here, and named it uk.shp, an sf object. Flooding data is proprietary and I cannot share but the original format is polygon shapefile. I then turn those shapefiles into gridded points so I can plot a continuous heat map, this sf object is named pt.shp.
Here is the the base map, original shapefile, and gridded points overlaid for context. You can see there are many floods here, often laying under the same point. I constructed a grid sf object that repeats a given point and uses flood ID as the unique identifier. Below is an example of the data with proprietary information removed. One possible issue I can think of is st_intersection returned the lat and long of the flooding shapefile (which I set as the mapping aes()) but the point's mapping coords are listed in the geometry column of the data.
However, when I use stat_density2d() with my base map, the continuous plot disappears. Below is my plotting code w/ each mapping iteration.
# base map plots
base <- ggplot()+
geom_sf(data=uk.shp)
# Plot density of the points
ggplot()+
stat_density2d(data=pt.shp, aes(x=long, y=lat, fill = ..density..), geom='tile', contour = F)
# base map shows up w/o density map? But legend exists so it is being mapped...
base +
stat_density2d(data=pt.shp, aes(x=long, y=lat, fill = ..density..), geom='tile', contour = F, alpha = .5) +
viridis::scale_fill_viridis(option='inferno')
Some issues are clear, like the plotting window of the heatmap has different dimensions than the base map. However, my main issues is I can't overlay the two plots and I don't know why.

ggplot, ggsave & coord_map/quickmap: how to save large spatial objects and get the projection right?

I have a largish polyline shapefile (Bavarian rivers, which can be accessed here) which I would like to plot and save via ggplot. This can easily be done via e.g. this code:
library(ggplot2)
library(rgdal)
library(sp)
library(rgeos)
riv <- readOGR(paste0(getwd(),"\\rivers_bavaria","rivers_bavaria"))
riv1 <- subset(riv,WDM=="1310"|WDM=="1320")
riv2 <- subset(riv,WDM=="1330")
p <- ggplot() +
geom_line(data=riv1, aes(x=long, y=lat, group=group), color="dodgerblue", size=1) +
geom_line(data=riv2, aes(x=long, y=lat, group=group), color="dodgerblue")
ggsave(paste0(getwd(),"\\riv.tiff",p,device="tiff",units="cm",dpi=300)
This is not exactly efficient, due to the large file size, but it works. However, without further specifying aspect ratio or projection, the dimensions of the output file are defined by the plot window - not desirable for maps. This can be remedied by using coord_quickmap().
p1 <- ggplot() +
geom_line(data=riv1, aes(x=long, y=lat, group=group), color="dodgerblue", size=1) +
geom_line(data=riv2, aes(x=long, y=lat, group=group), color="dodgerblue") +
coord_quickmap()
p1
Unfortunately, the projection is completely off. I have tried coord_map() for a better result, but due to the large file size, it takes forever and is therefore not a realistic option. Simplifying the polyline via gLinemerge() produces a much smaller object, but cannot be handled by ggplot, as it is a SpatialLines object. Using fortify() or data.frame() to coerce it into a ggplot-friendly data frame format also produces Error: ggplot2 doesn't know how to deal with data of class SpatialLines.
I'm therefore desperately looking for a workflow that will allow me to plot and save this kind of spatial data in good quality with ggplot. Any suggestions will be much appreciated!
Here's a quick walkthrough with sf. I recommend the sf vignettes and docs to see more details of any of the functions. I'm first reading the shapefile in as an sf object using sf::st_read, then filtering, mutating, and selecting the same as you would in dplyr to get a smaller version of the shape.
library(tidyverse)
library(sf)
rivers_sf <- st_read("rivers_bavaria/rivers_bavaria.shp") %>%
filter(WDM %in% c("1310", "1320", "1330")) %>%
mutate(name2 = ifelse(WDM == "1330", "river 2", "river 1")) %>%
select(name2, NAM, geometry)
The object is pretty big, and will be very slow to plot, so I simplified it by uniting the geometries by name, then using st_simplify. There's also rmapshaper::ms_simplify, which uses Mapshaper and which I prefer for better control over how much information you keep. Then to show a CRS transformation, I picked a projection from Spatial Reference for Germany.
riv_simple <- rivers_sf %>%
group_by(name2, NAM) %>%
summarise(geometry = st_union(geometry)) %>%
ungroup() %>%
st_simplify(preserveTopology = T, dTolerance = 1e6) %>%
st_transform(31493)
The dev version of ggplot2 on GitHub has a function geom_sf for plotting different types of sf objects. To get this version, run devtools::install_github("tidyverse/ggplot2").
geom_sf has some quirks, and works a little differently from other geoms, but it's pretty versatile. I believe it's being included in the next CRAN release. geom_sf has corresponding stat_sf and coord_sf. By default, it plots graticule lines; to turn those off, add coord_sf(ndiscr = F).
ggplot(riv_simple) +
geom_sf(aes(size = name2), color = "dodgerblue", show.legend = "line") +
scale_size_manual(values = c("river 1" = 1, "river 2" = 0.5)) +
theme_minimal() +
coord_sf(ndiscr = F)
Hope that helps you get started!

Bizarre polygons from a shapefile

I'm trying to apply a shapefile to a ggmaps map, but it's giving me really weird results. The shapefile in question is the "Statistical Local Area" (groups similar to postcode) shapefile from the Australian Bureau of Statistics available here.
Normally I might think that it's a problem of cut off edge points, but I'm hitting it even at zoom level 1 (in fact it looks even worse):
Here's some code I used to produce the charts above:
library(tidyverse)
library(ggmap)
library(rgdal)
slas <- readOGR(dsn="SLA",layer="SLA11aAust")
aus4 <- get_map("Australia",zoom=4)
ggmap(aus4)
ggmap(aus4)+
geom_polygon(data=slas, aes(x=long,y=lat))
aus1 <- get_map("Australia",zoom=1)
ggmap(aus1)
ggmap(aus1)+
geom_polygon(data=slas, aes(x=long,y=lat))
Am I doing something wrong, or is the shapefile incorrectly configured somehow?
I think you just need to (optionally) fortify the variable slas, don't forget to group and make the boundaries visible with a color:
slas <- fortify(slas, region = "SLA_CODE11")
ggmap(aus4) +
geom_polygon(data = slas2, color = "white", aes(x = long, y = lat, group = group))

using topoJSON in ggplot

I'm trying to plot (using ggplot) a topoJSON file I generated from https://pitchinteractiveinc.github.io/tilegrams/.
I used the code below to try to plot the example npr 1-to-1 data:
library(rgeos)
library(rgdal)
library(ggplot2)
library(dplyr)
map = readOGR("data/npr.json", "tiles")
map_df <- fortify(map)
gg = ggplot(data = map_df, aes(long,lat, group=group))
gg = gg + geom_polygon(colour="gray65", size=1.0)
print(gg)
The result is not right.
I've tried plotting this with geom_map, and tried adding coord_equal and coord_map without impact.
I also tried to plot a single polygon and got the image below. Perhaps it suggests that points of the polygon are in incorrect order? Anyone have an idea on how to correct?
Actually, the issues seems to be in the conversion done by the readOGR. I imported the json manually and extract the polygons and it worked fine.

maps r plot distances with color on these

perhaps you have an idea and could help me. I have following data:
lon.x <- c(11.581981, 13.404954, 9.993682, 7.842104 , 11.741185)
lat.x <- c(48.135125, 52.520007, 53.551085, 47.999008, 48.402880)
lon.y <- c(8.801694, 7.842104 , 11.581981, 13.404954, 7.842104 )
lat.y <- c(53.079296,47.999008, 48.135125, 52.520007, 47.999008)
pred <- c(1,2,3,4,5)
data <- data.frame(cbind(lon.x, lat.x, lon.y, lat.y, pred))
where "lon.x" and "lat.x" are longitude-latitude points of a city and "lon.y" and "lat.y" of another city. So there are pairs of cities.
Now, I would like to make a map in R, with
(1) the direct distances between the x and y coordinates as a line
(2) which will receive a different color based on the variable "pred", this could be red for higher values and blue for lower, or thicker lines with higher values of "pred".
The result should be a simple map, with lines between the cities, that are shaped based on the variable "pred". For instance, the line between the first pair of cities would be thinner, while the last one would be thicker. Is that possible?
I have currently only made to receive a (very complicated) google map of Germany:
library(mapproj)
map <- get_map(location = 'Germany', zoom = 6.2)
ggmap(map)
But I am not sure how to plot points and especially relations between the points that differ based on "pred". Also a very simple map (not so detailed google map) would be best! Any idea? THANKS!
You can use ggplot2 to add lines onto the plot.
library(ggplot2)
library(ggmap)
map <- get_map(location = 'Germany', zoom = 6)
ggmap(map) +
geom_segment(data=data, aes(x=lon.x, xend=lon.y, y=lat.x, yend=lat.y, color=pred), size=2) +
scale_color_continuous(high="red", low="blue")
As for the simpler map, you can download shape files (just the outlines of countries) from www.gadm.org. Level 0 maps are just the country, level 1 have state boundaries, etc. To use one of these, download the file from the website and use this code:
load("DEU_adm0.RData")
gadm <- fortify(gadm)
ggplot(gadm) +
geom_path(aes(x=long, y=lat, group=group)) +
geom_segment(data=data, aes(x=lon.x, xend=lon.y, y=lat.x, yend=lat.y, color=pred), size=2) +
scale_color_continuous(high="red", low="blue")

Resources