Shapefile not filling properly with geom_polygon, inconsistent with QGIS/ArcMap - r

I've imported a shapefile of the world's oceans from Natural Earth to R via readOGR. When I try to render it in ggplot, it fills in land over N. & S. America. The behaviour is inconsistent with QGIS & ArcMap, both of which render & fill the shapefile just fine. Any ideas?
download.file("https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/50m/physical/ne_50m_ocean.zip" , destfile="./ne_50m_ocean.zip")
system("unzip ./ne_50m_ocean.zip")
wrld <- readOGR(dsn=getwd(),layer="ne_50m_ocean")
wrld <- tidy(wrld)
ggplot() + geom_polygon(data = wrld, aes(x = long, y = lat, group = group), colour = "black", fill = "blue")
screenshot of RStudio render
screenshot of QGIS render

I was able to resolve this using read_sf() instead of readOGR(), then tweaking the ggplot code to accommodate. Also worked for the next bit of my workflow which involved rasterizing the sf object using fasterize() for a mask ocean layer (simplified demo code included in case useful to others):
#get data
download.file("https://www.naturalearthdata.com/http//www.naturalearthdata.com/download/50m/physical/ne_50m_ocean.zip" , destfile="./ne_50m_ocean.zip")
system("unzip ./ne_50m_ocean.zip")
wrld <- read_sf(dsn=getwd(),layer="ne_50m_ocean")
#plot sf object
ggplot() + geom_sf(data=wrld, colour = "black", fill = "blue")
#rasterize sf object
r <- raster(ncol=720, nrow=360)
extent(r) <- extent(wrld)
rp <- fasterize(wrld, r)
ocean <- as.data.frame(rasterToPoints(rp))
#plot raster object
ggplot() + geom_tile(data=ocean,aes(x=x,y=y),fill="white")

Related

st_centroid renders all labels on the same point

I'm trying to display labels on GIS polygon features in R using the st_centroid function in the sf library. Unfortunately, while the head() function seems to show that each polygon has different x and y coordinates associated with it, all labels get rendered overlapping at a single point on the map (which is apparently the centroid of one particular polygon). What am I doing wrong here?
Current code setup:
library("ggplot2")
library("sf")
sf::sf_use_s2(FALSE) #makes centroids not break
world <- st_read("C:/prgrm/gis/source/10m_land_and_islands.shp")
prov <- st_read("C:/prgrm/gis/edited ncm/ncm_provinces.shp")
prov <- cbind(prov, st_coordinates(st_centroid(prov))) #attaches centroids to 'prov' dataset
head(prov)
ggplot(data = world) +
geom_sf() +
geom_sf(data=prov, aes(fill="blue")) +
geom_text(data=prov, aes(X,Y, label=provname_r), size=5) +
coord_sf(xlim=c(-2000000,1000000),ylim=c(-1500000, 3000000), crs=st_crs(3310))
You may be better off with specifying the centroid placement via fun.geometry argument of the geom_sf_text() call / by the way the default is sf::st_point_on_surface() - which is a good default as it makes sure that the label is not placed inside a hole, should the polygon have one.
Consider this example, using the well known & much loved nc.shp shapefile that ships with {sf}.
library(sf)
library(ggplot2)
# in place of your world dataset
shape <- st_read(system.file("shape/nc.shp", package="sf")) # included with sf package
# in place of your prov dataset
ashe <- shape[1, ]
ggplot(data = shape) +
geom_sf() +
geom_sf(data = ashe, fill = "blue") +
geom_sf_text(data = ashe,
aes(label = NAME),
color = "red",
fun.geometry = st_centroid)

how map certain USDA hardiness zones in R

