I am using R to draw a map of China and the USA. I would like to map the "holistic thinking" across these two countries. Could you help me with the followings?
relocate these two countries? There is too much space between the two countries. I would like to narrow the space between the two countries and zoom the size of the two countries.
smaller the size of "Alaska"? I think the size of Alaska is too big relative to the size of the USA.
This is my code.
holistic_10_plot1 <- st_read(" two_triad_current ") %>%
ggplot() +
geom_sf(aes(fill = holistic_10_province)) +
scale_fill_gradient(low = "lightblue", high = "darkblue") +
geom_sf_text(aes(label = province_current), color = "grey", family = "sans", angle = 0) +
theme_void() +
labs(title = "The number of holistic groups(10 focal groups) \n current states/provinces") +
theme(legend.position = "bottom", plot.title = element_text(hjust = 0.5), title = element_text(size = 14, face = "bold", color = "black")) +
guides(fill = guide_colorbar(title = "The number of the holistic groups", title.position = "top"))
This is the plot.
enter image description here
to relocate the positon of China or the USA to make them closer.
You can shift an sf object simply by recalculating its geometry column like so:
library(sf)
## create example sf object "nc":
nc <- st_read(system.file("shape/nc.shp", package="sf"))
add another geometry column (or change it in place):
library(sf)
## shift geometry .1 degrees east and .1 degrees north:
nc$shifted_geometry <- nc |> st_geometry() + c(.1, -.1)
plot original and shifted geometry:
nc |> st_geometry() |> plot()
nc$shifted_geometry |> plot(add = TRUE, border = 'red')
Concerning the area, you could set an equal-area projection with st_transform().
Related
I'd like to limit a plot based on a polygon defined in geojson, so that it only shows the area shaded blue here.
i.e. just plot the features inside and including the ring road.
The geojson is available here.
It would also be great to add a buffer around the edge to include the ring road.
My code to draw all the features (unlimited by the geojson is below).
library(tidyverse)
library(osmdata)
bounding_box <- getbb("Birmingham", featuretype = "city")
streets <- bounding_box %>%
opq()%>%
add_osm_feature(key = "highway",
value = c("motorway", "trunk", "primary", "secondary", "tertiary")) %>%
osmdata_sf()
ggplot() +
geom_sf(data = streets$osm_lines,
inherit.aes = FALSE,
color = "grey",
size = 1) +
theme_void() +
theme(
plot.background = element_rect(fill = "white"),
legend.position = "none"
) +
coord_sf(xlim = c(-1.933, -1.869),
ylim = c(52.46, 52.496),
expand = FALSE)
I assume in the following that the object streets has already been defined by running the first few lines of the code in the question. The next step is then to read the polygon using read_sf() from the sf package. The next line converts to a more suitable coordinate system (OSGB 1936 / British National Grid) because adding a buffer in meters is not possible in lon/lat-coordinates. A buffer of 40 meters is added using st_buffer() and finally the coordinates are transformed back to WGS84:
library(sf)
area <- read_sf("~/Birmingham CAZ 2020.GeoJSON") %>%
st_transform(27700) %>%
st_buffer(units::set_units(40, m)) %>%
st_transform(4326)
Of course, you need to adapt the path to where you have actually stored the file. Then I use st_intersection() to extract the part of streets$osm_lines that lies inside the polygon:
streets_area <- st_intersection(poly, streets$osm_lines)
And finally I produce the plot using the code from your question. Note that I have added a layer with the polygon in the second line in order to demonstrate that the streets indeed lie inside the polygon:
ggplot() +
geom_sf(data = area) +
geom_sf(data = streets_area,
inherit.aes = FALSE,
color = "grey",
size = 1) +
theme_void() +
theme(
plot.background = element_rect(fill = "white"),
legend.position = "none"
) +
coord_sf(xlim = c(-1.933, -1.869),
ylim = c(52.46, 52.496),
expand = FALSE)
I am now plotting the map of Canada using ggplot2. Because the default projection method is "aea"(Albers Equal Area), so the longitude and latitude are straight lines on the map. I wonder how I can display the longitude and latitude in the form of "110W, 100W, 90W" and "50N, 60N, 70N" on the map. They should be curves. Thanks a lot.
The arcgis shapfile is downloaded from https://www.arcgis.com/home/item.html?id=dcbcdf86939548af81efbd2d732336db
library(ggplot2)
library(rgdal)
countries<-readOGR("Canada.shp", layer="Canada")
ggplot()+geom_polygon(data=countries,aes(x=long,y=lat,group=group),fill='white',color = "black")
The final result should be like this.
You can do this with the coord_map argument of ggplot documented here
This uses projections to alter the coordinate grid. Curved lines would include equal-distance projections, but you should look here for a list of all projections allowed. Which one you choose is a manner of preference.
Using azequidistant (I think this is the Azimuthal equidistant projection), and adding labels manually:
axis_labels <- rbind(
data.frame(long = rep(-140,5),lat = seq(40,80,10), labels = seq(40,80,10)), # x axis labels
data.frame(long = seq(-140,-60,40),lat = rep(85,3), labels = seq(140,60,-40)) # y axis labels
)
ggplot() +
geom_polygon(data=countries,aes(x=long,y=lat,group=group),fill='white',color = "black") +
coord_map("azequidistant") +
scale_x_continuous(breaks = seq(-140,60, by = 20))+
scale_y_continuous(breaks = seq(40,80, by = 10)) +
geom_text(data = axis_labels, aes(x = long, y = lat, label = labels)) +
theme_bw() +
theme(panel.grid.major = element_line(colour = "grey"),
panel.border = element_blank(),
axis.text = element_blank())
You can use a separate graticule layer of spatial data, which you then project based on your Canada layer.
You can find free graticule layers for download at NaturalEarthData.
countries<-readOGR("Canada.shp", layer="Canada")
grat <- readOGR("graticule.shp", layer="graticule")
grat_prj <- spTransform(grat, CRS(countries))
ggplot() +
geom_polygon(data=countries, aes(x=long,y=lat,group=group),fill='white',color = "black") +
geom_path(data=grat_prj, aes(long, lat, group=group, fill=NULL), linetype="solid", color="grey50")
I am trying to create an image similar to that presented by Ricardo Bion of Airbnb but I would like to plot the visualization over the NASA "black marble" image to give more context as I don't have nearly the data density of the Airbnb dataset.
I downloaded the Nasa black marble image here using the global map 13500x6750 (3km) GeoTIFF 39 MB option.
This issue I keep running into is most of the options and explanations available online have been depreciated in the past few years. I tried using EBImage as shown here but ebimageGrob has been removed from gridExtra. I also tried to use the rasterVis package as shown here but the code breaks at the colorable step.
Here is as far as I have made it trying to layer the tiff behind the plot using the ggplot2 annotation_raster option (this gives the lines between the destinations but only a white background):
library(ggplot2)
library(ggmap)
library(sp)
library(grid)
library(geosphere)
library(plyr)
library(tiff)
# source the theme_map for ggplot2
# source("https://dl.dropboxusercontent.com/u/2364714/theme_map.R")
# in the original post I had a data.frame with 500k rows of top origin destination pairs
trips <- data.frame(origin = c("San Francisco", "Sydney", "Chicago"),
destination = c("Paris", "Tokyo", "Boston"),
stringsAsFactors = FALSE)
# get lat and lon of cities
trips$geocode_origin <- suppressMessages(geocode(trips$origin))
trips$geocode_destination <- suppressMessages(geocode(trips$destination))
# get intermediate points between the two locations
arch <- gcIntermediate(trips$geocode_origin,
trips$geocode_destination,
n=100,
breakAtDateLine=FALSE,
addStartEnd=TRUE, sp=TRUE)
# http://docs.ggplot2.org/0.9.3.1/fortify.map.html
arch_fortified <- ldply(arch#lines, fortify)
earth <- readTIFF("~/Downloads/dnb_land_ocean_ice.2012.13500x6750_geo.tif")
theme_map <- function(base_size = 12) {
require(grid)
theme_grey(base_size) %+replace%
theme(
axis.title = element_blank(),
axis.text = element_blank(),
panel.grid = element_blank(),
axis.ticks.length = unit(0,"cm"),
panel.margin = unit(0,"lines"),
plot.margin = unit(c(0,0,0,0),"lines"),
complete = TRUE,
panel.background = element_rect(fill = NA, colour=NA)
)
}
# a few lines of ggplot2 code
ggplot() +
geom_line(aes(long,lat,group=group), data=arch_fortified, alpha=0.1,size=1, colour="skyblue1") +
coord_cartesian(ylim =c(-45, 70), xlim=c(-165, 165)) +
theme_map() +
geom_point(aes(lon, lat),data=trips$geocode_origin, alpha = 0.8, size = 1, colour = "white") +
geom_point(aes(lon, lat),data=trips$geocode_destination, alpha = 0.8, size = 1, colour = "white") +
annotation_raster(earth, -180, 180, -90, 90)
Thanks!
I just had to slightly modify your plotting code to get it work:
ggplot(arch_fortified) +
coord_cartesian(ylim =c(-45, 70), xlim=c(-165, 165)) +
theme_map() +
annotation_raster(earth, -180, 180, -90, 90) +
geom_line(aes(long,lat,group=group), alpha=0.1,size=1, colour="skyblue1") +
geom_point(aes(lon, lat),data=trips$geocode_origin, alpha = 0.8, size = 1, colour = "white") +
geom_point(aes(lon, lat),data=trips$geocode_destination, alpha = 0.8, size = 1, colour = "white")
Note that you should first draw the background and only then the lines and points, otherwise the image will cover other plot elements.
I'm trying to figure out how to display my complete map in gglot2 including the island Both r_base and tmap were able to display the islands but ggplot2 couldn't differentiate the island from the rest of the waterbody...
.
My question is how to make the Islands appear in ggplot2?
See the code i used below.
library(ggplot2)
library (rgdal)
library (rgeos)
library(maptools)
library(tmap)
Loading the Persian Gulf shape fill referred to as iho
PG <- readShapePoly("iho.shp")
the shape file is available here
http://geo.vliz.be:80/geoserver/wfs?request=getfeature&service=wfs&version=1.0.0&typename=MarineRegions:iho&outputformat=SHAPE-ZIP&filter=%3CPropertyIsEqualTo%3E%3CPropertyName%3Eid%3C%2FPropertyName%3E%3CLiteral%3E41%3C%2FLiteral%3E%3C%2FPropertyIsEqualTo%3E
plot with r_base
Q<-plot(PG)
Corresponds to figure A
Ploting with tmap
qtm(PG)
Corresponds to figure B
convert to dataframe
AG <- fortify(PG)
Plot with ggplot2
ggplot()+ geom_polygon(data=AG, aes(long, lat, group = group),
colour = alpha("darkred", 1/2), size = 0.7, fill = 'skyblue', alpha = .3)
Corresponds to figure C
You need to tell ggplot you want the holes filled in with a different color..for example:
ggplot()+ geom_polygon(data=AG, aes(long, lat, group = group, fill = hole), colour = alpha("darkred", 1/2), size = 0.7) + scale_fill_manual(values = c("skyblue", "white")) + theme(legend.position="none")
Also try readOGR() function from the rgdal package instead of readShapePoly() it keeps all the projection and datum information when you read the shape file.
Further to #AdamMccurdy's answer:, there are some possibilities to get the same colour for islands and adjacent background.
The first sets the fill colour of the islands and the colour of the background to be the same. But the grid lines are under the polygon, and thus disappear.
The second is an attempt to get the grid lines back. It plots the background (which includes the grid lines) on top of the panel (using panel.ontop = TRUE). But it's a bit of a fiddle adjusting alpha values to get the same background and island colour.
The third sets the background and island colours to be the same (as in the first), then plots the grid lines on top of the panel. There's a couple of ways to do this; here, I grab the grid lines grob from the original plot, then draw them on top of the panel. Thus the colours remain the same, and no need for alpha transparencies.
library(ggplot2)
library (rgdal)
library (rgeos)
library(maptools)
PG <- readOGR("iho.shp", layer = "iho")
AG <- fortify(PG)
Method 1
bg = "grey92"
ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", bg)) +
theme(panel.background = element_rect(fill = bg),
legend.position = "none")
Method 2
ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", "grey97")) +
theme(panel.background = element_rect(fill = alpha("grey85", .5)),
panel.ontop = TRUE,
legend.position = "none")
Method 3
Minor edit updating to ggplot version 3.0.0
library(grid)
bg <- "grey92"
p <- ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", bg)) +
theme(panel.background = element_rect(fill = bg),
legend.position = "none")
# Get the ggplot grob
g <- ggplotGrob(p)
# Get the Grid lines
grill <- g[7,5]$grobs[[1]]$children[[1]]
# grill includes the grey background. Remove it.
grill$children[[1]] <- nullGrob()
# Draw the plot, and move to the panel viewport
p
downViewport("panel.7-5-7-5")
# Draw the edited grill on top of the panel
grid.draw(grill)
upViewport(0)
But this version might be a little more robust to changes to ggplot
library(grid)
bg <- "grey92"
p <- ggplot() +
geom_polygon(data = AG, aes(long, lat, group = group, fill = hole),
colour = alpha("darkred", 1/2), size = 0.7) +
scale_fill_manual(values = c("skyblue", bg)) +
theme(panel.background = element_rect(fill = bg),
legend.position = "none")
# Get the ggplot grob
g <- ggplotGrob(p)
# Get the Grid lines
grill <- getGrob(grid.force(g), gPath("grill"), grep = TRUE)
# grill includes the grey background. Remove it.
grill = removeGrob(grill, gPath("background"), grep = TRUE)
# Get the name of the viewport containing the panel grob.
# The names of the viewports are the same as the names of the grobs.
# It is easier to select panel's name from the grobs' names
names = grid.ls(grid.force(g))$name
match = grep("panel.\\d", names, value = TRUE)
# Draw the plot, and move to the panel viewport
grid.newpage(); grid.draw(g)
downViewport(match)
# Draw the edited grill on top of the panel
grid.draw(grill)
upViewport(0)
I am attempting to make a GIF of several different maps, showing how crime moves across the city at different times in the day. In order to do this, I am making several different stat_density2d() plots, one plot for each time interval. Here's the code:
data <- read.csv(".../CrimeLocationAndTime.csv",
stringsAsFactors = FALSE)
# Get base map layer
denver = c(lon = -104.9903, lat = 39.7392)
denver_map = get_map(location = denver, zoom = 13, color = "bw")
# Get data slices
twoAMTimeSlice <- data[data$time == "2:00 AM",]
tenAMTimeSlice <- data[data$time == "10:00 AM",]
# Create density map
ggmap(denver_map, extent = "panel", maprange=FALSE) +
stat_density2d(data = twoAMTimeSlice,
aes(x = longitude, y = latitude, fill = ..level.., alpha = ..level..),
size = 0.1, bins = 16, geom = 'polygon') +
scale_fill_gradient(low = "green", high = "red", limits=c(0,2000)) + # Color scale
scale_alpha(range = c(0.1, 0.4), guide = "legend") + # Here is the alpha scale
geom_text(label = twoAMTimeSlice$time[1], x = -104.95, y=39.775) +
theme(plot.title = element_text(size = rel(1.5), vjust = 1),
axis.title = element_blank(),
text = element_text(size = 12)) +
labs(title = "Criminal Occurrences by Time of Day")
So here's my problem: I need my alpha scale to be consistent across all of my maps. Currently, if I make the graphs across different times with different amounts of crime, the alpha scale does not stay consistent. This is apparent in these pictures:
Observe how the green color in this picture is very transparent and the red is more opaque. This is the correct scale that I would like to apply to all maps.
Here, observe how the green is very opaque. You can also see the legend change from the last picture. This is bad. I would like the green in this picture to be just as opaque as the last one.
I have achieved this consistency of scale for the colors, using the limits argument to scale_fill_gradient. However, this argument gives wonky results when applied to scale_alpha.
The solution was changing the limits to
limits=c(0, 2000)
(answered by OP in comments)