R: tmap Legend Formatting - r

I am trying to create a function to map different variables for a specific state's school districts. However I a running into some problems formatting the legend. At the moment I have it laid out the best way to fit the maps(horizontally), but some of the text is being cut off (Below Average Poverty Rate), and I'd like to add % to the number labels in the legend. My code and an image of the legend is below. Any help you can provide would be very much appreciated. Thank You.
MakeLEAMap <-function(StateNum,NCHE_VAR,VAR1_NAME,In,Folder){
as.character(substitute(StateNum))
as.character(substitute(NCHE_VAR))
as.character(substitute(NCHE_In))
as.character(substitute(VAR1_NAME))
as.character(substitute(Folder))
map <-
tm_shape(LEA_1415_New[LEA_1415_New#data$STATEFP == StateNum, ]) +
tm_polygons(NCHE_VAR,border.col="#000000", lwd= .5, textNA="Below Average \nPoverty Rate" , palette = 'Blues', style="quantile",
title=paste(In," State LEA Map: ",VAR1_NAME),
legend.is.portrait = FALSE) +
tm_text("LCITY", size=NCHE_VAR,scale=.8, root=2,print.tiny = FALSE, size.lowerbound = .85, bg.alpha = .75,
remove.overlap = TRUE,legend.size.show = FALSE, col="black") +
tm_layout( legend.title.size = 3,
frame = FALSE, inner.margins = c(0,.0,.05,.0), asp = 1.5,
legend.text.size = 1,
legend.outside=TRUE, legend.outside.position = 'bottom',
legend.frame = TRUE,
legend.outside.size = .3, legend.position = c(-0.1, 0.3))
save_tmap(map, filename=paste("State_Maps_TEST/",Folder,"/",In,".pdf", sep = ''),width=8, height=8 )
}
MakeLEAMap("48","Abv_Diff_Home_Pov","% Children in Poverty minus \n% Children HCY (Ages5-17)",
"TX","ALL")
Here is what the legend looks like now

To make the legend show percentages use this function inside your tm_polygons call:
legend.format=list(fun=function(x) paste0(formatC(x, digits=0, format="f"), " %"))
You can play with the digits (decimal points) and you can drop the space before % sign if you desire.
To make the legend more legible increase the space around your map by making a bigger bbox (possibly using extent function from raster package to read bbox of your spatial object and then enlarging it) and move the legend by adjusting its position.
This is what I came up with in a different context, but one which also called for a percentage sign in tmap legend.

Related

Using BFAST to show breakpoints

I am currently working with bfastSpatial, I am attempting to plot breakpoint values as a year colour based legend. I am aware of changeMonth function for plotting monthly based breakpoints (http://www.loicdutrieux.net/bfastSpatial/) however, I am attempting to achieve a similar outcome as in Morrison et al. (2019) https://www.mdpi.com/2072-4292/10/7/1075
Any assistance would be appreciated.
If you want to round the breakpoint dates to integer years, you can use floor, as the dates are in decimal years. Next, to make a similar plot as the one you showed, you can use the tmap package. Since you did not attach any data to the OP, I used the tura data included in the bfastSpatial package.
library(bfastSpatial)
library(tmap)
# Load tura data
data(tura)
# Perform bfast analysis
bfm <- bfmSpatial(tura, start=c(2009, 1), order=1)
# Extract the first band (breakpoints)
change <- bfm[[1]]
# As breakpoints dates are in year decimals,
# you can use floor to round them to lowest integer
change <- floor(change)
# Set shape as change, the object to plot
tm_shape(change) +
# Plot it as raster and set the palette, number of categories,
# style (categorical) and title of the legend.
tm_raster(palette = "Spectral",
n = 5,
style = "cat",
title = "Year") +
# Set the legend's position and eliminate the comma used by default for
# separating thousands values. Add background color and transparency
tm_layout(legend.position = c("right", "bottom"),
legend.format=list(fun=function(x) formatC(x, digits=0, format="d")),
legend.bg.color = "white",
legend.bg.alpha = 0.7) +
# Add scale bar, set position and other arguments
tm_scale_bar(breaks = c(0,0.5,1),
position = c("right", "top"),
bg.color = "white",
bg.alpha = 0.7) +
# Add north arrow with additional parameters
tm_compass(type = "arrow",
position = c("left", "top"),
bg.color = "white",
bg.alpha = 0.7)
The obtained plot:

How to reduce the space between the plot and the border for geographic maps?

I am trying to plot a bathymetry map of the the northeast US using the marmap library. The following code loads the correct extent but when I plot the map I have blank space between the border and the map either at the top/bottom or left/right of the map. This also occurs when exporting the plots. If I drag the plot viewer screen size the plot adjusts and I can remove almost all of the empty space but I will be running this script in a loop so its not practical to solve this problem this way. Because of the loop I also can't hard code any dimensions into the plot because it will change for each new extent. How can I set the border of the plot to match the extent of the bathymetry?
library(marmap)
library(maps)
atl<- getNOAA.bathy(-80.93645,-41.61417,30.2 ,60.905 ,resolution=4)
blues <- colorRampPalette(c("darkblue", "cyan"))
greys <- colorRampPalette(c(grey(0.4),grey(0.99)))
plot(atl, image = TRUE, land = TRUE, n=0,
bpal = list(c(0, max(atl), greys(100)),
c(min(atl), 0, blues(100))))
map(database= "state", col="black", fill=FALSE, add=TRUE)
text(x=state.center$x, y=state.center$y, state.abb, cex=0.5)
This behavior is caused by the asp argument of plot.bathy(). By default, it is fixed as asp = 1 to ensure that the scales on both axes are the same (one degree of longitude equals one degree of latitude). An unwelcome consequence of this default, is the white bands appearing either on the left/right sides of the graph, or on the top/bottom sides depending on the dimensions of your bathymetric map and the plotting device.
So I suppose you have 2 options:
If you don't mind having a slightly distorted perspective, you can set asp = NA in your call to plot.bathy()
If you want to have the correct aspect ratio but need to use the default size for your plotting region, then you have to download a bathymetric region that covers the whole plotting region of your active device. For instance, you could call plot.bathy() once to create a "default" plot, then, use par("usr") to determine the limits of the bathymetry needed to fill the entire plotting area. You would then download a second bathymetry with the appropriate ranges in longitude and latitude. Which is maybe not desirable.
Here is what the code would look like for the second option:
atl <- getNOAA.bathy(-80.93645, -41.61417, 30.2, 60.905, resolution = 4)
blues <- colorRampPalette(c("darkblue", "cyan"))
greys <- colorRampPalette(c(grey(0.4), grey(0.99)))
plot(atl, image = TRUE, land = TRUE, n = 0,
bpal = list(c(0, max(atl), greys(100)),
c(min(atl), 0, blues(100))))
coord <- par("usr")
atl2 <- getNOAA.bathy(coord[1], coord[2], coord[3], coord[4], res = 4)
plot(atl2, image = TRUE, land = TRUE, lwd = 0.2,
bpal = list(c(0, max(atl2), greys(100)),
c(min(atl2), 0, blues(100))))
map(database = "state", col = "black", fill = FALSE, add = TRUE)
text(x = state.center$x, y = state.center$y, state.abb, cex = 0.5)
I suppose the solution proposed by Roman Luštrik works too, but it has the inconvenience of leaving the white bands visible on both sides of the plot.
As an aside, if you have a lot of bathymetric regions to plot, you should maybe consider using the keep = TRUE argument of getNOAA.bathy() to avoid querying the NOAA servers each time you need to re-execute your code (and it is much faster to load local data than remote ones). And you could also download once and for all the global 4Go ETOPO1 and use subset.bathy() to, well, subset the bathymetry you need for each plot.
Here is a proposal using a workaround. The idea is to convert the bathy object into raster object and then make the plot using levelplot from rasterVisthat correctly fits the plotting area to the raster extent. Note that using raster allows having a defined pixel size and, therefore, a correct width/height ratio that you don't seem to have with marmap::plot method.
library(raster)
library(rasterVis)
r <- marmap::as.raster(atl)
state <- map('state', plot = FALSE)
state <- data.frame(lon = state$x, lat = state$y)
state.lab <- data.frame(lon = state.center$x, lat = state.center$y,
label = state.abb)
# you can remove the color legend by adding colorkey = FALSE in levelplot()
levelplot(r,
at = c(seq(min(atl), 0, length.out = 100),
seq(0, max(atl), length.out = 100)[-1]),
col.regions = c(blues(100), greys(100)),
margin = FALSE) +
xyplot(lat ~ lon, state, type = 'l',
col = 'black') +
xyplot(lat ~ lon, data = state.lab,
panel = function(y, x, ...) {
ltext(x = x, y = y, labels = state.lab$label, cex = 0.75)
})

I want to adjust the graph area in ggplot2

I made the following graph using the geomnet package and ggplot2. Then exported it to a pdf. But the graph by itself seems to be larger than the graphing area. it seems to be framed in a small square, as you can see in this picture:
graph
I don't know how to change the size of the square that's framing my graph so that the net nodes will be shown fully in my pdf. Thanks in advance.
Here's the code i'm using, and a data example:
red_list<-data_frame(From=c("A","B","C","D","D"),To=c("C","C","D","Z","A"))
red_list%>%ggplot(aes(from_id=From,to_id=To))+
geom_net(layout.alg = "circle", labelon = TRUE,
size = 12, directed = TRUE, fontsize=2, vjust = 0.5, labelcolour = "grey80",
arrowsize = 1.5, linewidth = 0.5, arrowgap = 0.05, col="darkred",
selfloops = F, ecolour = "grey40") +
theme_net() +
theme(plot.title=element_text(hjust=.5),
plot.subtitle=element_text(hjust=.5))+
ggtitle(label=paste("Figura",i,sep=" "),subtitle = paste("Interacciones entre los sectores de",names(red_list)[i],by=" ")))
I'm not sure how well it will work with graphs, but I uselly play with coord_cartesian(xlim = c(...,...), ylim = c(...,...) to adjust the plotting area.

How to change the legend title and position in a lattice plot

I'm using lsmip from lsmeans to plot my model,
library(lsmeans)
PhWs1 <- lsmip(GausNugget1, Photoperiod:Ws ~ Month,
ylab = "Observed log(number of leaves)", xlab = "Month",
main = "Interaction between Photoperiod and Water stress over the months (3 photoperiods)",
par.settings = list(fontsize = list(text = 15, points = 10)))
but I was not able to get a suggestion on the internet on how to handle the legend position, size, title, etc.
I used trellis.par.get() to see the parameters but I could not find the one related to my issue. As you can see from the graph, the legend should be "Photoperiod*Ws" but Ws is not visible.
I see two possibly complementing alternatives to approach this issue. The first would be to create a fully customized legend and pass it on to the key argument of xyplot (which lsmip is heavily based on). Here is an example taken from ?lsmip to clarify my point.
## default trellis point theme
trellis_points <- trellis.par.get("superpose.symbol")
## create customized key
key <- list(title = "Some legend title", # legend title
cex.title = 1.2,
x = .7, y = .9, # legend position
points = list(col = trellis_points$col[1:2], # points
pch = trellis_points$pch[1:2],
cex = 1.5),
text = list(c("A", "B"), cex = .9)) # text
## create results and extract lattice plot
d <- lsmip(warp.lm, wool ~ tension, plotit = FALSE,
main = "Some figure title", key = key)
p <- attr(d, "lattice")
p
As you can see, setting up a customized legend let's you modify all the different components of the legend - including labels, text and symbol sizes, legend spacing, etc. Have a deeper look at the key argument described in ?xyplot which describes the various modification options in detail.
Now, if you have a long legend title and you do not want to include the legend inside the plot area, you could also define separate viewports, thus allowing the legend to occupy more space at the right margin. Note the use of update to remove the initially created legend from p and the subsequent assembly of the single figure components using grid functionality.
## remove legend from figure
p <- update(p, legend = NULL)
## assemble figure incl. legend
library(grid)
png("plot.png", width = 14, height = 10, units = "cm", res = 300)
grid.newpage()
## add figure without legend
vp0 <- viewport(x = 0, y = 0, width = .75, height = 1,
just = c("left", "bottom"))
pushViewport(vp0)
print(p, newpage = FALSE)
## add legend
upViewport(0)
vp1 <- viewport(x = .7, y = 0, width = .3, height = 1,
just = c("left", "bottom"))
pushViewport(vp1)
draw.key(key, draw = TRUE)
dev.off()

How to position annotate text in the blank area of facet ggplot

How to annotate some text in the blank space within a odd numbered faceted ggplot.
Lets have a faceted ggplot with data as below with with 2 rows and 2 columns. So there is blank space in place of 2 row, 2nd column.
df<- data.frame(Ara = rep("XTX", each = 3),
Len = c(744, 750, 755),
Mon = c("Sep", "Oct","Nov"),
Value=c(11.224,10.15,4.23))
df
facetplot<-ggplot(df, aes(x=Value, y=Len, shape=Ara))+
geom_point(size=5.0)+
theme(legend.position = c(.7, .4), legend.direction="vertical")+
facet_wrap(~Mon,scales="free_x", nrow=2)
facetplot
Now i am trying to annotate some text in the space but could not ( as written in red in the image). I am looking for something similar to legend.position for annotated text. Do anyone has any idea on this.Or what would be the possible work around.
Thank you.
After you create your plot, simply use
print(facetplot)
grid.text("your text", x = 0.75, y = 0.25)
See ?grid.text for details on positioning. The default coordinate system is the entire screen device with (0,0) as the lower left and (1,1) as the upper right.
To modify the graphical parameter setting for grid.text such as font color, family, fontface and size ...
grid.text("your text", x = 0.6, y = 0.15, gp = gpar(col="red", fontsize = 20, family = "Times", fontface = "italic"))

Resources