Has anyone been able to create maps of a selection of USDA hardiness zones in R, maybe with ggplot2 and sf packages? I'd specifically like to create a map with only zones 9b and higher in color .
I think some of the data to create the map is found here Prism Climate Group, but I am inexperienced and at a loss to know what to do with GIS data (file extensions SGML,XML,DBF, PRJ, SHP,SHX).
To elaborate a little bit on the answer by #niloc:
The USA looks more natural when shown in the Albers conical projection (Canada border slightly curved - like in the original image).
This can be achieved by using coord_sf(crs = 5070) in your {ggplot2} call.
The gist of the answer (downloading, unzipping & plotting via ggplot2::geom_sf()) remains unchanged).
library(sf)
library(tidyverse)
library(USAboundaries)
# Download and unzip file
temp_shapefile <- tempfile()
download.file('http://prism.oregonstate.edu/projects/public/phm/phm_us_shp.zip', temp_shapefile)
unzip(temp_shapefile)
# Read full shapefile
shp_hardness <- read_sf('phm_us_shp.shp')
# Subset to zones 9b and higher
shp_hardness_subset <- shp_hardness %>%
filter(str_detect(ZONE, '9b|10a|10b|11a|11b'))
# state boundaries for context
usa <- us_boundaries(type="state", resolution = "low") %>%
filter(!state_abbr %in% c("PR", "AK", "HI")) # lower 48 only
# Plot it
ggplot() +
geom_sf(data = shp_hardness_subset, aes(fill = ZONE)) +
geom_sf(data = usa, color = 'black', fill = NA) +
coord_sf(crs = 5070) +
theme_void() # remove lat/long grid lines
There is a lot going on in that map with all of the insets, the legend with F and C, states displayed over the CONUS. Would be better to narrow down your question.
But here is a start. The shapefile is composed of many files (XML, DBF, etc) but you only need to point read_sf() at the .shp file. Subsetting with an sf object can be done just like with a data.frame.
library(sf)
library(tidyverse)
# Download and unzip file
temp_shapefile <- tempfile()
download.file('http://prism.oregonstate.edu/projects/public/phm/phm_us_shp.zip', temp_shapefile)
unzip(temp_shapefile)
# Read full shapefile
shp_hardness <- read_sf('phm_us_shp.shp')
# Subset to zones 9b and higher
shp_hardness_subset <- shp_hardness %>%
filter(str_detect(ZONE, '9b|10a|10b|11a|11b'))
# Plot it
ggplot() +
geom_sf(data = shp_hardness_subset, aes(fill = ZONE)) +
geom_polygon(data = map_data("state"), # add states for context
aes(x=long, y=lat,group=group),
color = 'black',
fill = NA) +
theme_void() # remove lat/long grid lines

ggplot2 issue with plotting points and polygons

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.

Plotting roads from an osmar object on a ggplot map

I have created an elevation map from a raster object (elevation data from worldclim) of my study sites in China, using ggplot code (simplified version of the code). The relevant raster objects have been downloaded from worldclim.org and converted to a data.frame using the raster package. Here is a link to the data used for this plot.
# load library
library("tidyverse")
load(file = "gongga.RData")
ggplot() +
geom_raster(data = gongga, aes(x=x, y=y, fill = elev)) +
coord_equal() +
scale_fill_gradient(name = "Elevation", low = "grey0", high = "grey100") +
scale_x_continuous(expand = c(0,0)) +
scale_y_continuous(expand = c(0,0)) +
theme(aspect.ratio=1/1, text = element_text(size=15))
For clarity I would like to add roads to the map. I came across the osmar package that extracts roads from Openstreetmap.
Using code from here, I extract the roads for the right section, but I don't know how to plot them to my existing ggplot.
# EXTRACT ROADS FROM OPENSTREETMAP AND PLOT THEM WITH RANDOM POINTS
# Load libraries
library('osmar')
library('geosphere')
# Define the spatial extend of the OSM data we want to retrieve
moxi.box <- center_bbox(center_lon = 102.025, center_lat = 29.875,
width = 10000, height = 10000)
# Download all osm data inside this area
api <- osmsource_api()
moxi <- get_osm(moxi.box, source = api)
# Find highways
ways <- find(moxi, way(tags(k == "highway")))
ways <- find_down(moxi, way(ways))
ways <- subset(moxi, ids = ways)
# SpatialLinesDataFrame object
hw_lines <- as_sp(ways, "lines")
# Plot points
plot(hw_lines, xlab = "Lon", ylab = "Lat")
box()
Does the object need any transformation to plot it in ggplot?
Or is there a better solution than osmar package for my purpose?
You can fortify the SpatialLinesDataFrame and then plot that with ggplot
fortify(hw_lines) %>%
ggplot(aes(x = long, y = lat, group = group)) +
geom_path()
The group aesthetic stops ggplot from joining all the roads together into one long line.

Plotting Shapefile on ggmap

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]))

Resources