resumen - paralelos y meridianos
Use un centro diferente al meridiano principal para trazar un mapa mundial (3)
Estoy superponiendo un mapa mundial del paquete de maps
a una geometría de trama ggplot2
. Sin embargo, este ráster no está centrado en el meridiano principal (0 grados), sino en 180 grados (aproximadamente el Mar de Bering y el Pacífico). El siguiente código obtiene el mapa y vuelve a centrar el mapa en 180 grados:
require(maps)
world_map = data.frame(map(plot=FALSE)[c("x","y")])
names(world_map) = c("lon","lat")
world_map = within(world_map, {
lon = ifelse(lon < 0, lon + 360, lon)
})
ggplot(aes(x = lon, y = lat), data = world_map) + geom_path()
que produce el siguiente resultado:
Obviamente, hay líneas dibujadas entre polígonos que están en un extremo u otro del meridiano principal. Mi solución actual es reemplazar los puntos cercanos al meridiano principal por NA, reemplazando el llamado within
anterior por:
world_map = within(world_map, {
lon = ifelse(lon < 0, lon + 360, lon)
lon = ifelse((lon < 1) | (lon > 359), NA, lon)
})
ggplot(aes(x = lon, y = lat), data = world_map) + geom_path()
Lo que lleva a la imagen correcta. Ahora tengo una cantidad de preguntas:
- Debe haber una mejor manera de centrar el mapa en otro meridiano. Intenté usar el parámetro de
orientation
en elmap
, pero al establecer esto enorientation = c(0,180,0)
noorientation = c(0,180,0)
el resultado correcto, de hecho no cambió nada al objeto resultante (all.equal
arrojóTRUE
). - Deshacerse de las rayas horizontales debería ser posible sin eliminar algunos de los polígonos. Puede ser que resolver el punto 1. también resuelva este punto.
Aquí hay un enfoque diferente. Funciona por:
- Conversión del mapa mundial del paquete de
maps
en un objetoSpatialLines
con un CRS geográfico (lat-long). - Proyectando el mapa
SpatialLines
en la proyección Plate Carée (también conocida como Equidistant Cylindrical) centrada en el Meridiano de Greenwich. (Esta proyección es muy similar a un mapeo geográfico). - Cortar en dos segmentos que, de lo contrario, quedarían recortados por los bordes izquierdo y derecho del mapa. (Esto se hace usando funciones topológicas del paquete
rgeos
). - Reproyectando a una proyección Plate Carée centrada en el meridiano deseado (
lon_0
en la terminología tomada del programaPROJ_4
utilizado porspTransform()
en el paquetergdal
). - Identificando (y eliminando) cualquier ''raya'' restante. Automaticé esto buscando las líneas que cruzan dos de los tres meridianos ampliamente separados. (Esto también usa funciones topológicas del paquete
rgeos
).
Obviamente, esto es mucho trabajo, pero deja uno con mapas que están mínimamente truncados, y se pueden reproyectar fácilmente usando spTransform()
. Para superponer estos en la parte superior de las imágenes de trama con gráficos de base
o en lattice
, primero reproyecto los rásteres, también utilizando spTransform()
. Si los necesita, también se pueden proyectar líneas de cuadrícula y etiquetas para que coincidan con el mapa de SpatialLines
.
library(sp)
library(maps)
library(maptools) ## map2SpatialLines(), pruneMap()
library(rgdal) ## CRS(), spTransform()
library(rgeos) ## readWKT(), gIntersects(), gBuffer(), gDifference()
## Convert a "maps" map to a "SpatialLines" map
makeSLmap <- function() {
llCRS <- CRS("+proj=longlat +ellps=WGS84")
wrld <- map("world", interior = FALSE, plot=FALSE,
xlim = c(-179, 179), ylim = c(-89, 89))
wrld_p <- pruneMap(wrld, xlim = c(-179, 179))
map2SpatialLines(wrld_p, proj4string = llCRS)
}
## Clip SpatialLines neatly along the antipodal meridian
sliceAtAntipodes <- function(SLmap, lon_0) {
## Preliminaries
long_180 <- (lon_0 %% 360) - 180
llCRS <- CRS("+proj=longlat +ellps=WGS84") ## CRS of ''maps'' objects
eqcCRS <- CRS("+proj=eqc")
## Reproject the map into Equidistant Cylindrical/Plate Caree projection
SLmap <- spTransform(SLmap, eqcCRS)
## Make a narrow SpatialPolygon along the meridian opposite lon_0
L <- Lines(Line(cbind(long_180, c(-89, 89))), ID="cutter")
SL <- SpatialLines(list(L), proj4string = llCRS)
SP <- gBuffer(spTransform(SL, eqcCRS), 10, byid = TRUE)
## Use it to clip any SpatialLines segments that it crosses
ii <- which(gIntersects(SLmap, SP, byid=TRUE))
# Replace offending lines with split versions
# (but skip when there are no intersections (as, e.g., when lon_0 = 0))
if(length(ii)) {
SPii <- gDifference(SLmap[ii], SP, byid=TRUE)
SLmap <- rbind(SLmap[-ii], SPii)
}
return(SLmap)
}
## re-center, and clean up remaining streaks
recenterAndClean <- function(SLmap, lon_0) {
llCRS <- CRS("+proj=longlat +ellps=WGS84") ## map package''s CRS
newCRS <- CRS(paste("+proj=eqc +lon_0=", lon_0, sep=""))
## Recenter
SLmap <- spTransform(SLmap, newCRS)
## identify remaining ''scratch-lines'' by searching for lines that
## cross 2 of 3 lines of longitude, spaced 120 degrees apart
v1 <-spTransform(readWKT("LINESTRING(-62 -89, -62 89)", p4s=llCRS), newCRS)
v2 <-spTransform(readWKT("LINESTRING(58 -89, 58 89)", p4s=llCRS), newCRS)
v3 <-spTransform(readWKT("LINESTRING(178 -89, 178 89)", p4s=llCRS), newCRS)
ii <- which((gIntersects(v1, SLmap, byid=TRUE) +
gIntersects(v2, SLmap, byid=TRUE) +
gIntersects(v3, SLmap, byid=TRUE)) >= 2)
SLmap[-ii]
}
## Put it all together:
Recenter <- function(lon_0 = -100, grid=FALSE, ...) {
SLmap <- makeSLmap()
SLmap2 <- sliceAtAntipodes(SLmap, lon_0)
recenterAndClean(SLmap2, lon_0)
}
## Try it out
par(mfrow=c(2,2), mar=rep(1, 4))
plot(Recenter(-90), col="grey40"); box() ## Centered on 90w
plot(Recenter(0), col="grey40"); box() ## Centered on prime meridian
plot(Recenter(90), col="grey40"); box() ## Centered on 90e
plot(Recenter(180), col="grey40"); box() ## Centered on International Date Line
Esto debería funcionar:
wm <- map.wrap(map(projection="rectangular", parameter=0, orientation=c(90,0,180), plot=FALSE))
world_map <- data.frame(wm[c("x","y")])
names(world_map) <- c("lon","lat")
El map.wrap corta las líneas que cruzan el mapa. Puede usarse a través de una opción para mapear (wrap = TRUE), pero eso solo funciona cuando plot = TRUE.
Una molestia que queda es que en este punto, lat / lon están en rad, no en grados:
world_map$lon <- world_map$lon * 180/pi + 180
world_map$lat <- world_map$lat * 180/pi
ggplot(aes(x = lon, y = lat), data = world_map) + geom_path()
Esto puede ser algo complicado, pero puedes hacerlo de la siguiente manera:
mp1 <- fortify(map(fill=TRUE, plot=FALSE))
mp2 <- mp1
mp2$long <- mp2$long + 360
mp2$group <- mp2$group + max(mp2$group) + 1
mp <- rbind(mp1, mp2)
ggplot(aes(x = long, y = lat, group = group), data = mp) +
geom_path() +
scale_x_continuous(limits = c(0, 360))
Con esta configuración, puede establecer fácilmente el centro (es decir, límites):
ggplot(aes(x = long, y = lat, group = group), data = mp) +
geom_path() +
scale_x_continuous(limits = c(-100, 260))
ACTUALIZADO
Aquí pongo algunas explicaciones:
La información completa se ve así:
ggplot(aes(x = long, y = lat, group = group), data = mp) + geom_path()
pero por scale_x_continuous(limits = c(0, 360))
, puede recortar un subconjunto de la región de 0 a 360 de longitud.
Y en geom_path
, los datos del mismo grupo están conectados. Entonces, si mp2$group <- mp2$group + max(mp2$group) + 1
está ausente, parece que: