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)
I want to generate 12 maps using ggplot and facet_wrap which illustrate ocean temperature across the Scotian shelf from Jan-Dec. I was given a ".csv" file with 25,000 observations, which has monthly temperature data from 2016-2018 with the associated latitude and longitude values. I have tried using geom_raster to map this data but I get the following error message
Error: cannot allocate vector of size 39.7 Gb
In addition: Warning messages:
1: In f(...) :
Raster pixels are placed at uneven horizontal intervals and will be shifted. Consider using geom_tile() instead.
2: In f(...) :
Raster pixels are placed at uneven vertical intervals and will be shifted. Consider using geom_tile() instead
So of course, I have also tried using geom_tile but when I run the code I get a blank map with no color even though I have specified both fill and color. Here is some sample data similar to my data frame, please note that my real data for latitude and longitude values are not evenly spaced by seq(...,...,0.1) (I'm not sure how to make sample data without the sequencing, sorry) so you won't get the same error as me, and the code will work
#In my data frame the lat and long values are not equally spaced!
mapoc_temp = expand.grid(data.frame(Longitude= seq(-64.5,-62.4,0.1),
Latitude= seq(42.7,44.8,0.1),
year = sample(c(2016,2017,2018), 22, replace = T),
month = sample(month.abb, 22, replace = T)))
mapoc_temp$Temp = runif(nrow(mapoc_temp))
Here is my code for geom_raster which gives me the error I mentioned
library(mapdata)
library(ggplot2)
#map I'm using for ggplot
Canada = map_data("wildfires", "Canada")
ggplot(mapoc_temp, aes(x=Longitude, y=Latitude)) +
#try to map my temp on the ocean
geom_raster(aes(fill = Temp, x = Longitude), interpolate = TRUE) +
geom_polygon(data = Canada, aes(x=long, y=lat, group=group), colour="grey50", fill = 'grey55')+
coord_sf(xlim=c(-64.5,-62.8), ylim=c(42.7,45)) +
#get months
facet_wrap(vars(month))
here is the code when I try geom_tile, again I noticed with my sample data it works, but with my real data it doesn't and I believe it has something to do with my coordinates not being equal distances apart.
ggplot(mapoc_temp, aes(x=Longitude, y=Latitude)) +
geom_tile(aes(fill = Temp, x = Longitude, y = Latitude), colour = mapoc_temp$Temp) +
geom_polygon(data = canada, aes(x=long, y=lat, group=group), colour="grey50", fill = 'grey55')+
coord_sf(xlim=c(-64.5,-62.8), ylim=c(42.7,45)) +
facet_wrap(vars(month))
here is the picture I get with geom_tile
and here is roughly what I am trying to produce, but obviously, 12 of these maps because I want a map for each month, with better resolution (if possible, but at this point, I'll take any map colored with my temperature data).
I have a feeling I'll have to do something more along the lines with this type of code (a snippet of something I found a while ago), but I have tried manipulating this with no success. Any suggestions?
#my CRS value!
latlong = "+proj=longlat +datum=NAD83 +no_defs +ellps=GRS80 +towgs84=0,0,0"
#for transforming
planar ="+proj=utm +zone=20 +datum=NAD83 +units=km +no_defs +ellps=GRS80 +towgs84=0,0,0"
out <- x%>%
st_as_sf(coords=c("Longitude","Latitude"),crs=latlong)%>%
st_transform(planar)%>%
st_coordinates()%>%
raster::rasterize(.,grid,field=xyz[,3],fun=rasterFun)%>%
raster::projectRaster(.,crs=latlong)%>%
raster::rasterToPolygons(.)%>% # this part is slow
st_as_sf()%>%
rename(MAP = layer)
You have sort of answered your own question with the example data you offered. The geom_raster and geom_tile functions have limits to their use. A big one is that because they require the data to be on an even grid, with very high res station data the machine will try to create an even grid before plotting, hence the error of needing 39.7 GB of memory. By rounding your lon/lat values to an even 0.1x0.1 grid the machine is now able to plot the data in the other answers in this post. Try running the following code chunk on your data.
mapoc_temp <- mapoc_temp %>%
ungroup() %>%
mutate(Longitude = plyr::round_any(Longitude, 0.1),
Latitude = plyr::round_any(Latitude, 0.1))
You may change the level of rounding as you prefer, but avoid going too fine scale unless it is absolutely necessary. Once your data have been rounded to an even grid you can plot them with the following chunk.
ggplot(mapoc_temp, aes(x = Longitude, y = Latitude)) +
borders(fill = "grey80") +
geom_raster(aes(fill = Temp)) +
coord_quickmap(xlim = c(-64.5,-62.8), ylim = c(42.7,45)) +
facet_wrap(~month)
There is no need to create any sf shape polygons or add any CRS information to the plot. The tidyverse is not designed to utilise this information and in my opinion it only complicates things unnecessarily to bring in the sf package. I hope this helps!
It looks like the geom_polygon(mapping layer) was overplotting the geom_tile layer. Below is an example using your example data with geom_tile called last, and with alpha set to 0.4 to show the map beneath.
#In my dataframe the lat and long values are not equally spaced!
mapoc_temp = expand.grid(data.frame(Longitude= seq(-64.5,-62.4,0.1),
Latitude= seq(42.7,44.8,0.1),
year = sample(c(2016,2017,2018), 22, replace = T),
month = sample(month.abb, 22, replace = T)))
mapoc_temp$Temp = runif(nrow(mapoc_temp))
library(mapdata)
#> Loading required package: maps
library(ggplot2)
#map I'm using for ggplot
canada = map_data("worldHires", "Canada")
## Use geom_raster as the last layer in the ggplot2 call,
## otherwise the polygons plot over the tiles.
## Below alpha is set on the raster layer to show underlying map.
ggplot(mapoc_temp, aes(x=Longitude, y=Latitude)) +
#try to map my temp on ocean
geom_polygon(data = canada, aes(x=long, y=lat, group=group), colour="grey50", fill = 'grey55')+
geom_raster(aes(fill = Temp, x = Longitude),alpha = .4, interpolate = TRUE) +
coord_sf(xlim=c(-64.5,-62.8), ylim=c(42.7,45)) +
#get months
facet_wrap(vars(month))
Created on 2020-02-22 by the reprex package (v0.3.0)
You can also set fill= NA within your geom_polygon function if you have that layer on top of the geom_raster
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?
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]))