R 在绘制世界地图时使用不同于本初子午线的中心

R 在绘制世界地图时使用不同于本初子午线的中心,r,ggplot2,R,Ggplot2,我正在将maps包中的世界地图覆盖到ggplot2光栅几何体上。但是,该光栅并非以本初子午线(0度)为中心,而是以180度为中心(大致为白令海和太平洋)。以下代码获取贴图并以180度角重新居中贴图: 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

我正在将
maps
包中的世界地图覆盖到
ggplot2
光栅几何体上。但是,该光栅并非以本初子午线(0度)为中心,而是以180度为中心(大致为白令海和太平洋)。以下代码获取贴图并以180度角重新居中贴图:

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()
这将导致正确的图像。我现在有几个问题:

  • 一定有更好的方法把地图集中在另一条子午线上。我尝试在
    map
    中使用
    orientation
    参数,但将其设置为
    orientation=c(0180,0)
    并没有产生正确的结果,事实上它没有改变结果对象的任何内容(
    all.equal
    产生
    TRUE
  • 在不删除某些多边形的情况下,应该可以去除水平条纹。这可能是解决问题的第一点。也解决了这一点

  • 这可能有点棘手,但您可以通过以下方式完成:

    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))
    

    已更新

    我在这里作了一些解释:

    整个数据如下所示:

    ggplot(aes(x = long, y = lat, group = group), data = mp) + geom_path()
    

    但是通过
    scale\x\u continuous(limits=c(0360))
    ,您可以从0到360经度裁剪区域的子集


    geom_path
    中,连接同一组的数据。因此,如果
    mp2$group,这里有一个不同的方法。其工作原理如下:

  • 将世界地图从
    maps
    包转换为具有地理(lat-long)CRS的
    SpatialLines
    对象
  • SpatialLines
    地图投影到以本初子午线为中心的板车(也称为等距圆柱)投影中。(该投影非常类似于地理地图)
  • 切割成两段,否则将被地图的左边缘和右边缘剪裁。(这是使用
    rgeos
    包中的拓扑函数完成的。)
  • 重新投影到以所需子午线为中心的平板车投影(
    lon_0
    术语取自
    PROJ_4
    程序,该程序由
    rgdal
    包中的
    spTransform()
    使用)
  • 识别(并移除)任何剩余的“条纹”。我通过搜索跨越三条广泛分离的子午线中的两条来实现自动化。(这也使用了
    rgeos
    包中的拓扑函数。)
  • 这显然是一个很大的工作,但留下的映射是最小截断的,并且可以使用
    spTransform()
    轻松地重新编程。要使用
    base
    lattice
    图形将这些图像覆盖在光栅图像之上,我首先使用
    spTransform()
    重新投影光栅。如果需要,网格线和标签同样可以投影以匹配
    空间线
    地图


    库(sp)
    图书馆(地图)
    库(maptools)35;#map2SpatialLines(),prueMap()
    库(rgdal)35;#CRS(),spTransform()
    库(rgeos)##readWKT(),gIntersects(),gBuffer(),gDifference()
    ##将“贴图”贴图转换为“空间线”贴图
    makeSLmap这应该可以:

     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")
    

    wm如果您只想将地图居中180度,则
    maps
    包中的地图“world2”或
    mapdata
    包中的高分辨率版本“world2Hires”已经居中180度。其余代码工作正常。感谢您的评论!不过我还是对灵活的解决方案感兴趣。你只对ggplot解决方案感兴趣吗?我会使用
    sp
    和相关软件包来完成大部分工作,但不知道如何将
    sp
    Spatial*
    对象(尤其是光栅表示的对象)转换为ggplot…sp类转换为ggplot2可以使用的东西非常简单。所以我对基于sp的答案持开放态度。@PaulHiemstra——这也是一个解决方案吗?有没有可能(对一个好奇的非r用户)描述一下它是如何工作的?+1,很好!尽管我仍然很好奇是否可以防止修改
    map
    的输出,并通过更改
    map
    的输入来指定它。因为
    map(orientation=c(90,0,180),projection=“mercator”)
    也失败了,可能没有办法。很好的解决方案。如果您还想将填充映射到国家/地区,是否有办法使其工作?感谢此伟大的解决方案(在您回答问题5年后),只需指出,您可以将比例从连续缩放(限制=c(0,360))更改为坐标缩放(xlim=c(0,360))。如果有人决定使用多边形,它将避免出现问题。这里有一个例子:这是一个很好的治疗方法。有没有一种方法可以对空间多边形对象执行类似的操作,以便我们可以保留国家边界信息?@PeterEllis——这是我第一次尝试的,使用(IIRC)
    rgeos
    gBuffer()
    沿分裂子午线制作一个薄的垂直多边形,然后
    gDifference()
    以剪切其交叉的空间多边形。不幸的是,
    rgeos
    “多边形检查标准太高了,&它拒绝了我可以从
    map
    对象创建的任何空间多边形,以及关于无效拓扑等的消息。我在尝试修复多边形时把头撞在墙上太久了,所以如果你有任何发现,请务必告诉我!或者,我在注释中链接到上述问题的解决方案是否有效?当我在此处运行代码时,除非您向gBuffer函数添加byid=T参数,否则将失败,即:SP感谢您的回答,尽管它没有使用
    ggplot2
    。Oops,true。您可以在原始代码中替换它,但令人烦恼的是,当前需要先设置plot=TRUE,因为wrap=TRUE仅在打印数据时使用。
    ggplot(aes(x = long, y = lat, group = group), data = mp) + geom_path()
    
    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
    
     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")
    
    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()