5

I would like to rotate only the map, or change the orientation, for example 45 degrees, but not the other elements. It's posible? For example:

  • Without rotating the image

enter image description here

  • Rotating the image

enter image description here

My initial code without rotating:

library("maps")
library("mapproj")
library("mapdata")

xlon = seq(-1, 7, 0.01)
xlat = seq(34, 42, 0.01)

map(database = "worldHires",
    xlim = c(min(xlon), max(xlon)), ylim = c(min(xlat),max(xlat)),
    mar = c(0, 0, 0, 0))
text(2, 37, labels = "point1", pos = 4)
points(2, 37)
Henrik
  • 65,555
  • 14
  • 143
  • 159
user56474
  • 95
  • 1
  • 7
  • 1
    See: [elide](http://www.inside-r.org/packages/cran/maptools/docs/elide) | [SO example](http://stackoverflow.com/questions/13757771/relocating-alaska-and-hawaii-on-thematic-map-of-the-usa-with-ggplot2) – hrbrmstr Aug 07 '15 at 09:56
  • It looks like within `map()` there's an option called `orientation` that lets you do this, but when I try it as `orientation = c(median(xlat), median(xlon), degrees = 45)`, I get this error message: `Error in plot && coord$error : invalid 'y' type in 'x && y'`. – ulfelder Aug 07 '15 at 10:07

3 Answers3

5

Normally when you call maps::map() it automatically plots the map during the function call, but you can pass plot=F to prevent that. At the same time, you can store the return value from the call in a variable, which will contain the x and y coordinates of the contours of the requested map. You can then use some trigonometry to rotate all the x and y coordinates about a center point, and finally plot the rotated points manually using base R plotting functions.

library('maps');
library('mapproj');
library('mapdata');

xlon = seq(-1,7,0.01);
xlat = seq(34,42,0.01);

md <- map('worldHires',xlim=range(xlon),ylim=range(xlat),mar=c(0,0,0,0),plot=F);
md2 <- md;
rot <- -30*pi/180;
about <- c(2,37);
newangles <- atan2(md$y-about[2],md$x-about[1])+rot;
mags <- sqrt((md$x-about[1])^2+(md$y-about[2])^2);
md2$x <- about[1]+cos(newangles)*mags;
md2$y <- about[2]+sin(newangles)*mags;
par(mar=c(0,0,0,0)); plot(md2,type='l',xlim=range(xlon),ylim=range(xlat),axes=F,ann=F);

text(about[1],about[2],labels='point1',pos=4);
points(about[1],about[2]);

rotated

bgoldst
  • 34,190
  • 6
  • 38
  • 64
  • Nice indeed (+1). Any idea if and how the `orientation` argument in `map` could be used? "a vector `c(latitude, longitude, rotation)` describing where the map should be centered and a clockwise rotation (in degrees) about this center." sound promising (as also noted by @ulfelder above), at least to a `map` ignorant like me. – Henrik Aug 07 '15 at 17:29
  • @Henrik Yeah, I noticed that parameter before I started on my trigonometry solution, and I got the same error that @ulfelder mentioned in his comment. Looks like a bug in the function. I just tried fixing it in my R session by computing on the language, and I think I succeeded, but I get the warning "projection failed for some data" and the plot looks identical to how it looks without specifying the `orientation` parameter. Probably a lost cause. – bgoldst Aug 07 '15 at 18:22
4

It's 2021 now and this is much easier with the modern sf R package. With this you can rotate the underlying polygons before plotting to achieve the desired affect. This also uses country polygon data from Natural Earth via the rnaturalearth package.

library(sf)
library(rnaturalearth)
library(ggplot2)


# Rotate an sf geom around a center point. If no center is
# specified then it rotates around the center of the geom.
# This is technically an affine transformation: https://r-spatial.github.io/sf/articles/sf3.html#affine-transformations-1
st_ellide_rotate = function(x, degrees, center_coords=NULL){
  if(degrees < -360 | degrees > 360) stop('Degrees must be in the range -360 to 360')
  x = sf::st_combine(x)
  if(is.null(center_coords)){
    center_coords = sf::st_centroid(x)
  }
  radians = degrees * pi/180
  transform_matrix = matrix(c(cos(radians), sin(radians), -sin(radians), cos(radians)), 2, 2)
  
  return((x-center_coords) * transform_matrix + center_coords)
}


countries = rnaturalearth::ne_countries(scale = 10,returnclass = 'sf')
saved_crs = st_crs(countries)

points = st_sf(name=c('point1'), geometry = st_sfc(st_point(c(2,37)), crs = saved_crs))

countries_rotated = countries %>%
  st_ellide_rotate(-20, center_coords = c(2,37))
# applying an affine transformation nulls the CRS for some reason, so reset it here
st_crs(countries_rotated) <- saved_crs

ggplot() + 
  geom_sf(data=countries_rotated) +
  geom_sf(data=points, size=1) + 
  geom_sf_label(data=points, aes(label=name), nudge_x=1) + 
  coord_sf(xlim = c(-1,7), ylim=c(34,42)) +
  labs(subtitle = 'rotated 20 deg')

ggplot() + 
  geom_sf(data=countries) +
  geom_sf(data=points, size=1) + 
  geom_sf_label(data=points, aes(label=name), nudge_x = 1) + 
  coord_sf(xlim = c(-1,7), ylim=c(34,42)) +
  labs(subtitle = 'original')

image rotated 20 degrees original image

Shawn
  • 336
  • 3
  • 10
-2

If you are not tightly integrated with R, you can explore map rotation with emptymap.js here is the article explaining the usage.

Disclaimer: I am developer of the module.

Gagan
  • 1,267
  • 3
  • 18
  • 28