I've problem with projecting properly map of Europe when I use coord_map in ggplot package. The code below gives me a weird and unwanted horizontal lines in random places. Does anyone know how to overcome this problem?
I don't want to use coord_quickmap nor coord_cartesian because I want to preserve straight lines of countries.
library(ggplot2)
map.world <- map_data("world")
ggplot(map.world, aes(x = long, y = lat)) +
geom_polygon(mapping = aes(x = long, y = lat, group = group), fill = "#B3B1B5", color = "#D9D8DA",size = 0.4) +
theme_minimal() +
theme(axis.text = element_blank(), text = element_blank(), panel.grid = element_blank()) +
coord_map(xlim = c(-27,36), ylim = c(34,67))
There are two ways to work around your problem:
The straightforward way
This way just needs a little tweaking of your code (note that I use magrittr's forward pipes):
library(maps)
library(magrittr)
library(maptools)
library(broom)
library(ggplot2)
europe <- maps::map("world", fill=TRUE, plot=FALSE) %>%
maptools::pruneMap(xlim = c(-27,36), ylim = c(34,67))
ggplot(data= broom::tidy(europe)) +
geom_polygon(mapping = aes(x = long, y = lat, group = group),
fill = "#B3B1B5", color = "#D9D8DA",size = 0.4) +
theme_void() +
coord_map()
The "owin/extent" approach
Another way to work around your problem can be done by using owin objects. This kind of objects will allow you to create a spatial window. Then you can represent only the intersection of such window over the world map.
Using this approach your code will be something as follows (also using magrittr's forward pipes and setting up a general CRS)
library(maps)
library(magrittr)
library(spatstat)
library(maptools)
library(raster)
library(broom)
library(ggplot2)
#Defining a general EPSG so there won't be any superposition problems
epsg <- "+proj=longlat +datum=WGS84 +no_defs"
#Using the original maps package, then converting map into SpatialPolygons object
map.world <- maps::map("world", fill=TRUE) %$%
maptools::map2SpatialPolygons(., IDs=names,proj4string=CRS(epsg))
#In order to keep the names of the countries we create the following data.frame
country.labs <- sapply(slot(map.world, "polygons"), function(x) slot(x, "ID")) %>%
data.frame( ID=1:length(map.world), name=., row.names = .)
#We convert object map.world into a SpatialPolygonsDataFrame object
map.world.SPDF <- sp::SpatialPolygonsDataFrame(map.world, country.labs)
#Creating owin object using your zooming coordinates
#This step always requires to load packages 'spatstat' and 'maptools'
zoom <- as(spatstat::as.owin(c(-27,36,34,67)), "SpatialPolygons")
raster::projection(zoom)=epsg
#Storing intersection between 'zoom' and 'world.map'
europe <- raster::intersect(map.world.SPDF, zoom)
#*country names of object europe can be accessed via europe#data
#Representing object 'europe' using broom::tidy to create a data.frame object
ggplot() +
geom_polygon(data = broom::tidy(europe, region="name"),
mapping = aes(x = long, y = lat, group = group),
fill = "#B3B1B5", color = "#D9D8DA",size = 0.4) +
theme_void() +
coord_map()
#*country names after tidying 'europe' this way are in a new column called 'id'
Depending on what you are doing, you may want to use
zoom <- as(raster::extent(c(-27,36,34,67)), "SpatialPolygons")
To create an extent object instead of an owin object (The result here is going to be the same)
The result obtained with any method is displayed below
In case you need to do different zooms you can easily wrap any of the alternatives up in a function.
I hope it helps
BONUS BALL: It might be interesting for you to check out the tmap package
Related
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 am using sf and ggplot2 to read shapefiles as simple features and plot various maps. I have been working through the maps chapter in the ggplot2 book but could not really find an answer to the following issue:
Plotting a map using geom_sf and labelling its features with geom_sf_text is a pretty straightforward task.
library(ggplot2)
library(sf)
library(ozmaps)
oz_states <- ozmaps::ozmap_states
ggplot() +
geom_sf(data = oz_states) +
geom_sf_text(data = oz_states, aes(label = NAME))
Once we zoom in on a section of the previous map, not all labels of the features present in the plot are visible.
xlim <- c(120.0, 140.0)
ylim <- c(-40, -24)
ggplot() +
geom_sf(data = oz_states) +
geom_sf_text(data = oz_states, aes(label = NAME)) +
coord_sf(xlim = xlim, ylim = ylim)
I have found a workaround to zoom in on sections of the map and still be able to label the features present in the plot by calculating the centroids of the features, extracting the coordinates as separate columns, selecting the elements I would like to be displayed in the final map, and using ggrepel to label them.
library(dplyr)
library(ggrepel)
oz_states_labels <- oz_states %>% st_centroid()
oz_states_labels <- do.call(rbind, st_geometry(oz_states_labels)) %>%
as_tibble() %>%
rename(x = V1) %>%
rename(y = V2) %>%
cbind(oz_states_labels) %>%
slice(4,5,7,3)
ggplot() +
geom_sf(data = oz_states) +
geom_text_repel(data = oz_states_labels, aes(label = NAME, x = x, y = y)) +
coord_sf(xlim = xlim, ylim = ylim)
Naturally, if possible, I would like to avoid the workaround of first having to calculate the centroids, extract the coordinates from the resulting sf and select the labels to be shown in the final map.
Hence my question: Is there a faster way of labelling all elements visible in the plot for example by specifying this either in geom_sf_text or coord_sf?
Thanks in advance for your tips and answers!
I believe the issue you are facing is caused by your applying the crop at presentation level / the actual data underlying your ggplot object is not cropped.
I suggest applying the crop at data level, for example via sf::st_crop(). In this example I am using the values of your xlim and ylim objects to create a bounding box (called crop_factor for no good reason) to limit the extent of the oz_states at the data level, by creating a new object called oz_cropped & continuing in your original workflow.
All the centroids and labels and what not will be much better behaved now.
library(ggplot2)
library(sf)
library(ozmaps)
oz_states <- ozmaps::ozmap_states
crop_factor <- st_bbox(c(xmin = 120,
xmax = 140,
ymax = -24,
ymin = -40),
crs = st_crs(oz_states))
oz_cropped <- st_crop(oz_states, crop_factor)
ggplot() +
geom_sf(data =oz_cropped) +
geom_sf_text(data = oz_cropped, aes(label = NAME))
I've been having difficulties with a map I created in R. I'm trying to make a map with a grid where in each square, there's a value corresponding to a dataframe. So far, I made the shapefile and the script following some tutorials and posts from here (Download), but the final result got kinda weird.
library(xlsx)
library(ggplot2)
library(sp)
library(raster)
library(plyr)
library(dplyr)
library(tidyr)
library(sp)
library(raster)
library(rgeos)
library(rgbif)
library(viridis)
library(gridExtra)
library(rasterVis)
library(ggplot2)
library(maps)
library(rgdal)
br <- readOGR(choose.files(), "brgrid")
plot(br)
class(br)
str(br#data)
br#data$id <- rownames(br#data)
br.df <- fortify(br)
br.df <- join(br.df, br#data, by="id")
str(br.df)
tail(br.df)
names(br.df)
tail(br.df$id)
dados <- read.xlsx("ptsgrid.xlsx",6)
names(dados)
br.df <- merge(br.df, dados, by.x="id", by.y="id", all.x=T, a..ly=F)
str(br.df)
ggp <- ggplot(data=br.df, aes(x=long, y=lat, group=group))
ggp <- ggp + geom_polygon(aes(fill=value)) # draw polygons
ggp <- ggp + geom_path(color="grey", linestyle=1) # draw boundaries
ggp <- ggp + coord_equal()
ggp <- ggp + scale_fill_gradient(low = "#ffffcc", high = "#ff4444",
space = "Lab", na.value = "grey50",
guide = "colourbar")
print(ggp)
I want to know why the lines got crossed in the map, and how to fix it. Also, I wanna know if it's possible to omit some squares (I only have interest in coastal region, so it'll look better if I omit the rest). And finally, I don't know why the map boundaries got "overlayed" (maybe a projection issue?).
Welcome to Stack Overflow! What have you done so far/what does your code look like? I will share with you a map/code I made that functions as I intended, so you can replace values in my code with what you need for your needs/with your data Below is code for a map I created in R for income in the United States:
library(maps)
library(ggplot2)
usa=map_data("state")
ggplot(usa)+geom_polygon(aes(x=long,y=lat,group=group,fill=region),color="white")+coord_fixed(1.3)+guides(fill=FALSE,color=FALSE)
The result of the above code:
I downloaded your data set. I did not examine your code. But I think when you created br.df using merge(), order got messed up. In your code, I am talking about this line (br.df<-merge(br.df, dados, by.x="id", by.y="id", all.x=T, a..ly=F). You may want to check this question. You wanted to combine all data sets. But you do not have to do that.
In your data set, dados, you have 148 data points. But you have 150 grids in your map data. So I modified your EXCEL data; I added id = 0 and id = 149. Each data point has 0 and 495, respectively. As long as there is a common column name (in this case, id), you can do the following. I used geom_cartogram() in the ggalt package. The second geom_cartogram() is adding colors to the grids.
library(rgdal)
library(ggplot2)
library(ggalt)
library(readxl)
# Create a map data
foo <- readOGR(dsn = "brgrid.shp")
mygrid <- fortify(foo)
# Import the EXCEL data
dados <- read_excel("ptsgrid.xlsx", sheet = 6)
ggplot() +
geom_cartogram(data = mygrid, map = mygrid,
aes(x = long, y = lat, map_id = id),
color = "black", alpha = 0.5) +
geom_cartogram(data = dados, map = mygrid,
aes(fill = value, map_id = id)) +
scale_fill_gradient(low = "#ffffcc", high = "#ff4444",
space = "Lab", na.value = "grey50",
guide = "colourbar")
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.
I'm trying to do some plots in ggplot, which I'm much more comfortable working with than R's built in plotting function. However, with one of the file's I'm using (a SpatialPolygonsDataFrame containing a map of Kenya), when I try to call fortify on the SPDF to convert it to a data.frame, plotting the result yields odd behavior in a particularly jagged region of the border, while plot manages to work fine. Anyone have any ideas as to what's going on?
library(sp)
library(ggplot2)
riso <- "KEN"
query <- paste0("http://biogeo.ucdavis.edu/data/gadm2/R/", riso, "_adm0.RData")
destination <- "myfile.RData"
download.file(query, destination)
load(destination)
plot(gadm)
map <- fortify(gadm)
g <- ggplot() + geom_path(data = map, aes(x = long, y = lat))
g
Just add group = group to the aes() mappings. Wish I could explain why this works.
g <- ggplot() + geom_path(data = map, aes(x = long, y = lat, group = group))
g