#' Generate random data for map plotting
#' @export
random_data <- function(n, prefix="")
{
x <- replicate(3, runif(n, -1, 1))
colnames(x) <- c("x", "y", "z")
x <- as.data.frame(x)
#x$label <- words::sentences(nwords, s = n, lang = "en", maxchar = Inf)
ii <- 1L:nrow(x)
x$label <- paste0(prefix, ii)
# x$col <- 1
# x$cex <- 1
x
}
#' 3d cartesians to sperical with fixed radius
#'
#' @keywords internal
#'
cart_to_surface_of_sphere <- function(x, r=1)
{
s <- t(apply(x, 1, cartesian_to_spherical_coords))
colnames(s) <- c("phi", "theta", "r")
as.data.frame(s)
}
#' Add colummns with spherical coordinates
#'
#' @keywords internal
#' @export
add_sphere_coords <- function(x, r=1)
{
xyz <- x[c("x", "y", "z")]
s <- cart_to_surface_of_sphere(xyz, r=r)
cbind_replace(x, s)
}
#' Add projections as user coords based on spherical coords
#'
#' Uses last projection \code{.Last.projection()} to calc projected
#' coordinates. Adds the variables \code{xp} and \code{yp} to dataframe
#' based on spherical coordinates \code{phi} and \code{theta}.
#'
#' @keywords internal
#' @export
add_proj_data <- function(s, proj="", parameters=NULL, orientation=NULL)
{
p <- mapproj::mapproject(s$phi, s$theta, proj=proj,
parameters=NULL, orientation=orientation)
xy <- cbind(xp = p$x, yp = p$y)
cbind_replace(s, xy)
}
#' Set projection and orientation
#'
#' Wrapper for \code{mapproj::mapproject}
#' @export
#'
map_proj <- function(preset=NULL, proj="", parameters=NULL, orientation=NULL)
{
if (!is.null(preset)) {
preset <- match.arg(preset, c("mollweide", "orthographic", "gall", "mercator"))
if (preset == "mollweide") {
proj <- preset
orientation = c(90,0,0)
parameters = NULL # lat0
}
if (preset == "orthographic") {
proj <- preset
orientation = c(0,0,0)
parameters = NULL
}
if (preset == "gall") {
proj <- preset
orientation = NULL
parameters = 0 # lat0
}
if (preset == "mercator") {
proj <- preset
orientation = NULL
parameters = NULL
}
}
mapproj::mapproject(0, 0, proj=proj, parameters=parameters,
orientation=orientation)
invisible(NULL)
}
#' Convert a vector of length three into dataframe with columns x, y, z.
#' @keywords internal
#'
vec_to_xyz_df <- function(x)
{
if (is.vector(x) & length(x) == 3) {
x <- as.data.frame(rbind(x))
colnames(x) <- c("x", "y", "z")
}
x
}
#' Prepares the data for plotting
#'
#' The function needs a dataframe as input. The arguments that are accepted
#' cartesian coordinates \code{x}, \code{y}, \code{z},) spherical coordinates
#' (\code{phi}, \code{theta}, \code{r}), or projected user coordinates
#' (\code{xp}, \code{yp}). If all are supplied, they are respected in reverse order,
#' i.e. projected user coordinates are used if given.
#'
#' @export
#'
map_data <- function(x, preset=NULL, proj="", parameters=NULL, orientation=NULL)
{
map_proj(preset=preset, proj=proj, parameters=parameters,
orientation=orientation)
nms <- colnames(x)
has.xyz <- all(c("x", "y", "z") %in% nms)
has.sphere <- all(c("phi", "theta") %in% nms) # r optinal as not used
has.proj <- all(c("xp", "yp") %in% nms)
if (!(has.xyz | has.sphere | has.proj)) {
stop("Either cartesian, spherical or projected user ",
"coordinates must be supplied.", call. = FALSE)
}
if (has.xyz) {
x <- add_sphere_coords(x)
x <- add_proj_data(x)
}
if (has.sphere) {
x <- add_proj_data(x)
}
#
# if (has.proj) {
# # do nothing and return x
# } else if (has.sphere) {
# x <- add_proj_data(x)
# } else if (has.xyz) {
# x <- add_sphere_coords(x)
# x <- add_proj_data(x)
# } else {
# stop("Either cartesian, spherical or projected user ",
# "coordinates must be supplied.", call. = FALSE)
# }
x
}
#' Find centroid of a set of points
#'
#' @export
centroid <- function(x, subset=.())
{
x <- map_data(x)
s <- subset2(x, subset)
s <- coordinate_set(s)
m <- col_means(s)
map_data(m) # implicitly xyz are used for caclulation as map_data builds all on xyz
}
#### +----------- CREATING THE PLOT ----------- ####
#' Get limits in user coords for current projection
#'
#' To determine \code{xlim} and \code{ylim} for a current projection
#' a brute force approach is used. All combinations of a sequence of
#' lattitude and longitude coordinates is projected and the x and y limits
#' are returned.
#'
#' @param by Sequence step width for lattitude and longitude.
#' @return A list with the elements \code{xlim} and \code{ylim}.
#' @export
#' @examples
#' map_proj("mollweide")
#' map_range()
#'
map_range <- function(by=1)
{
long <- seq(-180, 180, by=by)
lat <- seq(-90, 90, by=by)
xy <- expand.grid(x=long, y=lat)
mp <- mapproj::mapproject(xy$x, xy$y)
r <- mp$range
list(xlim=r[1:2],
ylim=r[3:4] )
}
#' Set up plotting region
#'
#' Convenient wrapper around \code{plot} automatically sets nice
#' plotting limits.
#'
#' @param xlim,ylim Values determined by \code{\link{map_range}} if not set explicitly.
#' @param scale A factor to multiply \code{xlim} and \code{ylim} by.
#' @param s Depretated. Will be removed in next version.
#' @export
#' @examples
#' map_proj("mollweide")
#' map_setup()
#' map_grid()
#'
map_setup <- function(s=NULL, xlim=NULL, ylim=NULL, scale=1)
{
if (!is.null(s))
warning("Argument 's' is deprecated and will be removed in next version.")
# s <- map_data(s)
mr <- map_range()
if (is.null(xlim)) {
xlim <- mr$xlim
}
if (is.null(ylim)) {
ylim <- mr$ylim
}
xlim <- xlim * scale
ylim <- ylim * scale
# xmax <- max(abs(s$xp), na.rm=T)
# ymax <- max(abs(s$yp), na.rm=T)
# x.lim <- c(-xmax, xmax) * scale
# y.lim <- c(-ymax, ymax) * scale
plot(NULL, xlim=xlim, ylim=ylim, type="n", pch=4,
xaxt="n", yaxt="n", xlab="", ylab="", asp=1, bty="n")
}
#' Add grid lines to map
#'
#' Convenient wrapper around \code{mapproj::map.grid}
#'
#' @export
map_grid <- function(lim = c(-180, 180, -90, 90), lty=1, col=grey(.6))
{
mapproj::map.grid(lim, nx=20, ny=20, font=1, col=col, cex=.6, lty=lty, labels=F)
}
# order of evaluation: is argument supplied?
# is argument in dataframe?
# else use default value
#' Adds arguments supplied explicity to dataframe.
#'
#' Most functions have default values whoch can be overwritten.
#'
#' @param x Info dataframe.
#' @param ... Arguments that are added as columns to dataframe.
#' @param replace Replace existing columns? If set to \code{FALSE}
#' default arguments can be supplied.
#' @keywords internal
#
add_args_to_dataframe <- function(x, ..., replace=TRUE)
{
dots <- list(...)
args <- names(dots)
if (!replace)
args <- setdiff(args, colnames(x))
for (arg in args) {
x[[arg]] <- dots[[arg]]
}
x
}
#' Draw points on map
#'
#' @export
map_points <- function(x, subset=.(), ...)
{
x <- add_args_to_dataframe(x, ...)
x <- add_args_to_dataframe(x, pch=16, col="black", cex=1, replace=FALSE)
warn_if_var_is_factor(x, c("col", "pch"))
x <- map_data(x)
x <- subset2(x, subset)
points(x$xp, x$yp, pch=x$pch, col=x$col, cex=x$cex)
}
# #' Draw points on map
# #'
# map_points <- function(s)
# {
# points(s$xp, s$yp, pch=16, col=s$color, cex=s$cex)
# }
#' Add point labels to map
#'
#' \code{map_labels} positions labels using an pabels positioning algorithm.
#' \code{map_text} also plots text but passes all arguments directly to \code{text} while
#' in \code{map_labels} they are mapped from the dataframe.
#'
#' @param x A dataframe.
#' @param subset Logical condition to select subset. Either as a string or using
#' the \code{\link{plyr::.}} function.
#' @param ... Arguments that are added as columns to dataframe.
#' @export
#' @rdname map-text
#'
map_labels <- function(x, subset=.(), ...)
{
x <- check_matrix(x)
x <- add_args_to_dataframe(x, ...)
x <- add_args_to_dataframe(x, label="", pch=16, cex=1, col="black", replace=FALSE)
x <- map_data(x)
x <- subset2(x, subset)
o <- na.omit(x) # pointLabel does not work on missing position values
l.xy <- maptools::pointLabel(o$xp, o$yp, labels=o$label, cex=o$cex, do=FALSE)
text(l.xy, labels = o$label, cex=o$cex, col=o$col)
}
#' @rdname map-text
#' @export
map_text <- function(x, ..., subset=.())
{
x <- check_matrix(x)
x <- add_args_to_dataframe(x, ...)
x <- add_args_to_dataframe(x, label="", pch=16, cex=1, col="black", offset=.5, replace=FALSE)
x <- map_data(x)
x <- subset2(x, subset)
xy <- c_proj(x)
text(xy, labels = x$label, cex=x$cex, col=x$col, pos=x$pos, offset=x$offset)
}
# map_text <- function(x, labels="", ..., subset=.())
# {
# x <- check_matrix(x)
# x <- map_data(x)
# x <- subset2(x, subset)
# pp <- c_proj(x)
# text(pp, labels = labels, ...)
# }
# x,y two points. x and y can have any number of dimensions.
#
interpolate_between_points <- function(x, y, n =100)
{
inter <- function(i) x + i/n *(y - x)
t(sapply(0L:n, inter))
}
#' Draw line between two points
#'
#' @keywords internal
#' @export
map_line <- function(x, y, n=200, ...)
{
# extract only x,y,z coords (might need generalization in te future)
x <- c_cart(x)
y <- c_cart(y)
# find points on line
s <- interpolate_between_points(x, y, n = n) # interpolated points in n-dim
s <- cart_to_sphere(s) # get spherical coords
s <- as.data.frame(s)
#colnames(s) <- c("phi", "theta", "r")
s <- add_proj_data(s) # uses Last.projection() settings if proj=""
h <- s[, c("xp", "yp")]
h <- na.omit(h)
# check if point changes "around the world" (i.e. big differences in xp or yp)
# and omit this segment
n0 <- 1L:(length(h$xp) - 1)
n1 <- n0 + 1
delta.x <- abs(h$xp[n0] - h$xp[n1])
delta.y <- abs(h$yp[n0] - h$yp[n1])
draw <- !delta.x > .1 | delta.y > .1
n0.draw <- n0[draw]
n1.draw <- n1[draw]
segments(h$xp[n0.draw], h$yp[n0.draw], h$xp[n1.draw], h$yp[n1.draw], ...)
}
#' Draw lines between all points in x and y
#'
#' @param x,y Either a matrix ot vector with three columns (x,y,z).
#' Spherical and projected coords are currently not supported).
#' @param n Number of segments used to draw the lines.
#' @export
map_lines <- function(x, y, n=200, ...)
{
x <- check_matrix(x)
y <- check_matrix(y)
ii <- nrow(x)
jj <- nrow(y)
for (i in 1L:ii) {
for (j in 1L:jj) {
map_line(x[i, ], y[j, ], n=n, ...)
}
}
}
#' Interpolate points that form a polygon
#'
#' @param x A dataframe with xyz colummns (must have three columns).
#' @param n Number of points to interpolate.
#' @keywords internal
#'
interpolate_polygon_points <- function(x, n=100)
{
ll <- rbind(x, head(x, 1)) # add last point again at bottom
cc <- list() # get interpolated xyz between each set of points
for (i in 1L:nrow(x))
{
cc[[i]] <- interpolate_between_points(ll[i, ], ll[i + 1, ], n=n)
#cc[[i]] <- interpolate_xyz_points(ll[i, ], ll[i + 1, ])
}
d <- do.call(rbind, cc)
as.data.frame(d)
}
#' Map convex hull on globe
#' @param x A dataframe containing xyz and coordinates
#' @param subset
#' @param subset Logical condition to select subset. Either as a string or using
#' the \code{\link{plyr::.}} function.
#' @param n Number of points used to draw each polygon line.
#' @param ... Arguments passed to \code{polygon}.
#' @export
#'
map_convex_hull <- function(x, subset=.(), n = 100, ...)
{
x <- map_data(x) # in case only x,y,z is supplied
s <- subset2(x, subset)
p <- c_proj(s) # get projected points
ohpts <- chull(p) # get ordered hull points
xyz <- c_cart(s[ohpts, ]) # xyz of hull points ordered
d <- interpolate_polygon_points(xyz, n=n) # interpolate cartesian xyz points
z <- map_data(d) # build map projections
xy <- c_proj(z)
polygon(xy, ...)
}
#' Draw centroid of set of points
#'
#' @export
map_centroid_point <- function(x, subset=.(), ...)
{
s <- subset2(x, subset)
c <- centroid(s)
map_points(c, ...)
}
#' Draw a star (lines from centroid to points)
#'
#' @export
map_centroid_star <- function(x, subset=.(), ...)
{
s <- subset2(x, subset)
c <- centroid(s)
map_lines(s, c, ...)
}
#' Draw label for centroid.
#'
#' Label must currently be set explicitly.
#'
#' @export
map_centroid_label <- function(x, label="", subset=.(), ...)
{
s <- subset2(x, subset)
c <- centroid(s)
pp <- c_proj(c)
text(pp, labels = label, ...)
}
#### +----------- OLD STUFF ----------- ####
# example projection
# p <- mapproject(s$phi, s$theta, proj="orthographic",
# parameters=NULL, orientation=c(0,0,0))
# xy <- cbind(xp = p$x, yp = p$y)
# s <- cbind(s, xy)
# s
# par(mar=rep(2, 4))
# lim <- c(-180, 180, -90, 90)
# xmax <- max(abs(s$xp), na.rm=T)
# ymax <- max(abs(s$yp), na.rm=T)
# x.lim <- c(-xmax, xmax)
# y.lim <- c(-ymax, ymax)
# plot(NULL, xlim=x.lim, ylim=y.lim, type="n", pch=4,
# xaxt="n", yaxt="n", xlab="", ylab="", asp=1, bty="n")
# map.grid(lim, nx=20, ny=20, font=1, col=grey(.6), cex=.6, lty=2, labels=T)
# points(s$xp, s$yp, pch=16, col=s$color, cex=s$cex)
#
# sn <- na.omit(s)
# l.xy <- maptools::pointLabel(sn$xp, sn$yp, labels=sn$label, cex=sn$cex, do=FALSE)
# text(l.xy, sn$label, cex=sn$cex, col=sn$color)
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.