I'm getting started with maps in R, and I'm facing a problem which I'm not being able to solve.
Suppose the following script:
tmp_dir = tempdir()
url_data = "http://www.sharegeo.ac.uk/download/10672/50/English%20Government%20Office%20Network%20Regions%20(GOR).zip"
zip_file = sprintf("%s/shpfile.zip", tmp_dir)
download.file(url_data, zip_file)
unzip(zip_file, exdir = tmp_dir)
library(maptools)
gor=readShapeSpatial(sprintf('%s/Regions.shp', tmp_dir))
col=gray(gor$NUMBER/sum(gor$NUMBER))
col[5] = NA
plot(gor, col=col)
I would like a way to add a texture to the state with missing data on the "col" vector, instead of just leaving it white.
So in this case for example, I'm looking for something like that:
How can I add textures to specific parts of my plot, specially when working with maps?
I've read about functions like add.texture, but I couldn't use them in such a flexible way.
plot.SpatialPolygons() is capable of using line texture. If the argument density isn't NA, plot.SpatialPolygons() uses line texture.
: # (skip)
library(maptools)
col=gray(gor$NUMBER/sum(gor$NUMBER))
col[5] = NA
plot(gor, col=col) # It's easy to use the argument `add=T`
plot(gor[5,], add=T, density=10, angle=90, col="blue") # Left map
## Of cource, you can draw the map at once without `add=T`
col2 <- col
col2[4:5] <- c("red", "blue")
plot(gor, col=col2, density=c(rep(NA,3), 30, 10, rep(NA,4)),
angle=c(rep(NA,3), 0, 90, rep(NA,4))) # Right map
Related
Problem:
I want to make a multi-panel figure using PNG or JPEG images. The images were not created in R but I want to patch them together in R to form one figure. All the images are the same size/dimensions.
What I've tried:
library(png)
img1 <- readPNG("filepath/img1.png")
img2 <- readPNG("filepath/img2.png")
library(patchwork)
patch <- img1 + img2
patch
When I run this, I get the following error:
[ reached getOption("max.print") -- omitted 3 matrix slice(s) ]
I increased the max print multiple times (to ridiculously high numbers):
options(maxprint = 1000000000000)
But still get the same error.
I then tried making each image into a ggplot (without the points) using:
library(ggplot2)
img1plot <- ggplot() +
background_image(img1) +
theme(plot.margin = margin(t=1, l=1, r=1, b=1, unit = "cm"))
Which returns the following error:
Error in background_image(d311) :
could not find function "background_image"
Is there another way to patch images together in R to make a figure?
Edit:
Based on the comment from #davidnortes, I tried the following:
p1 <- ggplot2::annotation_custom(grid::rasterGrob(img1,
width=ggplot2::unit(1,"npc"),
height=ggplot2::unit(1,"npc")),
-Inf, Inf, -Inf, Inf)
p2 <- ggplot2::annotation_custom(grid::rasterGrob(img2,
width=ggplot2::unit(1,"npc"),
height=ggplot2::unit(1,"npc")),
-Inf, Inf, -Inf, Inf)
library(cowplot)
plots <- plot_grid(
p1, p2,
labels = c('A', 'B'),
align="hv"
)
plots
I get the following warning messages and the plot doesn't form:
Warning messages:
1: In as_grob.default(plot) :
Cannot convert object of class LayerInstanceLayerggprotogg into a grob.
2: In as_grob.default(plot) :
Cannot convert object of class LayerInstanceLayerggprotogg into a grob.
I'm giving you the couple of alternatives that I Use the most:
Alternative 1: combination of ggplot2, grid and cowplot.
Each of your PNG image can be embedded in a ggplot object using:
ggplot2::ggplot() + ggplot2::annotation_custom(grid::rasterGrob(YourPNGimage,
width=ggplot2::unit(1,"npc"),
height=ggplot2::unit(1,"npc")),
-Inf, Inf, -Inf, Inf)
Then you can use cowplot::plot_grid() to arrange your plots.
Alternative 2: using magick package.
The package counts on its own functions to read images, thus we need to tweak your code a bit:
img1 <- magick::image_read("filepath/img1.png")
img2 <- magick::image_read("filepath/img2.png")
Then using functions like magick::image_append(c(img1, img2)) you can combine them. See the magick package documentation for more info.
You can also rbind image arrays. But as they are 3D (x,y,RGB) you must use abind function from abind package. along=1 to bind vertically, 2 horizontally.
Works because image have same size.
img1 <- readPNG("filepath/img1.png")
img2 <- readPNG("filepath/img2.png")
img12 <- abind::abind(img1,img2,along=1)
png::writePNG(img12,"filepath/img12.png")
You can use magick package in R to do a collage.
# read the the png files into a list
pngfiles <-
list.files(
path = here::here("png_ouput_folder"),
recursive = TRUE,
pattern = "\\.png$",
full.names = T
)
# read images and then create a montage
# tile =2 , means arrange the images in 2 columns
# geometry controls the pixel sixe, spacing between each image in the collage output.
magick::image_read(pngfiles) %>%
magick::image_montage(tile = "2", geometry = "x500+10+5") %>%
magick::image_convert("jpg") %>%
magick::image_write(
format = ".jpg", path = here::here(paste(name,"_collage.jpg",sep="")),
quality = 100
)
As others have suggested the magick package is much simpler than a low-level solutions using grobs and related. magick is powerful but IMHO the documentation is poor and very circular.
However, there is a simple solution to your question in the page for image_montage(). The most important argument is the geometry specification, which governs the spacing between the "tiles."
library(magick)
input <- rep(logo, 12)
image_montage(input, geometry = 'x100+10+10', tile = '4x3', bg = 'pink', shadow = TRUE)
To get no spacing at all, use geometry = '0x100+0+0', shadow = FALSE".
I create a UK map representing some info by downloading an Spatial Polygons Data Frame from GADM.org and the following script.
lat<-c(51.5163,52.4847,51.4544,53.5933,51.481389,51.367778,55.953056,55.864167,51.482778)
lon<-c(-0.061389,-1.89,-2.587778,-2.296389,-3.178889,-0.07,-3.188056,-4.251667,-0.388056)
fr<-c(0.004278509,0.004111901,0.004150415,0.00421649,0.004221205,0.004191472,0.004507773,0.004314193,0.004098154)
uk<-data.frame(cbind(lat,lon,fr))
plotvar<-uk$fr
nclr<-4
plotclr <- brewer.pal(nclr,"Blues")
max.symbol.size=6
min.symbol.size=1
class <- classIntervals(plotvar, nclr, style="quantile")
colcode <- findColours(class, plotclr)
symbol.size <- ((plotvar-min(plotvar))/
(max(plotvar)-min(plotvar))*(max.symbol.size-min.symbol.size)
+min.symbol.size)
windows()
par(mai=c(0,0,0,0))
plot(UnK, col = 'lightgrey', border = 'darkgrey',xlim=c(-6,0),ylim=c(50,60)) #Unk is the map downloaded from GADM
points(uk$lon, uk$lat, col=2, pch=18)
points(uk$lon, uk$lat, pch=16, col=colcode, cex=symbol.size)
points(uk$lon, uk$lat, cex = symbol.size)
text(-120, 46.5, "Area: Frho")
legend(locator(1), legend=names(attr(colcode, "table")),
fill=attr(colcode, "palette"), cex=1, bty="n")
The following figure is the outcome of the above script.
Now, my problem is that I'm not happy with the colors and the breaks of the variable uk$fr. I need to change then in order to be able to compare this map with others, but I dont know how to do the following. My intention is to break this variable in 3 different classes like this (0-0.0125],(0.0125-0.0625],(0.0625-0.125]. And represent this classes by "Blues" and by different sizes circles. Also I want to force the legend to include these three classes.
One last question, how can I put title to the legend?
Thanks.
I'm trying to add this plot of a function defined on Veneto (italian region)
obtained by an image and contour:
image(X,Y,evalmati,col=heat.colors(100), xlab="", ylab="", asp=1,zlim=zlimits,main=title)
contour(X,Y,evalmati,add=T)
(here you can find objects: https://dl.dropboxusercontent.com/u/47720440/bounty.RData)
on a Google Map background.
I tried two ways:
PACKAGE RGoogleMaps
I downloaded the map mbackground
MapVeneto<-GetMap.bbox(lonR=c(10.53,13.18),latR=c(44.7,46.76),size = c(640,640),MINIMUMSIZE=TRUE)
PlotOnStaticMap(MapVeneto)
but i don't know the commands useful to add the plot defined by image and contour to the map
PACKAGE loa
I tried this way:
lat.loa<-NULL
lon.loa<-NULL
z.loa<-NULL
nx=dim(evalmati)[1]
ny=dim(evalmati)[2]
for (i in 1:nx)
{
for (j in 1:ny)
{
if(!is.na(evalmati[i,j]))
{
lon.loa<-c(lon.loa,X[i])
lat.loa<-c(lat.loa,Y[j])
z.loa<-c(z.loa,evalmati[i,j])
}
}
}
GoogleMap(z.loa ~ lat.loa*lon.loa,col.regions=c("red","yellow"),labels=TRUE,contour=TRUE,alpha.regions=list(alpha=.5, alpha=.5),panel=panel.contourplot)
but the plot wasn't like the first one:
in the legend of this plot I have 7 colors, and the plot use only these values. image plot is more accurate.
How can I add image plot to GoogleMaps background?
If the use of a GoogleMap map is not mandatory (e.g. if you only need to visualize the coastline + some depth/altitude information on the map), you could use the package marmap to do what you want. Please note that you will need to install the latest development version of marmap available on github to use readGEBCO.bathy() since the format of the files generated when downloading GEBCO files has been altered recently. The data from the NOAA servers is fine but not very accurate in your region of interest (only one minute resolution vs half a minute for GEBCO). Here is the data from GEBCO I used to produce the map : GEBCO file
library(marmap)
# Get hypsometric and bathymetric data from either NOAA or GEBCO servers
# bath <- getNOAA.bathy(lon1=10, lon2=14, lat1=44, lat2=47, res=1, keep=TRUE)
bath <- readGEBCO.bathy("GEBCO_2014_2D_10.0_44.0_14.0_47.0.nc")
# Create color palettes for sea and land
blues <- c("lightsteelblue4", "lightsteelblue3", "lightsteelblue2", "lightsteelblue1")
greys <- c(grey(0.6), grey(0.93), grey(0.99))
# Plot the hypsometric/bathymetric map
plot(bath, land=T, im=T, lwd=.03, bpal = list(c(0, max(bath), greys), c(min(bath), 0, blues)))
plot(bath, n=1, add=T, lwd=.5) # Add coastline
# Transform your data into a bathy object
rownames(evalmati) <- X
colnames(evalmati) <- Y
class(evalmati) <- "bathy"
# Overlay evalmati on the map
plot(evalmati, land=T, im=T, lwd=.1, bpal=col2alpha(heat.colors(100),.7), add=T, drawlabels=TRUE) # use deep= shallow= step= to adjust contour lines
plot(outline.buffer(evalmati),add=TRUE, n=1) # Outline of the data
# Add cities locations and names
library(maps)
map.cities(country="Italy", label=T, minpop=50000)
Since your evalmati data is now a bathy object, you can adjust its appearance on the map like you would for the map background (adjust the number and width of contour lines, adjust the color gradient, etc). plot.bath() uses both image() and contour() so you should be able to get the same results as when you plot with image(). Please take a look at the help for plot.bathy() and the package vignettes for more examples.
I am not realy inside the subject, but Lovelace, R. "Introduction to visualising spatial data in R" might help you
https://github.com/Robinlovelace/Creating-maps-in-R/raw/master/intro-spatial-rl.pdf From section "Adding base maps to ggplot2 with ggmap" with small changes and data from https://github.com/Robinlovelace/Creating-maps-in-R/archive/master.zip
library(dplyr)
library(ggmap)
library(rgdal)
lnd_sport_wgs84 <- readOGR(dsn = "./Creating-maps-in-R-master/data",
layer = "london_sport") %>%
spTransform(CRS("+init=epsg:4326"))
lnd_wgs84_f <- lnd_sport_wgs84 %>%
fortify(region = "ons_label") %>%
left_join(lnd_sport_wgs84#data,
by = c("id" = "ons_label"))
ggmap(get_map(location = bbox(lnd_sport_wgs84) )) +
geom_polygon(data = lnd_wgs84_f,
aes(x = long, y = lat, group = group, fill = Partic_Per),
alpha = 0.5)
I am a total newbie to R and I would like to draw a line (possibly weighted, e.g., by the number of trips made) between two countries. Currently, I use longitude and latitude for each capital to draw a line, but I would like to do it using the package ggmap. I was looking around, but did not find any solution so far. I would appreciate a quick help.
require(ggmap)
require (rworldmap)
all_content = readLines("ext_lt_intratrd_1_Data.csv")
skip_second = all_content[-2]
dat = read.csv(textConnection(skip_second), header = TRUE, stringsAsFactors =F)
dat[5,2]<- c("Germany") # using a data where the first line is
header, but second line must be skipped as it is EU 27
and not a single country
europe <- read.csv("eulonglat.csv", header = TRUE) # using world capitals to
generate points
myfulldata <- merge(dat, europe)
map <- get_map(location = 'Europe', zoom = 4)
mapPoints <- ggmap(map) + geom_point(aes(x = UNc_longitude, y = UNc_latitude, size
= log(myfulldata$Value)), data = myfulldata, col = "red", alpha= 0.5) # this can
be plotted
# i would continue with drawing line and i searched for references
# i found arrows(42.66,23.34,50.82,4.47) - which did not work
# i tried to look for a reference work more, but could not find
# instead i found it using with the package rworldmap the following
lines(c(4.47, 23.32), c(50.82, 42.66))
# this does not work on ggmap
I am trying to view this binary file(unsigned character, pixel =720 and lines=360) as a map ,I tried the piece of code given bellow. First, I downloaded and saved it as landcover.bin.
conne <- file("C:\\landcover.bin", "rb")
sd<- readBin(conne, integer(), size=1, n=360*720, signed=F)
y<-matrix((data=sd), ncol=360, nrow=720)
image(y)
I got a map that looks weird, and then I swap ncol and nrow as
y<-matrix((data=sd), ncol=720, nrow=360)
image(y)
I got a reasonable map but upside down
My question can anyone tell me how can I display my file as (which is not supposed to be the same)and how to display the longitudes and latitudes as are shown on this map:
Use the raster package for geographically based gridded data.
You can probably make it read directly from a binary file with some rgdal trickery, but lets do it via a matrix.
> require(raster)
> conne <- file("landcover.bin","rb")
> sd<- readBin(conne, integer(), size=1, n=360*720, signed=F)
> y<-t(matrix((data=sd), ncol=360, nrow=720))
> r = raster(y)
Now you have a raster object. But if you plot it you will notice three things - lots of green, the scale going up to 250, and the axes being from 0 to 1.
The map seems to use 255 for the sea. If we recode that as NA we'll get a better map:
> r[r==255]=NA
> plot(r)
Looks a lot better. Now let's fix the range:
> extent(r) = extent(c(xmn=-180,xmx=180,ymn=-90,ymx=90))
> plot(r)
Finally we should tell R that this is in the lat-long coordinate system - most likely epsg:4326:
> projection(r)=CRS("+init=epsg:4326")
> plot(r)
Note this is still using a continuous colour scheme, even though you have discrete data (which I guess is a classification scheme). You can map numbers to colours with raster, but that's a whole other problem...
You have to mirror the columns on the matrix to get the right result. Use abline to draw your lats and longs.
conne <- file("Landcover.bin", "rb")
sd<- readBin(conne, integer(), size=1, n=360*720, signed=F)
y<-matrix(sd,ncol=360,nrow=720)
image(y[,360:1])
lats=seq(-90,90,by=30)
longs=seq(-180,180,by=30)
trans.lats=(lats+90) / 180
trans.longs=(longs+180) / 360
abline(h=trans.lats,v=trans.longs)
But, as #January mentioned, there are many packages that handle maps. You should use one of them.
To get a ggplot2 based solution, you can use geom_raster. See my answer to this earlier question of yours for an example. ggplot2 displays lat lon lines, to tweak this see my answer to this other question of yours, specifically the breaks argument for scale_{x,y}_*.
ggplot2 requires a data.frame, not a matrix or array, use melt to do this transformation (example here). To get the correct lat lon values, please take care that the array has the correct dimnames, please see ?dimnames for more information.
Maybe the maps in the R map package would suit you better?
library( maps )
map( "world" )
points( -0.11832, 51.50939, pch= 19, col= "red" )
text( -0.11832, 51.50939, "London", pos=3, col= "red" )
abline( h= 0 )
text( -150, 0, "Equator", pos= 3 )
abline( v= 0 )
Result: